summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client/CanvasClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/client/CanvasClient.cpp')
-rw-r--r--gfx/layers/client/CanvasClient.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp
new file mode 100644
index 000000000..40513984e
--- /dev/null
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -0,0 +1,531 @@
+/* -*- 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 "CanvasClient.h"
+
+#include "ClientCanvasLayer.h" // for ClientCanvasLayer
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "ScopedGLHelpers.h"
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "nsDebug.h" // for printf_stderr, NS_ASSERTION
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TextureClientSharedSurface.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/* static */ already_AddRefed<CanvasClient>
+CanvasClient::CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ switch (aType) {
+ case CanvasClientTypeShSurf:
+ return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
+ case CanvasClientAsync:
+ return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
+ default:
+ return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
+ break;
+ }
+}
+
+void
+CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ if (!GetForwarder() || !mLayer || !aRenderer ||
+ !aRenderer->GetCanvasClient()) {
+ return;
+ }
+
+ uint64_t asyncID = aRenderer->GetCanvasClientAsyncID();
+ if (asyncID == 0 || mAsyncID == asyncID) {
+ return;
+ }
+
+ static_cast<ShadowLayerForwarder*>(GetForwarder())
+ ->AttachAsyncCompositable(asyncID, mLayer);
+ mAsyncID = asyncID;
+}
+
+void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+
+ if (!aTexture->IsSharedWithCompositor()) {
+ if (!AddTextureClient(aTexture)) {
+ return;
+ }
+ }
+
+ mBackBuffer = nullptr;
+ mFrontBuffer = nullptr;
+ mBufferProviderTexture = aTexture;
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = aTexture;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+ t->mFrameID = mFrameID;
+
+ GetForwarder()->UseTextures(this, textures);
+ aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
+CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ mBufferProviderTexture = nullptr;
+
+ AutoRemoveTexture autoRemove(this);
+ if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
+ autoRemove.mTexture = mBackBuffer;
+ mBackBuffer = nullptr;
+ }
+
+ bool bufferCreated = false;
+ if (!mBackBuffer) {
+ bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
+ gfxContentType contentType = isOpaque
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+ gfx::SurfaceFormat surfaceFormat
+ = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
+ if (!mBackBuffer) {
+ NS_WARNING("Failed to allocate the TextureClient");
+ return;
+ }
+ mBackBuffer->EnableReadLock();
+ MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
+
+ bufferCreated = true;
+ }
+
+ bool updated = false;
+ {
+ TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
+ if (target) {
+ if (!aLayer->UpdateTarget(target)) {
+ NS_WARNING("Failed to copy the canvas into a TextureClient.");
+ return;
+ }
+ updated = true;
+ }
+ }
+
+ if (bufferCreated && !AddTextureClient(mBackBuffer)) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ if (updated) {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mBackBuffer;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
+ t->mFrameID = mFrameID;
+ GetForwarder()->UseTextures(this, textures);
+ mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ mBackBuffer.swap(mFrontBuffer);
+}
+
+already_AddRefed<TextureClient>
+CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer)
+{
+ if (aLayer->IsGLLayer()) {
+ // We want a cairo backend here as we don't want to be copying into
+ // an accelerated backend and we like LockBits to work. This is currently
+ // the most effective way to make this work.
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, BackendType::CAIRO,
+ mTextureFlags | aFlags);
+ }
+
+#ifdef XP_WIN
+ return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags);
+#else
+ // XXX - We should use CreateTextureClientForDrawing, but we first need
+ // to use double buffering.
+ gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, backend,
+ mTextureFlags | aFlags);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+
+CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+{ }
+
+CanvasClientSharedSurface::~CanvasClientSharedSurface()
+{
+ ClearSurfaces();
+}
+
+////////////////////////////////////////
+// Readback
+
+// For formats compatible with R8G8B8A8.
+static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
+ // [RR, GG, BB, AA]
+ Swap(pixel[0], pixel[2]);
+}
+
+class TexClientFactory
+{
+ CompositableForwarder* const mAllocator;
+ const bool mHasAlpha;
+ const gfx::IntSize mSize;
+ const gfx::BackendType mBackendType;
+ const TextureFlags mBaseTexFlags;
+ const LayersBackend mLayersBackend;
+
+public:
+ TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
+ const gfx::IntSize& size, gfx::BackendType backendType,
+ TextureFlags baseTexFlags, LayersBackend layersBackend)
+ : mAllocator(allocator)
+ , mHasAlpha(hasAlpha)
+ , mSize(size)
+ , mBackendType(backendType)
+ , mBaseTexFlags(baseTexFlags)
+ , mLayersBackend(layersBackend)
+ {
+ }
+
+protected:
+ already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
+ return TextureClient::CreateForRawBufferAccess(mAllocator, format,
+ mSize, mBackendType,
+ mBaseTexFlags);
+ }
+
+public:
+ already_AddRefed<TextureClient> CreateB8G8R8AX8() {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ return Create(format);
+ }
+
+ already_AddRefed<TextureClient> CreateR8G8B8AX8() {
+ RefPtr<TextureClient> ret;
+
+ bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
+ if (!areRGBAFormatsBroken) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ ret = Create(format);
+ }
+
+ if (!ret) {
+ ret = CreateB8G8R8AX8();
+ if (ret) {
+ ret->AddFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return ret.forget();
+ }
+};
+
+static already_AddRefed<TextureClient>
+TexClientFromReadback(SharedSurface* src, CompositableForwarder* allocator,
+ TextureFlags baseFlags, LayersBackend layersBackend)
+{
+ auto backendType = gfx::BackendType::CAIRO;
+ TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
+ baseFlags, layersBackend);
+
+ RefPtr<TextureClient> texClient;
+
+ {
+ gl::ScopedReadbackFB autoReadback(src);
+
+ // We have a source FB, now we need a format.
+ GLenum destFormat = LOCAL_GL_BGRA;
+ GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
+ GLenum readFormat;
+ GLenum readType;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ auto gl = src->mGL;
+ GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
+
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA ||
+ readFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // With a format and type, we can create texClient.
+ if (readFormat == LOCAL_GL_BGRA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // 0xAARRGGBB
+ // In Lendian: [BB, GG, RR, AA]
+ texClient = factory.CreateB8G8R8AX8();
+
+ } else if (readFormat == LOCAL_GL_RGBA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // [RR, GG, BB, AA]
+ texClient = factory.CreateR8G8B8AX8();
+ } else {
+ MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
+ }
+
+ MOZ_ASSERT(texClient);
+ if (!texClient)
+ return nullptr;
+
+ // With a texClient, we can lock for writing.
+ TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
+ DebugOnly<bool> succeeded = autoLock.Succeeded();
+ MOZ_ASSERT(succeeded, "texture should have locked");
+
+ MappedTextureData mapped;
+ texClient->BorrowMappedData(mapped);
+
+ // ReadPixels from the current FB into mapped.data.
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+
+ {
+ ScopedPackState scopedPackState(gl);
+
+ MOZ_ASSERT(mapped.stride/4 == mapped.size.width);
+ gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data);
+ }
+
+ // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
+ // RB_SWAPPED doesn't work with Basic. (bug ???????)
+ // RB_SWAPPED doesn't work with D3D9. (bug ???????)
+ bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
+ layersBackend == LayersBackend::LAYERS_D3D9 ||
+ layersBackend == LayersBackend::LAYERS_D3D11;
+ if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
+ layersNeedsManualSwap)
+ {
+ size_t pixels = width * height;
+ uint8_t* itr = mapped.data;
+ for (size_t i = 0; i < pixels; i++) {
+ SwapRB_R8G8B8A8(itr);
+ itr += 4;
+ }
+
+ texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return texClient.forget();
+}
+
+////////////////////////////////////////
+
+static already_AddRefed<SharedSurfaceTextureClient>
+CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
+{
+ RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
+ if (!dest) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* destSurf = dest->Surf();
+
+ destSurf->ProducerAcquire();
+ SharedSurface::ProdCopy(src, dest->Surf(), factory);
+ destSurf->ProducerRelease();
+
+ return dest.forget();
+}
+
+void
+CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ Renderer renderer;
+ renderer.construct<ClientCanvasLayer*>(aLayer);
+ UpdateRenderer(aSize, renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ Renderer renderer;
+ renderer.construct<AsyncCanvasRenderer*>(aRenderer);
+ UpdateRenderer(aRenderer->GetSize(), renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer)
+{
+ GLContext* gl = nullptr;
+ ClientCanvasLayer* layer = nullptr;
+ AsyncCanvasRenderer* asyncRenderer = nullptr;
+ if (aRenderer.constructed<ClientCanvasLayer*>()) {
+ layer = aRenderer.ref<ClientCanvasLayer*>();
+ gl = layer->mGLContext;
+ } else {
+ asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
+ gl = asyncRenderer->mGLContext;
+ }
+ gl->MakeCurrent();
+
+ RefPtr<TextureClient> newFront;
+
+ if (layer && layer->mGLFrontbuffer) {
+ mShSurfClient = CloneSurface(layer->mGLFrontbuffer.get(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ gfxCriticalError() << "Invalid canvas front buffer";
+ return;
+ }
+ } else if (layer && layer->mIsMirror) {
+ mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ return;
+ }
+ } else {
+ mShSurfClient = gl->Screen()->Front();
+ if (mShSurfClient && mShSurfClient->GetAllocator() &&
+ mShSurfClient->GetAllocator() != GetForwarder()->GetTextureForwarder()) {
+ mShSurfClient = CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory());
+ }
+ if (!mShSurfClient) {
+ return;
+ }
+ }
+ MOZ_ASSERT(mShSurfClient);
+
+ newFront = mShSurfClient;
+
+ SharedSurface* surf = mShSurfClient->Surf();
+
+ // Readback if needed.
+ mReadbackClient = nullptr;
+
+ auto forwarder = GetForwarder();
+
+ bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
+ if (needsReadback) {
+ TextureFlags flags = TextureFlags::IMMUTABLE;
+
+ CompositableForwarder* shadowForwarder = nullptr;
+ if (layer) {
+ flags |= layer->Flags();
+ shadowForwarder = layer->ClientManager()->AsShadowForwarder();
+ } else {
+ MOZ_ASSERT(asyncRenderer);
+ flags |= mTextureFlags;
+ shadowForwarder = GetForwarder();
+ }
+
+ auto layersBackend = shadowForwarder->GetCompositorBackendType();
+ mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend);
+
+ newFront = mReadbackClient;
+ } else {
+ mReadbackClient = nullptr;
+ }
+
+ if (asyncRenderer) {
+ // If surface type is Basic, above codes will readback
+ // the GLContext to mReadbackClient in order to send frame to
+ // compositor. We copy from this TextureClient directly by
+ // calling CopyFromTextureClient().
+ // Therefore, if main-thread want the content of GLContext,
+ // it doesn't have to readback from GLContext again.
+ //
+ // Otherwise, if surface type isn't Basic, we will read from
+ // SharedSurface directly from main-thread. We still pass
+ // mReadbackClient which is nullptr here to tell
+ // AsyncCanvasRenderer reset some properties.
+ asyncRenderer->CopyFromTextureClient(mReadbackClient);
+ }
+
+ MOZ_ASSERT(newFront);
+ if (!newFront) {
+ // May happen in a release build in case of memory pressure.
+ gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
+ return;
+ }
+
+ mNewFront = newFront;
+}
+
+void
+CanvasClientSharedSurface::Updated()
+{
+ if (!mNewFront) {
+ return;
+ }
+
+ auto forwarder = GetForwarder();
+
+ mFront = mNewFront;
+ mNewFront = nullptr;
+
+ // Add the new TexClient.
+ MOZ_ALWAYS_TRUE( AddTextureClient(mFront) );
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mFront;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
+ t->mFrameID = mFrameID;
+ forwarder->UseTextures(this, textures);
+}
+
+void
+CanvasClientSharedSurface::OnDetach() {
+ ClearSurfaces();
+}
+
+void
+CanvasClientSharedSurface::ClearSurfaces()
+{
+ if (mFront) {
+ mFront->CancelWaitForRecycle();
+ }
+ mFront = nullptr;
+ mNewFront = nullptr;
+ mShSurfClient = nullptr;
+ mReadbackClient = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla