Bug 728524 - Public shared texture API + Canvas impl. r=bgirard,vlad,jgilbert

This commit is contained in:
Oleg Romashin 2012-05-31 21:30:08 -04:00
parent 55fbefa166
commit 26b109bb23
6 changed files with 227 additions and 57 deletions

View File

@ -47,6 +47,7 @@ namespace mozilla {
namespace gl {
class GLContext;
typedef uintptr_t SharedTextureHandle;
enum ShaderProgramType {
RGBALayerProgramType,
@ -98,6 +99,11 @@ public:
ForceSingleTile = 0x4
};
enum TextureShareType {
ThreadShared = 0x0,
ProcessShared = 0x1
};
typedef gfxASurface::gfxContentType ContentType;
virtual ~TextureImage() {}
@ -847,7 +853,43 @@ public:
return IsExtensionSupported(EXT_framebuffer_blit) || IsExtensionSupported(ANGLE_framebuffer_blit);
}
/**
* Create new shared GLContext content handle, must be released by ReleaseSharedHandle.
*/
virtual SharedTextureHandle CreateSharedHandle(TextureImage::TextureShareType aType) { return nsnull; }
/**
* Publish GLContext content to intermediate buffer attached to shared handle.
* Shared handle content is ready to be used after call returns, and no need extra Flush/Finish are required.
* GLContext must be current before this call
*/
virtual void UpdateSharedHandle(TextureImage::TextureShareType aType,
SharedTextureHandle aSharedHandle) { }
/**
* - It is better to call ReleaseSharedHandle before original GLContext destroyed,
* otherwise warning will be thrown on attempt to destroy Texture associated with SharedHandle, depends on backend implementation.
* - It does not require to be called on context where it was created,
* because SharedHandle suppose to keep Context reference internally,
* or don't require specific context at all, for example IPC SharedHandle.
* - Not recommended to call this between AttachSharedHandle and Draw Target call.
* if it is really required for some special backend, then DetachSharedHandle API must be added with related implementation.
* - It is recommended to stop any possible access to SharedHandle (Attachments, pending GL calls) before calling Release,
* otherwise some artifacts might appear or even crash if API backend implementation does not expect that.
* SharedHandle (currently EGLImage) does not require GLContext because it is EGL call, and can be destroyed
* at any time, unless EGLImage have siblings (which are not expected with current API).
*/
virtual void ReleaseSharedHandle(TextureImage::TextureShareType aType,
SharedTextureHandle aSharedHandle) { }
/**
* Attach Shared GL Handle to GL_TEXTURE_2D target
* GLContext must be current before this call
*/
virtual bool AttachSharedHandle(TextureImage::TextureShareType aType,
SharedTextureHandle aSharedHandle) { return false; }
/**
* Detach Shared GL Handle from GL_TEXTURE_2D target
*/
virtual void DetachSharedHandle(TextureImage::TextureShareType aType,
SharedTextureHandle aSharedHandle) { return; }
private:
GLuint mUserBoundDrawFBO;

View File

@ -9,6 +9,7 @@
#include "gfxUtils.h"
#include "BasicLayersImpl.h"
#include "nsXULAppAPI.h"
using namespace mozilla::gfx;
@ -312,13 +313,28 @@ public:
void DestroyBackBuffer()
{
if (IsSurfaceDescriptorValid(mBackBuffer)) {
if (mBackBuffer.type() == SurfaceDescriptor::TSharedTextureDescriptor) {
SharedTextureDescriptor handle = mBackBuffer.get_SharedTextureDescriptor();
if (mGLContext && handle.handle()) {
mGLContext->ReleaseSharedHandle(handle.shareType(), handle.handle());
mBackBuffer = SurfaceDescriptor();
}
} else if (IsSurfaceDescriptorValid(mBackBuffer)) {
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
mBackBuffer = SurfaceDescriptor();
}
}
private:
typedef mozilla::gl::SharedTextureHandle SharedTextureHandle;
typedef mozilla::gl::TextureImage TextureImage;
SharedTextureHandle GetSharedBackBufferHandle()
{
if (mBackBuffer.type() == SurfaceDescriptor::TSharedTextureDescriptor)
return mBackBuffer.get_SharedTextureDescriptor().handle();
return nsnull;
}
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
@ -354,6 +370,35 @@ BasicShadowableCanvasLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
return;
}
if (mGLContext &&
BasicManager()->GetParentBackendType() == LayerManager::LAYERS_OPENGL) {
TextureImage::TextureShareType flags;
// if process type is default, then it is single-process (non-e10s)
if (XRE_GetProcessType() == GeckoProcessType_Default)
flags = TextureImage::ThreadShared;
else
flags = TextureImage::ProcessShared;
SharedTextureHandle handle = GetSharedBackBufferHandle();
if (!handle) {
handle = mGLContext->CreateSharedHandle(flags);
if (handle) {
mBackBuffer = SharedTextureDescriptor(flags, handle, mBounds.Size());
}
}
if (handle) {
mGLContext->MakeCurrent();
mGLContext->UpdateSharedHandle(flags, handle);
FireDidTransactionCallback();
BasicManager()->PaintedCanvas(BasicManager()->Hold(this),
mNeedsYFlip,
mBackBuffer);
// Move SharedTextureHandle ownership to ShadowLayer
mBackBuffer = SurfaceDescriptor();
return;
}
}
bool isOpaque = (GetContentFlags() & CONTENT_OPAQUE);
if (!IsSurfaceDescriptorValid(mBackBuffer) ||
isOpaque != mBufferIsOpaque) {
@ -377,8 +422,7 @@ BasicShadowableCanvasLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
FireDidTransactionCallback();
BasicManager()->PaintedCanvas(BasicManager()->Hold(this),
mNeedsYFlip ? true : false,
mBackBuffer);
mNeedsYFlip, mBackBuffer);
}
class BasicShadowCanvasLayer : public ShadowCanvasLayer,

