ShadowImageLayerOGL::RenderLayer used to call gl()->GetSharedHandleDetails() directly and uses that result. This makes us do something similar. As a concequence we also drop the mTextureTransform member because we are not caching it anymore. I do wonder if it would make more sense for the content thread to send the transform along with the update to make sure that it always matches. This would also make it so that we have no chance of causing a GC in the compositor to get the transform by calling through java.
824 lines
25 KiB
C++
824 lines
25 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "TextureHostOGL.h"
|
|
#include "ipc/AutoOpenSurface.h"
|
|
#include "gfx2DGlue.h"
|
|
#include "ShmemYCbCrImage.h"
|
|
#include "GLContext.h"
|
|
#include "gfxImageSurface.h"
|
|
#include "SurfaceStream.h"
|
|
#include "SharedSurface.h"
|
|
#include "SharedSurfaceGL.h"
|
|
#include "SharedSurfaceEGL.h"
|
|
#include "mozilla/layers/CompositorOGL.h"
|
|
|
|
using namespace mozilla::gl;
|
|
using namespace mozilla::gfx;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
TemporaryRef<TextureHost>
|
|
CreateTextureHostOGL(SurfaceDescriptorType aDescriptorType,
|
|
uint32_t aTextureHostFlags,
|
|
uint32_t aTextureFlags)
|
|
{
|
|
RefPtr<TextureHost> result = nullptr;
|
|
|
|
if (aDescriptorType == SurfaceDescriptor::TYCbCrImage) {
|
|
result = new YCbCrTextureHostOGL();
|
|
} else if (aDescriptorType == SurfaceDescriptor::TSurfaceStreamDescriptor) {
|
|
result = new SurfaceStreamHostOGL();
|
|
} else if (aDescriptorType == SurfaceDescriptor::TSharedTextureDescriptor) {
|
|
result = new SharedTextureHostOGL();
|
|
#ifdef MOZ_WIDGET_GONK
|
|
} else if (aDescriptorType == SurfaceDescriptor::TSurfaceDescriptorGralloc) {
|
|
result = new GrallocTextureHostOGL();
|
|
#endif
|
|
} else if (aTextureHostFlags & TEXTURE_HOST_TILED) {
|
|
result = new TiledTextureHostOGL();
|
|
} else {
|
|
result = new TextureImageTextureHostOGL();
|
|
}
|
|
|
|
NS_ASSERTION(result, "Result should have been created.");
|
|
|
|
result->SetFlags(aTextureFlags);
|
|
return result.forget();
|
|
}
|
|
|
|
static void
|
|
MakeTextureIfNeeded(gl::GLContext* gl, GLuint& aTexture)
|
|
{
|
|
if (aTexture != 0)
|
|
return;
|
|
|
|
gl->fGenTextures(1, &aTexture);
|
|
|
|
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
|
|
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
|
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
static gl::TextureImage::Flags
|
|
FlagsToGLFlags(TextureFlags aFlags)
|
|
{
|
|
uint32_t result = TextureImage::NoFlags;
|
|
|
|
if (aFlags & UseNearestFilter)
|
|
result |= TextureImage::UseNearestFilter;
|
|
if (aFlags & NeedsYFlip)
|
|
result |= TextureImage::NeedsYFlip;
|
|
if (aFlags & ForceSingleTile)
|
|
result |= TextureImage::ForceSingleTile;
|
|
|
|
return static_cast<gl::TextureImage::Flags>(result);
|
|
}
|
|
|
|
GLenum
|
|
WrapMode(gl::GLContext *aGl, bool aAllowRepeat)
|
|
{
|
|
if (aAllowRepeat &&
|
|
(aGl->IsExtensionSupported(GLContext::ARB_texture_non_power_of_two) ||
|
|
aGl->IsExtensionSupported(GLContext::OES_texture_npot))) {
|
|
return LOCAL_GL_REPEAT;
|
|
}
|
|
return LOCAL_GL_CLAMP_TO_EDGE;
|
|
}
|
|
|
|
gfx::SurfaceFormat
|
|
FormatFromShaderType(ShaderProgramType aShaderType)
|
|
{
|
|
switch (aShaderType) {
|
|
case RGBALayerProgramType:
|
|
case RGBALayerExternalProgramType:
|
|
case RGBARectLayerProgramType:
|
|
case RGBAExternalLayerProgramType:
|
|
return FORMAT_R8G8B8A8;
|
|
case RGBXLayerProgramType:
|
|
return FORMAT_R8G8B8X8;
|
|
case BGRALayerProgramType:
|
|
return FORMAT_B8G8R8A8;
|
|
case BGRXLayerProgramType:
|
|
return FORMAT_B8G8R8X8;
|
|
default:
|
|
MOZ_NOT_REACHED("Unsupported texture shader type");
|
|
return FORMAT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
TextureImageTextureHostOGL::~TextureImageTextureHostOGL()
|
|
{
|
|
MOZ_COUNT_DTOR(TextureImageTextureHostOGL);
|
|
if (mTexture && mTexture->InUpdate()) {
|
|
mTexture->EndUpdate();
|
|
}
|
|
}
|
|
|
|
gfx::IntSize
|
|
TextureImageTextureHostOGL::GetSize() const
|
|
{
|
|
if (mTexture) {
|
|
if (mIterating) {
|
|
nsIntRect rect = mTexture->GetTileRect();
|
|
return gfx::IntSize(rect.width, rect.height);
|
|
}
|
|
return gfx::IntSize(mTexture->GetSize().width, mTexture->GetSize().height);
|
|
}
|
|
return gfx::IntSize(0, 0);
|
|
}
|
|
|
|
|
|
void
|
|
TextureImageTextureHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
GLContext* newGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
if (mGL != newGL) {
|
|
mGL = newGL;
|
|
mTexture = nullptr;
|
|
// if we have a buffer we reupload it with the new gl context
|
|
// Post landing TODO: the new TextureClient/Host model will make this
|
|
// go away.
|
|
if (newGL && mBuffer && IsSurfaceDescriptorValid(*mBuffer)) {
|
|
UpdateImpl(*mBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
TextureImageTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
if (!mGL) {
|
|
NS_WARNING("trying to update TextureImageTextureHostOGL without a compositor?");
|
|
return;
|
|
}
|
|
AutoOpenSurface surf(OPEN_READ_ONLY, aImage);
|
|
nsIntSize size = surf.Size();
|
|
|
|
if (!mTexture ||
|
|
mTexture->GetSize() != size ||
|
|
mTexture->GetContentType() != surf.ContentType()) {
|
|
mTexture = mGL->CreateTextureImage(size,
|
|
surf.ContentType(),
|
|
WrapMode(mGL, mFlags & AllowRepeat),
|
|
FlagsToGLFlags(mFlags));
|
|
}
|
|
|
|
// XXX this is always just ridiculously slow
|
|
nsIntRegion updateRegion;
|
|
|
|
if (!aRegion) {
|
|
updateRegion = nsIntRegion(nsIntRect(0, 0, size.width, size.height));
|
|
} else {
|
|
updateRegion = *aRegion;
|
|
}
|
|
mTexture->DirectUpdate(surf.Get(), updateRegion);
|
|
|
|
if (mTexture->InUpdate()) {
|
|
mTexture->EndUpdate();
|
|
}
|
|
}
|
|
|
|
bool
|
|
TextureImageTextureHostOGL::Lock()
|
|
{
|
|
if (!mTexture) {
|
|
NS_WARNING("TextureImageAsTextureHost to be composited without texture");
|
|
return false;
|
|
}
|
|
|
|
NS_ASSERTION(mTexture->GetContentType() != gfxASurface::CONTENT_ALPHA,
|
|
"Image layer has alpha image");
|
|
|
|
mFormat = FormatFromShaderType(mTexture->GetShaderProgramType());
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
SharedTextureHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
if (mGL && !glCompositor) {
|
|
DeleteTextures();
|
|
}
|
|
mGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
}
|
|
|
|
void
|
|
SharedTextureHostOGL::DeleteTextures()
|
|
{
|
|
MOZ_ASSERT(mGL);
|
|
mGL->MakeCurrent();
|
|
if (mSharedHandle) {
|
|
mGL->ReleaseSharedHandle(mShareType, mSharedHandle);
|
|
mSharedHandle = 0;
|
|
}
|
|
if (mTextureHandle) {
|
|
mGL->fDeleteTextures(1, &mTextureHandle);
|
|
mTextureHandle = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
SharedTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
SwapTexturesImpl(aImage, aRegion);
|
|
}
|
|
|
|
void
|
|
SharedTextureHostOGL::SwapTexturesImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
NS_ASSERTION(aImage.type() == SurfaceDescriptor::TSharedTextureDescriptor,
|
|
"Invalid descriptor");
|
|
|
|
SharedTextureDescriptor texture = aImage.get_SharedTextureDescriptor();
|
|
|
|
SharedTextureHandle newHandle = texture.handle();
|
|
nsIntSize size = texture.size();
|
|
mSize = gfx::IntSize(size.width, size.height);
|
|
if (texture.inverted()) {
|
|
mFlags |= NeedsYFlip;
|
|
}
|
|
|
|
if (mSharedHandle && mSharedHandle != newHandle) {
|
|
mGL->ReleaseSharedHandle(mShareType, mSharedHandle);
|
|
}
|
|
|
|
mShareType = texture.shareType();
|
|
mSharedHandle = newHandle;
|
|
|
|
GLContext::SharedHandleDetails handleDetails;
|
|
if (mSharedHandle && mGL->GetSharedHandleDetails(mShareType, mSharedHandle, handleDetails)) {
|
|
mTextureTarget = handleDetails.mTarget;
|
|
mShaderProgram = handleDetails.mProgramType;
|
|
mFormat = FormatFromShaderType(mShaderProgram);
|
|
}
|
|
}
|
|
|
|
bool
|
|
SharedTextureHostOGL::Lock()
|
|
{
|
|
MakeTextureIfNeeded(mGL, mTextureHandle);
|
|
|
|
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
mGL->fBindTexture(mTextureTarget, mTextureHandle);
|
|
if (!mGL->AttachSharedHandle(mShareType, mSharedHandle)) {
|
|
NS_ERROR("Failed to bind shared texture handle");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
SharedTextureHostOGL::Unlock()
|
|
{
|
|
mGL->DetachSharedHandle(mShareType, mSharedHandle);
|
|
mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
|
|
gfx3DMatrix
|
|
SharedTextureHostOGL::GetTextureTransform()
|
|
{
|
|
GLContext::SharedHandleDetails handleDetails;
|
|
// GetSharedHandleDetails can call into Java which we'd
|
|
// rather not do from the compositor
|
|
if (mSharedHandle) {
|
|
mGL->GetSharedHandleDetails(mShareType, mSharedHandle, handleDetails);
|
|
}
|
|
return handleDetails.mTextureTransform;
|
|
}
|
|
|
|
|
|
void
|
|
SurfaceStreamHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
if (mGL && !glCompositor) {
|
|
DeleteTextures();
|
|
}
|
|
mGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
}
|
|
|
|
void
|
|
SurfaceStreamHostOGL::DeleteTextures()
|
|
{
|
|
if (mUploadTexture) {
|
|
MOZ_ASSERT(mGL);
|
|
mGL->MakeCurrent();
|
|
mGL->fDeleteTextures(1, &mUploadTexture);
|
|
mUploadTexture = 0;
|
|
mTextureHandle = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
SurfaceStreamHostOGL::SwapTexturesImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceStreamDescriptor,
|
|
"Invalid descriptor");
|
|
}
|
|
|
|
void
|
|
SurfaceStreamHostOGL::Unlock()
|
|
{
|
|
// We don't know what this is unless we're locked
|
|
mFormat = gfx::FORMAT_UNKNOWN;
|
|
}
|
|
|
|
bool
|
|
SurfaceStreamHostOGL::Lock()
|
|
{
|
|
mGL->MakeCurrent();
|
|
SurfaceStream* surfStream = nullptr;
|
|
SharedSurface* sharedSurf = nullptr;
|
|
const SurfaceStreamDescriptor& streamDesc =
|
|
mBuffer->get_SurfaceStreamDescriptor();
|
|
|
|
surfStream = SurfaceStream::FromHandle(streamDesc.handle());
|
|
MOZ_ASSERT(surfStream);
|
|
|
|
sharedSurf = surfStream->SwapConsumer();
|
|
if (!sharedSurf) {
|
|
// We don't have a valid surf to show yet.
|
|
return false;
|
|
}
|
|
|
|
mGL->MakeCurrent();
|
|
|
|
mSize = IntSize(sharedSurf->Size().width, sharedSurf->Size().height);
|
|
|
|
gfxImageSurface* toUpload = nullptr;
|
|
switch (sharedSurf->Type()) {
|
|
case SharedSurfaceType::GLTextureShare: {
|
|
mTextureHandle = SharedSurface_GLTexture::Cast(sharedSurf)->Texture();
|
|
MOZ_ASSERT(mTextureHandle);
|
|
mShaderProgram = sharedSurf->HasAlpha() ? RGBALayerProgramType
|
|
: RGBXLayerProgramType;
|
|
break;
|
|
}
|
|
case SharedSurfaceType::EGLImageShare: {
|
|
SharedSurface_EGLImage* eglImageSurf =
|
|
SharedSurface_EGLImage::Cast(sharedSurf);
|
|
|
|
mTextureHandle = eglImageSurf->AcquireConsumerTexture(mGL);
|
|
if (!mTextureHandle) {
|
|
toUpload = eglImageSurf->GetPixels();
|
|
MOZ_ASSERT(toUpload);
|
|
} else {
|
|
mShaderProgram = sharedSurf->HasAlpha() ? RGBALayerProgramType
|
|
: RGBXLayerProgramType;
|
|
}
|
|
break;
|
|
}
|
|
case SharedSurfaceType::Basic: {
|
|
toUpload = SharedSurface_Basic::Cast(sharedSurf)->GetData();
|
|
MOZ_ASSERT(toUpload);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_NOT_REACHED("Invalid SharedSurface type.");
|
|
return false;
|
|
}
|
|
|
|
if (toUpload) {
|
|
// mBounds seems to end up as (0,0,0,0) a lot, so don't use it?
|
|
nsIntSize size(toUpload->GetSize());
|
|
nsIntRect rect(nsIntPoint(0,0), size);
|
|
nsIntRegion bounds(rect);
|
|
mShaderProgram = mGL->UploadSurfaceToTexture(toUpload,
|
|
bounds,
|
|
mUploadTexture,
|
|
true);
|
|
mTextureHandle = mUploadTexture;
|
|
}
|
|
|
|
mFormat = FormatFromShaderType(mShaderProgram);
|
|
|
|
MOZ_ASSERT(mTextureHandle);
|
|
mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
|
|
LOCAL_GL_TEXTURE_WRAP_S,
|
|
LOCAL_GL_CLAMP_TO_EDGE);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
|
|
LOCAL_GL_TEXTURE_WRAP_T,
|
|
LOCAL_GL_CLAMP_TO_EDGE);
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
YCbCrTextureHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
GLContext* newGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
if (mGL != newGL) {
|
|
mGL = newGL;
|
|
mYTexture->mTexImage = nullptr;
|
|
mCbTexture->mTexImage = nullptr;
|
|
mCrTexture->mTexImage = nullptr;
|
|
// if we have a buffer we reupload it with the new gl context
|
|
if (newGL && mBuffer && mBuffer->type() == SurfaceDescriptor::TYCbCrImage) {
|
|
UpdateImpl(*mBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
YCbCrTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
if (!mGL) {
|
|
return;
|
|
}
|
|
NS_ASSERTION(aImage.type() == SurfaceDescriptor::TYCbCrImage, "SurfaceDescriptor mismatch");
|
|
|
|
ShmemYCbCrImage shmemImage(aImage.get_YCbCrImage().data(),
|
|
aImage.get_YCbCrImage().offset());
|
|
|
|
gfxIntSize gfxSize = shmemImage.GetYSize();
|
|
gfxIntSize gfxCbCrSize = shmemImage.GetCbCrSize();
|
|
|
|
if (!mYTexture->mTexImage || mYTexture->mTexImage->GetSize() != gfxSize) {
|
|
mYTexture->mTexImage = CreateBasicTextureImage(mGL,
|
|
gfxSize,
|
|
gfxASurface::CONTENT_ALPHA,
|
|
WrapMode(mGL, mFlags & AllowRepeat),
|
|
FlagsToGLFlags(mFlags));
|
|
}
|
|
if (!mCbTexture->mTexImage || mCbTexture->mTexImage->GetSize() != gfxCbCrSize) {
|
|
mCbTexture->mTexImage = CreateBasicTextureImage(mGL,
|
|
gfxCbCrSize,
|
|
gfxASurface::CONTENT_ALPHA,
|
|
WrapMode(mGL, mFlags & AllowRepeat),
|
|
FlagsToGLFlags(mFlags));
|
|
}
|
|
if (!mCrTexture->mTexImage || mCrTexture->mTexImage->GetSize() != gfxCbCrSize) {
|
|
mCrTexture->mTexImage = CreateBasicTextureImage(mGL,
|
|
gfxCbCrSize,
|
|
gfxASurface::CONTENT_ALPHA,
|
|
WrapMode(mGL, mFlags & AllowRepeat),
|
|
FlagsToGLFlags(mFlags));
|
|
}
|
|
|
|
RefPtr<gfxImageSurface> tempY = new gfxImageSurface(shmemImage.GetYData(),
|
|
gfxSize, shmemImage.GetYStride(),
|
|
gfxASurface::ImageFormatA8);
|
|
RefPtr<gfxImageSurface> tempCb = new gfxImageSurface(shmemImage.GetCbData(),
|
|
gfxCbCrSize, shmemImage.GetCbCrStride(),
|
|
gfxASurface::ImageFormatA8);
|
|
RefPtr<gfxImageSurface> tempCr = new gfxImageSurface(shmemImage.GetCrData(),
|
|
gfxCbCrSize, shmemImage.GetCbCrStride(),
|
|
gfxASurface::ImageFormatA8);
|
|
|
|
nsIntRegion yRegion(nsIntRect(0, 0, gfxSize.width, gfxSize.height));
|
|
nsIntRegion cbCrRegion(nsIntRect(0, 0, gfxCbCrSize.width, gfxCbCrSize.height));
|
|
|
|
mYTexture->mTexImage->DirectUpdate(tempY, yRegion);
|
|
mCbTexture->mTexImage->DirectUpdate(tempCb, cbCrRegion);
|
|
mCrTexture->mTexImage->DirectUpdate(tempCr, cbCrRegion);
|
|
}
|
|
|
|
bool
|
|
YCbCrTextureHostOGL::Lock()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
TiledTextureHostOGL::~TiledTextureHostOGL()
|
|
{
|
|
DeleteTextures();
|
|
}
|
|
|
|
static void
|
|
GetFormatAndTileForImageFormat(gfxASurface::gfxImageFormat aFormat,
|
|
GLenum& aOutFormat,
|
|
GLenum& aOutType)
|
|
{
|
|
if (aFormat == gfxASurface::ImageFormatRGB16_565) {
|
|
aOutFormat = LOCAL_GL_RGB;
|
|
aOutType = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
|
|
} else {
|
|
aOutFormat = LOCAL_GL_RGBA;
|
|
aOutType = LOCAL_GL_UNSIGNED_BYTE;
|
|
}
|
|
}
|
|
|
|
void
|
|
TiledTextureHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
if (mGL && !glCompositor) {
|
|
DeleteTextures();
|
|
}
|
|
mGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
}
|
|
|
|
void
|
|
TiledTextureHostOGL::DeleteTextures()
|
|
{
|
|
if (mTextureHandle) {
|
|
mGL->MakeCurrent();
|
|
mGL->fDeleteTextures(1, &mTextureHandle);
|
|
|
|
gl::GLContext::UpdateTextureMemoryUsage(gl::GLContext::MemoryFreed, mGLFormat,
|
|
GetTileType(), TILEDLAYERBUFFER_TILE_SIZE);
|
|
mTextureHandle = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
TiledTextureHostOGL::Update(gfxReusableSurfaceWrapper* aReusableSurface, TextureFlags aFlags, const gfx::IntSize& aSize)
|
|
{
|
|
mSize = aSize;
|
|
mGL->MakeCurrent();
|
|
if (aFlags & NewTile) {
|
|
SetFlags(aFlags);
|
|
mGL->fGenTextures(1, &mTextureHandle);
|
|
mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
|
mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
|
} else {
|
|
mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
|
|
// We're re-using a texture, but the format may change. Update the memory
|
|
// reporter with a free and alloc (below) using the old and new formats.
|
|
gl::GLContext::UpdateTextureMemoryUsage(gl::GLContext::MemoryFreed, mGLFormat,
|
|
GetTileType(), TILEDLAYERBUFFER_TILE_SIZE);
|
|
}
|
|
|
|
GLenum type;
|
|
GetFormatAndTileForImageFormat(aReusableSurface->Format(), mGLFormat, type);
|
|
|
|
const unsigned char* buf = aReusableSurface->GetReadOnlyData();
|
|
mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, mGLFormat,
|
|
TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE, 0,
|
|
mGLFormat, type, buf);
|
|
|
|
gl::GLContext::UpdateTextureMemoryUsage(gl::GLContext::MemoryAllocated, mGLFormat,
|
|
type, TILEDLAYERBUFFER_TILE_SIZE);
|
|
|
|
if (mGLFormat == LOCAL_GL_RGB) {
|
|
mFormat = FORMAT_R8G8B8X8;
|
|
} else {
|
|
mFormat = FORMAT_B8G8R8A8;
|
|
}
|
|
}
|
|
|
|
bool
|
|
TiledTextureHostOGL::Lock()
|
|
{
|
|
if (!mTextureHandle) {
|
|
NS_WARNING("TiledTextureHostOGL not ready to be composited");
|
|
return false;
|
|
}
|
|
|
|
mGL->MakeCurrent();
|
|
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
static gfx::SurfaceFormat
|
|
SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat)
|
|
{
|
|
switch (aFormat) {
|
|
case android::PIXEL_FORMAT_RGBA_8888:
|
|
return FORMAT_B8G8R8A8;
|
|
case android::PIXEL_FORMAT_RGBX_8888:
|
|
return FORMAT_B8G8R8X8;
|
|
case android::PIXEL_FORMAT_RGB_565:
|
|
return FORMAT_R5G6B5;
|
|
case android::PIXEL_FORMAT_A_8:
|
|
return FORMAT_A8;
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
|
case HAL_PIXEL_FORMAT_YV12:
|
|
return FORMAT_B8G8R8A8; // yup, use FORMAT_B8G8R8A8 even though it's a YUV texture. This is an external texture.
|
|
default:
|
|
if (aFormat >= 0x100 && aFormat <= 0x1FF) {
|
|
// Reserved range for HAL specific formats.
|
|
return FORMAT_B8G8R8A8;
|
|
} else {
|
|
// This is not super-unreachable, there's a bunch of hypothetical pixel
|
|
// formats we don't deal with.
|
|
// We only want to abort in debug builds here, since if we crash here
|
|
// we'll take down the compositor process and thus the phone. This seems
|
|
// like undesirable behaviour. We'd rather have a subtle artifact.
|
|
MOZ_ASSERT(false, "Unknown Android pixel format.");
|
|
return FORMAT_UNKNOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
static GLenum
|
|
TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
|
|
{
|
|
switch (aFormat) {
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
|
case HAL_PIXEL_FORMAT_YV12:
|
|
return LOCAL_GL_TEXTURE_EXTERNAL;
|
|
case android::PIXEL_FORMAT_RGBA_8888:
|
|
case android::PIXEL_FORMAT_RGBX_8888:
|
|
case android::PIXEL_FORMAT_RGB_565:
|
|
case android::PIXEL_FORMAT_A_8:
|
|
return LOCAL_GL_TEXTURE_2D;
|
|
default:
|
|
if (aFormat >= 0x100 && aFormat <= 0x1FF) {
|
|
// Reserved range for HAL specific formats.
|
|
return LOCAL_GL_TEXTURE_EXTERNAL;
|
|
} else {
|
|
// This is not super-unreachable, there's a bunch of hypothetical pixel
|
|
// formats we don't deal with.
|
|
// We only want to abort in debug builds here, since if we crash here
|
|
// we'll take down the compositor process and thus the phone. This seems
|
|
// like undesirable behaviour. We'd rather have a subtle artifact.
|
|
MOZ_ASSERT(false, "Unknown Android pixel format.");
|
|
return LOCAL_GL_TEXTURE_EXTERNAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
|
|
{
|
|
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
|
if (mGL && !glCompositor) {
|
|
DeleteTextures();
|
|
}
|
|
mGL = glCompositor ? glCompositor->gl() : nullptr;
|
|
}
|
|
|
|
void
|
|
GrallocTextureHostOGL::DeleteTextures()
|
|
{
|
|
if (mGLTexture || mEGLImage) {
|
|
mGL->MakeCurrent();
|
|
if (mGLTexture) {
|
|
mGL->fDeleteTextures(1, &mGLTexture);
|
|
mGLTexture = 0;
|
|
}
|
|
if (mEGLImage) {
|
|
mGL->DestroyEGLImage(mEGLImage);
|
|
mEGLImage = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// only used for hacky fix in gecko 23 for bug 862324
|
|
static void
|
|
RegisterTextureHostAtGrallocBufferActor(TextureHost* aTextureHost, const SurfaceDescriptor& aSurfaceDescriptor)
|
|
{
|
|
if (IsSurfaceDescriptorValid(aSurfaceDescriptor)) {
|
|
GrallocBufferActor* actor = static_cast<GrallocBufferActor*>(aSurfaceDescriptor.get_SurfaceDescriptorGralloc().bufferParent());
|
|
actor->SetTextureHost(aTextureHost);
|
|
}
|
|
}
|
|
|
|
void
|
|
GrallocTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion* aRegion)
|
|
{
|
|
SwapTexturesImpl(aImage, aRegion);
|
|
}
|
|
|
|
void
|
|
GrallocTextureHostOGL::SwapTexturesImpl(const SurfaceDescriptor& aImage,
|
|
nsIntRegion*)
|
|
{
|
|
MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc);
|
|
|
|
if (mBuffer) {
|
|
// only done for hacky fix in gecko 23 for bug 862324.
|
|
RegisterTextureHostAtGrallocBufferActor(nullptr, *mBuffer);
|
|
}
|
|
|
|
const SurfaceDescriptorGralloc& desc = aImage.get_SurfaceDescriptorGralloc();
|
|
mGraphicBuffer = GrallocBufferActor::GetFrom(desc);
|
|
mFormat = SurfaceFormatForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
|
|
mTextureTarget = TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
|
|
|
|
DeleteTextures();
|
|
|
|
// only done for hacky fix in gecko 23 for bug 862324.
|
|
// Doing this in SetBuffer is not enough, as ImageHostBuffered::SwapTextures can
|
|
// change the value of *mBuffer without calling SetBuffer again.
|
|
RegisterTextureHostAtGrallocBufferActor(this, aImage);
|
|
}
|
|
|
|
void GrallocTextureHostOGL::BindTexture(GLenum aTextureUnit)
|
|
{
|
|
MOZ_ASSERT(mGLTexture);
|
|
|
|
mGL->MakeCurrent();
|
|
mGL->fActiveTexture(aTextureUnit);
|
|
mGL->fBindTexture(mTextureTarget, mGLTexture);
|
|
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
}
|
|
|
|
bool
|
|
GrallocTextureHostOGL::IsValid() const
|
|
{
|
|
return !!mGraphicBuffer.get();
|
|
}
|
|
|
|
GrallocTextureHostOGL::~GrallocTextureHostOGL()
|
|
{
|
|
DeleteTextures();
|
|
|
|
// only done for hacky fix in gecko 23 for bug 862324.
|
|
if (mBuffer) {
|
|
// make sure that if the GrallocBufferActor survives us, it doesn't keep a dangling
|
|
// pointer to us.
|
|
RegisterTextureHostAtGrallocBufferActor(nullptr, *mBuffer);
|
|
}
|
|
}
|
|
|
|
bool
|
|
GrallocTextureHostOGL::Lock()
|
|
{
|
|
/*
|
|
* The job of this function is to ensure that the texture is tied to the
|
|
* android::GraphicBuffer, so that texturing will source the GraphicBuffer.
|
|
*
|
|
* To this effect we create an EGLImage wrapping this GraphicBuffer,
|
|
* using CreateEGLImageForNativeBuffer, and then we tie this EGLImage to our
|
|
* texture using fEGLImageTargetTexture2D.
|
|
*
|
|
* We try to avoid re-creating the EGLImage everytime, by keeping it around
|
|
* as the mEGLImage member of this class.
|
|
*/
|
|
MOZ_ASSERT(mGraphicBuffer.get());
|
|
|
|
mGL->MakeCurrent();
|
|
|
|
if (!mGLTexture) {
|
|
mGL->fGenTextures(1, &mGLTexture);
|
|
}
|
|
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
mGL->fBindTexture(mTextureTarget, mGLTexture);
|
|
if (!mEGLImage) {
|
|
mEGLImage = mGL->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
|
|
}
|
|
mGL->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
GrallocTextureHostOGL::Unlock()
|
|
{
|
|
/*
|
|
* The job of this function is to ensure that we release any read lock placed on
|
|
* our android::GraphicBuffer by any drawing code that sourced it via this TextureHost.
|
|
*
|
|
* Indeed, as soon as we draw with a texture that's tied to a android::GraphicBuffer,
|
|
* the GL may place read locks on it. We must ensure that we release them early enough,
|
|
* i.e. before the next time that we will try to acquire a write lock on the same buffer,
|
|
* because read and write locks on gralloc buffers are mutually exclusive.
|
|
*/
|
|
mGL->MakeCurrent();
|
|
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
|
mGL->fBindTexture(mTextureTarget, mGLTexture);
|
|
mGL->fEGLImageTargetTexture2D(mTextureTarget, mGL->GetNullEGLImage());
|
|
}
|
|
|
|
gfx::SurfaceFormat
|
|
GrallocTextureHostOGL::GetFormat() const
|
|
{
|
|
return mFormat;
|
|
}
|
|
|
|
void
|
|
GrallocTextureHostOGL::SetBuffer(SurfaceDescriptor* aBuffer, ISurfaceAllocator* aAllocator) MOZ_OVERRIDE
|
|
{
|
|
MOZ_ASSERT(!mBuffer, "Will leak the old mBuffer");
|
|
mBuffer = aBuffer;
|
|
mDeAllocator = aAllocator;
|
|
|
|
// only done for hacky fix in gecko 23 for bug 862324.
|
|
// Doing this in SwapTextures is not enough, as the crash could occur right after SetBuffer.
|
|
RegisterTextureHostAtGrallocBufferActor(this, *mBuffer);
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace
|
|
} // namespace
|