View File

@ -21,6 +21,8 @@ using mozilla::layers::MagicGrallocBufferHandle;
using mozilla::layers::SurfaceDescriptorX11;
using mozilla::null_t;
using mozilla::WindowsHandle;
using mozilla::gl::SharedTextureHandle;
using mozilla::gl::TextureImage::TextureShareType;
namespace mozilla {
namespace layers {
@ -34,6 +36,12 @@ struct SurfaceDescriptorD3D10 {
WindowsHandle handle;
};
struct SharedTextureDescriptor {
TextureShareType shareType;
SharedTextureHandle handle;
nsIntSize size;
};
struct SurfaceDescriptorGralloc {
PGrallocBuffer buffer;
};
@ -47,6 +55,7 @@ union SurfaceDescriptor {
SurfaceDescriptorD3D10;
SurfaceDescriptorGralloc;
SurfaceDescriptorX11;
SharedTextureDescriptor;
};
struct YUVImage {

View File

@ -10,6 +10,7 @@
#include "IPC/IPCMessageUtils.h"
#include "Layers.h"
#include "GLContext.h"
#if defined(MOZ_ENABLE_D3D10_LAYER)
# include "mozilla/layers/ShadowLayerUtilsD3D10.h"
@ -74,6 +75,29 @@ struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
};
#endif // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11)
template<>
struct ParamTraits<mozilla::gl::TextureImage::TextureShareType>
{
typedef mozilla::gl::TextureImage::TextureShareType paramType;
static void Write(Message* msg, const paramType& param)
{
MOZ_STATIC_ASSERT(sizeof(paramType) <= sizeof(int32),
"TextureShareType assumes to be int32");
WriteParam(msg, int32(param));
}
static bool Read(const Message* msg, void** iter, paramType* result)
{
int32 type;
if (!ReadParam(msg, iter, &type))
return false;
*result = paramType(type);
return true;
}
};
#if !defined(MOZ_HAVE_SURFACEDESCRIPTORGRALLOC)
template <>
struct ParamTraits<mozilla::layers::MagicGrallocBufferHandle> {

View File

@ -33,6 +33,23 @@ using namespace mozilla;
using namespace mozilla::layers;
using namespace mozilla::gl;
static void
MakeTextureIfNeeded(GLContext* gl, GLuint& aTexture)
{
if (aTexture != 0)
return;
gl->fGenTextures(1, &aTexture);
gl->fActiveTexture(LOCAL_GL_TEXTURE0);
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);
}
void
CanvasLayerOGL::Destroy()
{
@ -72,7 +89,7 @@ CanvasLayerOGL::Initialize(const Data& aData)
} else {
mLayerProgram = gl::RGBXLayerProgramType;
}
MakeTexture();
MakeTextureIfNeeded(gl(), mTexture);
}
}
#endif
@ -98,7 +115,7 @@ CanvasLayerOGL::Initialize(const Data& aData)
GLint texSize = gl()->GetMaxTextureSize();
if (mBounds.width > (2 + texSize) || mBounds.height > (2 + texSize)) {
mDelayedUpdates = true;
MakeTexture();
MakeTextureIfNeeded(gl(), mTexture);
// This should only ever occur with 2d canvas, WebGL can't already have a texture
// of this size can it?
NS_ABORT_IF_FALSE(mCanvasSurface || mDrawTarget,
@ -106,23 +123,6 @@ CanvasLayerOGL::Initialize(const Data& aData)
}
}
void
CanvasLayerOGL::MakeTexture()
{
if (mTexture != 0)
return;
gl()->fGenTextures(1, &mTexture);
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
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);
}
/**
* Following UpdateSurface(), mTexture on context this->gl() should contain the data we want,
* unless mDelayedUpdates is true because of a too-large surface.
@ -157,7 +157,7 @@ CanvasLayerOGL::UpdateSurface()
mTexture == 0)
{
mOGLManager->MakeCurrent();
MakeTexture();
MakeTextureIfNeeded(gl(), mTexture);
}
} else {
nsRefPtr<gfxASurface> updatedAreaSurface;
@ -285,11 +285,17 @@ CanvasLayerOGL::CleanupResources()
}
}
static bool
IsValidSharedTexDescriptor(const SurfaceDescriptor& aDescriptor)
{
return aDescriptor.type() == SurfaceDescriptor::TSharedTextureDescriptor;
}
ShadowCanvasLayerOGL::ShadowCanvasLayerOGL(LayerManagerOGL* aManager)
: ShadowCanvasLayer(aManager, nsnull)
, LayerOGL(aManager)
, mNeedsYFlip(false)
, mTexture(0)
{
mImplData = static_cast<LayerOGL*>(this);
}
@ -321,7 +327,20 @@ ShadowCanvasLayerOGL::Swap(const CanvasSurface& aNewFront,
bool needYFlip,
CanvasSurface* aNewBack)
{
if (!mDestroyed) {
if (mDestroyed) {
*aNewBack = aNewFront;
return;
}
if (IsValidSharedTexDescriptor(aNewFront)) {
MakeTextureIfNeeded(gl(), mTexture);
if (!IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
mFrontBufferDescriptor = SharedTextureDescriptor(TextureImage::ThreadShared, 0, nsIntSize(0, 0));
}
*aNewBack = mFrontBufferDescriptor;
mFrontBufferDescriptor = aNewFront;
mNeedsYFlip = needYFlip;
} else {
AutoOpenSurface autoSurf(OPEN_READ_ONLY, aNewFront);
gfxIntSize sz = autoSurf.Size();
if (!mTexImage || mTexImage->GetSize() != sz ||
@ -330,15 +349,23 @@ ShadowCanvasLayerOGL::Swap(const CanvasSurface& aNewFront,
}
nsIntRegion updateRegion(nsIntRect(0, 0, sz.width, sz.height));
mTexImage->DirectUpdate(autoSurf.Get(), updateRegion);
*aNewBack = aNewFront;
}
*aNewBack = aNewFront;
}
void
ShadowCanvasLayerOGL::DestroyFrontBuffer()
{
mTexImage = nsnull;
if (mTexture) {
gl()->MakeCurrent();
gl()->fDeleteTextures(1, &mTexture);
}
if (IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
SharedTextureDescriptor texDescriptor = mFrontBufferDescriptor.get_SharedTextureDescriptor();
gl()->ReleaseSharedHandle(texDescriptor.shareType(), texDescriptor.handle());
mFrontBufferDescriptor = SurfaceDescriptor();
}
}
void
@ -352,7 +379,7 @@ ShadowCanvasLayerOGL::Destroy()
{
if (!mDestroyed) {
mDestroyed = true;
mTexImage = nsnull;
DestroyFrontBuffer();
}
}
@ -366,16 +393,12 @@ void
ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
const nsIntPoint& aOffset)
{
if (!mTexImage) {
if (!mTexImage && !IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
return;
}
mOGLManager->MakeCurrent();
ShaderProgramOGL *program =
mOGLManager->GetProgram(mTexImage->GetShaderProgramType(),
GetMaskLayer());
gfx3DMatrix effectiveTransform = GetEffectiveTransform();
gfxPattern::GraphicsFilter filter = mFilter;
#ifdef ANDROID
@ -389,6 +412,16 @@ ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
}
#endif
ShaderProgramOGL *program;
if (IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
program = mOGLManager->GetBasicLayerProgram(CanUseOpaqueSurface(),
true,
GetMaskLayer() ? Mask2d : MaskNone);
} else {
program = mOGLManager->GetProgram(mTexImage->GetShaderProgramType(),
GetMaskLayer());
}
program->Activate();
program->SetLayerTransform(effectiveTransform);
program->SetLayerOpacity(GetEffectiveOpacity());
@ -396,30 +429,47 @@ ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
program->SetTextureUnit(0);
program->LoadMask(GetMaskLayer());
mTexImage->SetFilter(filter);
mTexImage->BeginTileIteration();
if (gl()->CanUploadNonPowerOfTwo()) {
do {
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
program->SetLayerQuadRect(mTexImage->GetTileRect());
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip); // FIXME flip order of tiles?
} while (mTexImage->NextTile());
if (IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
// Shared texture handle rendering path, single texture rendering
SharedTextureDescriptor texDescriptor = mFrontBufferDescriptor.get_SharedTextureDescriptor();
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
if (!gl()->AttachSharedHandle(texDescriptor.shareType(), texDescriptor.handle())) {
NS_ERROR("Failed to attach shared texture handle");
return;
}
gl()->ApplyFilterToBoundTexture(filter);
program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), texDescriptor.size()));
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip);
gl()->DetachSharedHandle(texDescriptor.shareType(), texDescriptor.handle());
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
} else {
do {
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
program->SetLayerQuadRect(mTexImage->GetTileRect());
// We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f
// in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture
// We need to reset the origin to 0,0 from the tile rect because the tile originates at 0,0 in the
// actual texture, even though its origin in the composed (tiled) texture is not 0,0
// FIXME: we need to handle mNeedsYFlip, Bug #728625
mOGLManager->BindAndDrawQuadWithTextureRect(program,
nsIntRect(0, 0, mTexImage->GetTileRect().width,
mTexImage->GetTileRect().height),
mTexImage->GetTileRect().Size(),
mTexImage->GetWrapMode(),
mNeedsYFlip);
} while (mTexImage->NextTile());
// Tiled texture image rendering path
mTexImage->SetFilter(filter);
mTexImage->BeginTileIteration();
if (gl()->CanUploadNonPowerOfTwo()) {
do {
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
program->SetLayerQuadRect(mTexImage->GetTileRect());
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip); // FIXME flip order of tiles?
} while (mTexImage->NextTile());
} else {
do {
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
program->SetLayerQuadRect(mTexImage->GetTileRect());
// We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f
// in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture
// We need to reset the origin to 0,0 from the tile rect because the tile originates at 0,0 in the
// actual texture, even though its origin in the composed (tiled) texture is not 0,0
// FIXME: we need to handle mNeedsYFlip, Bug #728625
mOGLManager->BindAndDrawQuadWithTextureRect(program,
nsIntRect(0, 0, mTexImage->GetTileRect().width,
mTexImage->GetTileRect().height),
mTexImage->GetTileRect().Size(),
mTexImage->GetWrapMode(),
mNeedsYFlip);
} while (mTexImage->NextTile());
}
}
}

View File

@ -53,7 +53,6 @@ protected:
gl::ShaderProgramType mLayerProgram;
RefPtr<gfx::DrawTarget> mDrawTarget;
void MakeTexture();
GLuint mTexture;
bool mDelayedUpdates;
@ -127,6 +126,8 @@ private:
nsRefPtr<TextureImage> mTexImage;
bool mNeedsYFlip;
SurfaceDescriptor mFrontBufferDescriptor;
GLuint mTexture;
};
} /* layers */