import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,216 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_CANVASCLIENT_H
#define MOZILLA_GFX_CANVASCLIENT_H
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
#include "mozilla/layers/PersistentBufferProvider.h"
// Fix X11 header brain damage that conflicts with MaybeOneOf::None
#undef None
#include "mozilla/MaybeOneOf.h"
#include "mozilla/mozalloc.h" // for operator delete
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Types.h" // for SurfaceFormat
namespace mozilla {
namespace layers {
class AsyncCanvasRenderer;
class ClientCanvasLayer;
class CompositableForwarder;
class ShadowableLayer;
class SharedSurfaceTextureClient;
/**
* Compositable client for 2d and webgl canvas.
*/
class CanvasClient : public CompositableClient
{
public:
typedef MaybeOneOf<ClientCanvasLayer*, AsyncCanvasRenderer*> Renderer;
/**
* Creates, configures, and returns a new canvas client. If necessary, a
* message will be sent to the compositor to create a corresponding image
* host.
*/
enum CanvasClientType {
CanvasClientSurface,
CanvasClientGLContext,
CanvasClientTypeShSurf,
CanvasClientAsync, // webgl on workers
};
static already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClientType aType,
CompositableForwarder* aFwd,
TextureFlags aFlags);
CanvasClient(CompositableForwarder* aFwd, TextureFlags aFlags)
: CompositableClient(aFwd, aFlags)
, mFrameID(0)
{
mTextureFlags = aFlags;
}
virtual ~CanvasClient() {}
virtual void Clear() {};
virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) = 0;
virtual bool AddTextureClient(TextureClient* aTexture) override
{
++mFrameID;
return CompositableClient::AddTextureClient(aTexture);
}
virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
virtual void UpdateFromTexture(TextureClient* aTexture) {}
virtual void Updated() { }
protected:
int32_t mFrameID;
};
// Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
class CanvasClient2D : public CanvasClient
{
public:
CanvasClient2D(CompositableForwarder* aLayerForwarder,
TextureFlags aFlags)
: CanvasClient(aLayerForwarder, aFlags)
{
}
TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::IMAGE, mTextureFlags);
}
virtual void Clear() override
{
mBackBuffer = mFrontBuffer = nullptr;
}
virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
virtual void UpdateFromTexture(TextureClient* aBuffer) override;
virtual bool AddTextureClient(TextureClient* aTexture) override
{
return CanvasClient::AddTextureClient(aTexture);
}
virtual void OnDetach() override
{
mBackBuffer = mFrontBuffer = nullptr;
}
private:
already_AddRefed<TextureClient>
CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
TextureFlags aFlags,
ClientCanvasLayer* aLayer);
RefPtr<TextureClient> mBackBuffer;
RefPtr<TextureClient> mFrontBuffer;
// We store this texture separately to make sure it is not written into
// in Update() if for some silly reason we end up alternating between
// UpdateFromTexture and Update.
// This code is begging for a cleanup. The situation described above should
// not be made possible.
RefPtr<TextureClient> mBufferProviderTexture;
};
// Used for GL canvases where we don't need to do any readback, i.e., with a
// GL backend.
class CanvasClientSharedSurface : public CanvasClient
{
private:
RefPtr<SharedSurfaceTextureClient> mShSurfClient;
RefPtr<TextureClient> mReadbackClient;
RefPtr<TextureClient> mFront;
RefPtr<TextureClient> mNewFront;
void ClearSurfaces();
public:
CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
TextureFlags aFlags);
~CanvasClientSharedSurface();
virtual TextureInfo GetTextureInfo() const override {
return TextureInfo(CompositableType::IMAGE);
}
virtual void Clear() override {
ClearSurfaces();
}
virtual void Update(gfx::IntSize aSize,
ClientCanvasLayer* aLayer) override;
void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
virtual void Updated() override;
virtual void OnDetach() override;
};
/**
* Used for OMT<canvas> uploads using the image bridge protocol.
* Actual CanvasClient is on the ImageBridgeChild thread, so we
* only forward its AsyncID here
*/
class CanvasClientBridge final : public CanvasClient
{
public:
CanvasClientBridge(CompositableForwarder* aLayerForwarder,
TextureFlags aFlags)
: CanvasClient(aLayerForwarder, aFlags)
, mAsyncID(0)
, mLayer(nullptr)
{
}
TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::IMAGE);
}
virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override
{
}
virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
void SetLayer(ShadowableLayer* aLayer)
{
mLayer = aLayer;
}
protected:
uint64_t mAsyncID;
ShadowableLayer* mLayer;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,261 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientCanvasLayer.h"
#include "GLContext.h" // for GLContext
#include "GLScreenBuffer.h" // for GLScreenBuffer
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "SharedSurfaceEGL.h" // for SurfaceFactory_EGLImage
#include "SharedSurfaceGL.h" // for SurfaceFactory_GLTexture, etc
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/layers/AsyncCanvasRenderer.h"
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/LayersTypes.h"
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
#include "gfxPrefs.h" // for WebGLForceLayersReadback
using namespace mozilla::gfx;
using namespace mozilla::gl;
namespace mozilla {
namespace layers {
ClientCanvasLayer::~ClientCanvasLayer()
{
MOZ_COUNT_DTOR(ClientCanvasLayer);
if (mCanvasClient) {
mCanvasClient->OnDetach();
mCanvasClient = nullptr;
}
}
void
ClientCanvasLayer::Initialize(const Data& aData)
{
CopyableCanvasLayer::Initialize(aData);
mCanvasClient = nullptr;
if (!mGLContext)
return;
GLScreenBuffer* screen = mGLContext->Screen();
SurfaceCaps caps;
if (mGLFrontbuffer) {
// The screen caps are irrelevant if we're using a separate frontbuffer.
caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA()
: SurfaceCaps::ForRGB();
} else {
MOZ_ASSERT(screen);
caps = screen->mCaps;
}
MOZ_ASSERT(caps.alpha == aData.mHasAlpha);
auto forwarder = ClientManager()->AsShadowForwarder();
mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT;
if (!aData.mIsGLAlphaPremult) {
mFlags |= TextureFlags::NON_PREMULTIPLIED;
}
UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags);
if (mGLFrontbuffer || aData.mIsMirror) {
// We're using a source other than the one in the default screen.
// (SkiaGL)
mFactory = Move(factory);
if (!mFactory) {
// Absolutely must have a factory here, so create a basic one
mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags);
}
} else {
if (factory)
screen->Morph(Move(factory));
}
}
void
ClientCanvasLayer::RenderLayer()
{
PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
js::ProfileEntry::Category::GRAPHICS);
RenderMaskLayers(this);
if (!mCanvasClient) {
TextureFlags flags = TextureFlags::DEFAULT;
if (mOriginPos == gl::OriginPos::BottomLeft) {
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
}
if (!mIsAlphaPremultiplied) {
flags |= TextureFlags::NON_PREMULTIPLIED;
}
mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
ClientManager()->AsShadowForwarder(),
flags);
if (!mCanvasClient) {
return;
}
if (HasShadow()) {
if (mAsyncRenderer) {
static_cast<CanvasClientBridge*>(mCanvasClient.get())->SetLayer(this);
} else {
mCanvasClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this);
}
}
}
if (mCanvasClient && mAsyncRenderer) {
mCanvasClient->UpdateAsync(mAsyncRenderer);
}
if (!IsDirty()) {
return;
}
Painted();
FirePreTransactionCallback();
if (mBufferProvider && mBufferProvider->GetTextureClient()) {
if (!mBufferProvider->SetForwarder(ClientManager()->AsShadowForwarder())) {
gfxCriticalNote << "BufferProvider::SetForwarder failed";
return;
}
mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
} else {
mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
}
FireDidTransactionCallback();
ClientManager()->Hold(this);
mCanvasClient->Updated();
}
bool
ClientCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
{
MOZ_ASSERT(aDestTarget);
if (!aDestTarget) {
return false;
}
RefPtr<SourceSurface> surface;
if (!mGLContext) {
AutoReturnSnapshot autoReturn;
if (mAsyncRenderer) {
surface = mAsyncRenderer->GetSurface();
} else if (mBufferProvider) {
surface = mBufferProvider->BorrowSnapshot();
autoReturn.mSnapshot = &surface;
autoReturn.mBufferProvider = mBufferProvider;
}
MOZ_ASSERT(surface);
if (!surface) {
return false;
}
aDestTarget->CopySurface(surface,
IntRect(0, 0, mBounds.width, mBounds.height),
IntPoint(0, 0));
return true;
}
SharedSurface* frontbuffer = nullptr;
if (mGLFrontbuffer) {
frontbuffer = mGLFrontbuffer.get();
} else {
GLScreenBuffer* screen = mGLContext->Screen();
const auto& front = screen->Front();
if (front) {
frontbuffer = front->Surf();
}
}
if (!frontbuffer) {
NS_WARNING("Null frame received.");
return false;
}
IntSize readSize(frontbuffer->mSize);
SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
? SurfaceFormat::B8G8R8X8
: SurfaceFormat::B8G8R8A8;
bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
// Try to read back directly into aDestTarget's output buffer
uint8_t* destData;
IntSize destSize;
int32_t destStride;
SurfaceFormat destFormat;
if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
if (destSize == readSize && destFormat == format) {
RefPtr<DataSourceSurface> data =
Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
mGLContext->Readback(frontbuffer, data);
if (needsPremult) {
gfxUtils::PremultiplyDataSurface(data, data);
}
aDestTarget->ReleaseBits(destData);
return true;
}
aDestTarget->ReleaseBits(destData);
}
RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
// There will already be a warning from inside of GetTempSurface, but
// it doesn't hurt to complain:
if (NS_WARN_IF(!resultSurf)) {
return false;
}
// Readback handles Flush/MarkDirty.
mGLContext->Readback(frontbuffer, resultSurf);
if (needsPremult) {
gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
}
aDestTarget->CopySurface(resultSurf,
IntRect(0, 0, readSize.width, readSize.height),
IntPoint(0, 0));
return true;
}
CanvasClient::CanvasClientType
ClientCanvasLayer::GetCanvasClientType()
{
if (mAsyncRenderer) {
return CanvasClient::CanvasClientAsync;
}
if (mGLContext) {
return CanvasClient::CanvasClientTypeShSurf;
}
return CanvasClient::CanvasClientSurface;
}
already_AddRefed<CanvasLayer>
ClientLayerManager::CreateCanvasLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
RefPtr<ClientCanvasLayer> layer =
new ClientCanvasLayer(this);
CREATE_SHADOW(Canvas);
return layer.forget();
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef GFX_CLIENTCANVASLAYER_H
#define GFX_CLIENTCANVASLAYER_H
#include "CanvasClient.h" // for CanvasClient, etc
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
#include "Layers.h" // for CanvasLayer, etc
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/LayersMessages.h" // for CanvasLayerAttributes, etc
#include "mozilla/mozalloc.h" // for operator delete
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsRegion.h" // for nsIntRegion
namespace mozilla {
namespace gl {
class SurfaceFactory;
} // namespace gl
namespace layers {
class CompositableClient;
class ShadowableLayer;
class ClientCanvasLayer : public CopyableCanvasLayer,
public ClientLayer
{
typedef CanvasClient::CanvasClientType CanvasClientType;
public:
explicit ClientCanvasLayer(ClientLayerManager* aLayerManager) :
CopyableCanvasLayer(aLayerManager, static_cast<ClientLayer*>(this))
{
MOZ_COUNT_CTOR(ClientCanvasLayer);
}
protected:
virtual ~ClientCanvasLayer();
public:
virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
CanvasLayer::SetVisibleRegion(aRegion);
}
virtual void Initialize(const Data& aData) override;
virtual void RenderLayer() override;
virtual void ClearCachedResources() override
{
if (mCanvasClient) {
mCanvasClient->Clear();
}
}
virtual void HandleMemoryPressure() override
{
if (mCanvasClient) {
mCanvasClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = CanvasLayerAttributes(mSamplingFilter, mBounds);
}
virtual Layer* AsLayer() override { return this; }
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual void Disconnect() override
{
mCanvasClient = nullptr;
ClientLayer::Disconnect();
}
virtual CompositableClient* GetCompositableClient() override
{
return mCanvasClient;
}
const TextureFlags& Flags() const { return mFlags; }
protected:
bool UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
CanvasClientType GetCanvasClientType();
RefPtr<CanvasClient> mCanvasClient;
UniquePtr<gl::SurfaceFactory> mFactory;
TextureFlags mFlags;
friend class CanvasClient2D;
friend class CanvasClientSharedSurface;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientLayerManager.h" // for ClientLayerManager, etc
#include "Layers.h" // for ColorLayer, etc
#include "mozilla/layers/LayersMessages.h" // for ColorLayerAttributes, etc
#include "mozilla/mozalloc.h" // for operator new
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRegion.h" // for nsIntRegion
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
class ClientColorLayer : public ColorLayer,
public ClientLayer {
public:
explicit ClientColorLayer(ClientLayerManager* aLayerManager) :
ColorLayer(aLayerManager, static_cast<ClientLayer*>(this))
{
MOZ_COUNT_CTOR(ClientColorLayer);
}
protected:
virtual ~ClientColorLayer()
{
MOZ_COUNT_DTOR(ClientColorLayer);
}
public:
virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
ColorLayer::SetVisibleRegion(aRegion);
}
virtual void RenderLayer()
{
RenderMaskLayers(this);
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ColorLayerAttributes(GetColor(), GetBounds());
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect()
{
ClientLayer::Disconnect();
}
protected:
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
};
already_AddRefed<ColorLayer>
ClientLayerManager::CreateColorLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
RefPtr<ClientColorLayer> layer =
new ClientColorLayer(this);
CREATE_SHADOW(Color);
return layer.forget();
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientContainerLayer.h"
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "mozilla/mozalloc.h" // for operator new
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
namespace mozilla {
namespace layers {
already_AddRefed<ContainerLayer>
ClientLayerManager::CreateContainerLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
RefPtr<ClientContainerLayer> layer =
new ClientContainerLayer(this);
CREATE_SHADOW(Container);
return layer.forget();
}
already_AddRefed<RefLayer>
ClientLayerManager::CreateRefLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
RefPtr<ClientRefLayer> layer =
new ClientRefLayer(this);
CREATE_SHADOW(Ref);
return layer.forget();
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,189 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef GFX_CLIENTCONTAINERLAYER_H
#define GFX_CLIENTCONTAINERLAYER_H
#include <stdint.h> // for uint32_t
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "Layers.h" // for Layer, ContainerLayer, etc
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
#include "nsRegion.h" // for nsIntRegion
#include "nsTArray.h" // for AutoTArray
#include "ReadbackProcessor.h"
#include "ClientPaintedLayer.h"
namespace mozilla {
namespace layers {
class ShadowableLayer;
class ClientContainerLayer : public ContainerLayer,
public ClientLayer
{
public:
explicit ClientContainerLayer(ClientLayerManager* aManager) :
ContainerLayer(aManager, static_cast<ClientLayer*>(this))
{
MOZ_COUNT_CTOR(ClientContainerLayer);
mSupportsComponentAlphaChildren = true;
}
protected:
virtual ~ClientContainerLayer()
{
while (mFirstChild) {
ContainerLayer::RemoveChild(mFirstChild);
}
MOZ_COUNT_DTOR(ClientContainerLayer);
}
public:
virtual void RenderLayer() override
{
RenderMaskLayers(this);
DefaultComputeSupportsComponentAlphaChildren();
AutoTArray<Layer*, 12> children;
SortChildrenBy3DZOrder(children);
ReadbackProcessor readback;
readback.BuildUpdates(this);
for (uint32_t i = 0; i < children.Length(); i++) {
Layer* child = children.ElementAt(i);
ToClientLayer(child)->RenderLayerWithReadback(&readback);
if (!ClientManager()->GetRepeatTransaction() &&
!child->GetInvalidRegion().IsEmpty()) {
child->Mutated();
}
}
}
virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
ContainerLayer::SetVisibleRegion(aRegion);
}
virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
{
if(!ClientManager()->InConstruction()) {
NS_ERROR("Can only set properties in construction phase");
return false;
}
if (!ContainerLayer::InsertAfter(aChild, aAfter)) {
return false;
}
ClientManager()->AsShadowForwarder()->InsertAfter(ClientManager()->Hold(this),
ClientManager()->Hold(aChild),
aAfter ? ClientManager()->Hold(aAfter) : nullptr);
return true;
}
virtual bool RemoveChild(Layer* aChild) override
{
if (!ClientManager()->InConstruction()) {
NS_ERROR("Can only set properties in construction phase");
return false;
}
// hold on to aChild before we remove it!
ShadowableLayer *heldChild = ClientManager()->Hold(aChild);
if (!ContainerLayer::RemoveChild(aChild)) {
return false;
}
ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this), heldChild);
return true;
}
virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
{
if (!ClientManager()->InConstruction()) {
NS_ERROR("Can only set properties in construction phase");
return false;
}
if (!ContainerLayer::RepositionChild(aChild, aAfter)) {
return false;
}
ClientManager()->AsShadowForwarder()->RepositionChild(ClientManager()->Hold(this),
ClientManager()->Hold(aChild),
aAfter ? ClientManager()->Hold(aAfter) : nullptr);
return true;
}
virtual Layer* AsLayer() override { return this; }
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
{
DefaultComputeEffectiveTransforms(aTransformToSurface);
}
void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
virtual void Disconnect() override
{
ClientLayer::Disconnect();
}
protected:
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
};
class ClientRefLayer : public RefLayer,
public ClientLayer {
public:
explicit ClientRefLayer(ClientLayerManager* aManager) :
RefLayer(aManager, static_cast<ClientLayer*>(this))
{
MOZ_COUNT_CTOR(ClientRefLayer);
}
protected:
virtual ~ClientRefLayer()
{
MOZ_COUNT_DTOR(ClientRefLayer);
}
public:
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect()
{
ClientLayer::Disconnect();
}
virtual void RenderLayer() { }
virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
{
DefaultComputeEffectiveTransforms(aTransformToSurface);
}
private:
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,170 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientLayerManager.h" // for ClientLayerManager, etc
#include "ImageContainer.h" // for AutoLockImage, etc
#include "ImageLayers.h" // for ImageLayer
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/ImageClient.h" // for ImageClient, etc
#include "mozilla/layers/LayersMessages.h" // for ImageLayerAttributes, etc
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRegion.h" // for nsIntRegion
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
class ClientImageLayer : public ImageLayer,
public ClientLayer {
public:
explicit ClientImageLayer(ClientLayerManager* aLayerManager)
: ImageLayer(aLayerManager, static_cast<ClientLayer*>(this))
, mImageClientTypeContainer(CompositableType::UNKNOWN)
{
MOZ_COUNT_CTOR(ClientImageLayer);
}
protected:
virtual ~ClientImageLayer()
{
DestroyBackBuffer();
MOZ_COUNT_DTOR(ClientImageLayer);
}
virtual void SetContainer(ImageContainer* aContainer) override
{
ImageLayer::SetContainer(aContainer);
mImageClientTypeContainer = CompositableType::UNKNOWN;
}
virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
ImageLayer::SetVisibleRegion(aRegion);
}
virtual void RenderLayer() override;
virtual void ClearCachedResources() override
{
DestroyBackBuffer();
}
virtual void HandleMemoryPressure() override
{
if (mImageClient) {
mImageClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = ImageLayerAttributes(mSamplingFilter, mScaleToSize, mScaleMode);
}
virtual Layer* AsLayer() override { return this; }
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual void Disconnect() override
{
DestroyBackBuffer();
ClientLayer::Disconnect();
}
void DestroyBackBuffer()
{
if (mImageClient) {
mImageClient->SetLayer(nullptr);
mImageClient->OnDetach();
mImageClient = nullptr;
}
}
virtual CompositableClient* GetCompositableClient() override
{
return mImageClient;
}
protected:
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
CompositableType GetImageClientType()
{
if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
return mImageClientTypeContainer;
}
if (mContainer->IsAsync()) {
mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
return mImageClientTypeContainer;
}
AutoLockImage autoLock(mContainer);
mImageClientTypeContainer = autoLock.HasImage()
? CompositableType::IMAGE : CompositableType::UNKNOWN;
return mImageClientTypeContainer;
}
RefPtr<ImageClient> mImageClient;
CompositableType mImageClientTypeContainer;
};
void
ClientImageLayer::RenderLayer()
{
RenderMaskLayers(this);
if (!mContainer) {
return;
}
if (!mImageClient ||
!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
CompositableType type = GetImageClientType();
if (type == CompositableType::UNKNOWN) {
return;
}
TextureFlags flags = TextureFlags::DEFAULT;
mImageClient = ImageClient::CreateImageClient(type,
ClientManager()->AsShadowForwarder(),
flags);
if (!mImageClient) {
return;
}
mImageClient->SetLayer(this);
if (HasShadow() && !mContainer->IsAsync()) {
mImageClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
}
if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
return;
}
}
ClientManager()->Hold(this);
}
already_AddRefed<ImageLayer>
ClientLayerManager::CreateImageLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
RefPtr<ClientImageLayer> layer =
new ClientImageLayer(this);
CREATE_SHADOW(Image);
return layer.forget();
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,907 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientLayerManager.h"
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "gfxPrefs.h" // for gfxPrefs::LayersTile...
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/Hal.h"
#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
#include "mozilla/dom/TabChild.h" // for TabChild
#include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration
#include "mozilla/layers/CompositableClient.h"
#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
#include "mozilla/layers/ContentClient.h"
#include "mozilla/layers/FrameUniformityData.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/PLayerChild.h" // for PLayerChild
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ShadowLayerChild.h"
#include "mozilla/layers/PersistentBufferProvider.h"
#include "ClientReadbackLayer.h" // for ClientReadbackLayer
#include "nsAString.h"
#include "nsDisplayList.h"
#include "nsIWidgetListener.h"
#include "nsTArray.h" // for AutoTArray
#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
#include "TiledLayerBuffer.h"
#include "mozilla/dom/WindowBinding.h" // for Overfill Callback
#include "FrameLayerBuilder.h" // for FrameLayerbuilder
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#include "LayerMetricsWrapper.h"
#endif
#ifdef XP_WIN
#include "mozilla/gfx/DeviceManagerDx.h"
#endif
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
void
ClientLayerManager::MemoryPressureObserver::Destroy()
{
UnregisterMemoryPressureEvent();
mClientLayerManager = nullptr;
}
NS_IMETHODIMP
ClientLayerManager::MemoryPressureObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData)
{
if (!mClientLayerManager || strcmp(aTopic, "memory-pressure")) {
return NS_OK;
}
mClientLayerManager->HandleMemoryPressure();
return NS_OK;
}
void
ClientLayerManager::MemoryPressureObserver::RegisterMemoryPressureEvent()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService) {
observerService->AddObserver(this, "memory-pressure", false);
}
}
void
ClientLayerManager::MemoryPressureObserver::UnregisterMemoryPressureEvent()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, "memory-pressure");
}
}
NS_IMPL_ISUPPORTS(ClientLayerManager::MemoryPressureObserver, nsIObserver)
ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
: mPhase(PHASE_NONE)
, mWidget(aWidget)
, mLatestTransactionId(0)
, mLastPaintTime(TimeDuration::Forever())
, mTargetRotation(ROTATION_0)
, mRepeatTransaction(false)
, mIsRepeatTransaction(false)
, mTransactionIncomplete(false)
, mCompositorMightResample(false)
, mNeedsComposite(false)
, mPaintSequenceNumber(0)
, mForwarder(new ShadowLayerForwarder(this))
, mDeviceCounter(gfxPlatform::GetPlatform()->GetDeviceCounter())
{
MOZ_COUNT_CTOR(ClientLayerManager);
mMemoryPressureObserver = new MemoryPressureObserver(this);
}
ClientLayerManager::~ClientLayerManager()
{
mMemoryPressureObserver->Destroy();
ClearCachedResources();
// Stop receiveing AsyncParentMessage at Forwarder.
// After the call, the message is directly handled by LayerTransactionChild.
// Basically this function should be called in ShadowLayerForwarder's
// destructor. But when the destructor is triggered by
// CompositorBridgeChild::Destroy(), the destructor can not handle it correctly.
// See Bug 1000525.
mForwarder->StopReceiveAsyncParentMessge();
mRoot = nullptr;
MOZ_COUNT_DTOR(ClientLayerManager);
}
void
ClientLayerManager::Destroy()
{
// It's important to call ClearCachedResource before Destroy because the
// former will early-return if the later has already run.
ClearCachedResources();
LayerManager::Destroy();
if (mTransactionIdAllocator) {
// Make sure to notify the refresh driver just in case it's waiting on a
// pending transaction. Do this at the top of the event loop so we don't
// cause a paint to occur during compositor shutdown.
RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
uint64_t id = mLatestTransactionId;
RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
allocator->NotifyTransactionCompleted(id);
});
NS_DispatchToMainThread(task.forget());
}
// Forget the widget pointer in case we outlive our owning widget.
mWidget = nullptr;
}
int32_t
ClientLayerManager::GetMaxTextureSize() const
{
return mForwarder->GetMaxTextureSize();
}
void
ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
ScreenRotation aRotation)
{
mTargetRotation = aRotation;
}
void
ClientLayerManager::SetRoot(Layer* aLayer)
{
if (mRoot != aLayer) {
// Have to hold the old root and its children in order to
// maintain the same view of the layer tree in this process as
// the parent sees. Otherwise layers can be destroyed
// mid-transaction and bad things can happen (v. bug 612573)
if (mRoot) {
Hold(mRoot);
}
mForwarder->SetRoot(Hold(aLayer));
NS_ASSERTION(aLayer, "Root can't be null");
NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
mRoot = aLayer;
}
}
void
ClientLayerManager::Mutated(Layer* aLayer)
{
LayerManager::Mutated(aLayer);
NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
mForwarder->Mutated(Hold(aLayer));
}
already_AddRefed<ReadbackLayer>
ClientLayerManager::CreateReadbackLayer()
{
RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
return layer.forget();
}
bool
ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
{
MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
if (!mForwarder->IPCOpen()) {
gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
return false;
}
mInTransaction = true;
mTransactionStart = TimeStamp::Now();
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
Log();
#endif
NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
mPhase = PHASE_CONSTRUCTION;
if (DependsOnStaleDevice()) {
FrameLayerBuilder::InvalidateAllLayers(this);
mDeviceCounter = gfxPlatform::GetPlatform()->GetDeviceCounter();
}
MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
// If the last transaction was incomplete (a failed DoEmptyTransaction),
// don't signal a new transaction to ShadowLayerForwarder. Carry on adding
// to the previous transaction.
dom::ScreenOrientationInternal orientation;
if (dom::TabChild* window = mWidget->GetOwningTabChild()) {
orientation = window->GetOrientation();
} else {
hal::ScreenConfiguration currentConfig;
hal::GetCurrentScreenConfiguration(&currentConfig);
orientation = currentConfig.orientation();
}
LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds();
targetBounds.x = targetBounds.y = 0;
mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation,
orientation);
// If we're drawing on behalf of a context with async pan/zoom
// enabled, then the entire buffer of painted layers might be
// composited (including resampling) asynchronously before we get
// a chance to repaint, so we have to ensure that it's all valid
// and not rotated.
//
// Desktop does not support async zoom yet, so we ignore this for those
// platforms.
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
if (mWidget && mWidget->GetOwningTabChild()) {
mCompositorMightResample = AsyncPanZoomEnabled();
}
#endif
// If we have a non-default target, we need to let our shadow manager draw
// to it. This will happen at the end of the transaction.
if (aTarget && XRE_IsParentProcess()) {
mShadowTarget = aTarget;
} else {
NS_ASSERTION(!aTarget,
"Content-process ClientLayerManager::BeginTransactionWithTarget not supported");
}
// If this is a new paint, increment the paint sequence number.
if (!mIsRepeatTransaction) {
// Increment the paint sequence number even if test logging isn't
// enabled in this process; it may be enabled in the parent process,
// and the parent process expects unique sequence numbers.
++mPaintSequenceNumber;
if (gfxPrefs::APZTestLoggingEnabled()) {
mApzTestData.StartNewPaint(mPaintSequenceNumber);
}
}
return true;
}
bool
ClientLayerManager::BeginTransaction()
{
return BeginTransactionWithTarget(nullptr);
}
bool
ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags)
{
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
js::ProfileEntry::Category::GRAPHICS);
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
Log();
#endif
profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
NS_ASSERTION(InConstruction(), "Should be in construction phase");
mPhase = PHASE_DRAWING;
ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
mTransactionIncomplete = false;
// Apply pending tree updates before recomputing effective
// properties.
GetRoot()->ApplyPendingUpdatesToSubtree();
mPaintedLayerCallback = aCallback;
mPaintedLayerCallbackData = aCallbackData;
GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
// Skip the painting if the device is in device-reset status.
if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
TimeStamp start = TimeStamp::Now();
root->RenderLayer();
mLastPaintTime = TimeStamp::Now() - start;
} else {
root->RenderLayer();
}
} else {
gfxCriticalNote << "LayerManager::EndTransaction skip RenderLayer().";
}
if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
GetRoot()->Mutated();
}
if (!mIsRepeatTransaction) {
mAnimationReadyTime = TimeStamp::Now();
GetRoot()->StartPendingAnimations(mAnimationReadyTime);
}
mPaintedLayerCallback = nullptr;
mPaintedLayerCallbackData = nullptr;
// Go back to the construction phase if the transaction isn't complete.
// Layout will update the layer tree and call EndTransaction().
mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
NS_ASSERTION(!aCallback || !mTransactionIncomplete,
"If callback is not null, transaction must be complete");
if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
FrameLayerBuilder::InvalidateAllLayers(this);
}
return !mTransactionIncomplete;
}
void
ClientLayerManager::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations)
{
if (mForwarder) {
mForwarder->StorePluginWidgetConfigurations(aConfigurations);
}
}
void
ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags)
{
if (!mForwarder->IPCOpen()) {
mInTransaction = false;
return;
}
if (mWidget) {
mWidget->PrepareWindowEffects();
}
EndTransactionInternal(aCallback, aCallbackData, aFlags);
ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
if (mRepeatTransaction) {
mRepeatTransaction = false;
mIsRepeatTransaction = true;
if (BeginTransaction()) {
ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
}
mIsRepeatTransaction = false;
} else {
MakeSnapshotIfRequired();
}
mInTransaction = false;
mTransactionStart = TimeStamp();
}
bool
ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
{
mInTransaction = false;
if (!mRoot || !mForwarder->IPCOpen()) {
return false;
}
if (!EndTransactionInternal(nullptr, nullptr, aFlags)) {
// Return without calling ForwardTransaction. This leaves the
// ShadowLayerForwarder transaction open; the following
// EndTransaction will complete it.
return false;
}
if (mWidget) {
mWidget->PrepareWindowEffects();
}
ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
MakeSnapshotIfRequired();
return true;
}
CompositorBridgeChild *
ClientLayerManager::GetRemoteRenderer()
{
if (!mWidget) {
return nullptr;
}
return mWidget->GetRemoteRenderer();
}
CompositorBridgeChild*
ClientLayerManager::GetCompositorBridgeChild()
{
if (!XRE_IsParentProcess()) {
return CompositorBridgeChild::Get();
}
return GetRemoteRenderer();
}
void
ClientLayerManager::Composite()
{
mForwarder->Composite();
}
void
ClientLayerManager::DidComposite(uint64_t aTransactionId,
const TimeStamp& aCompositeStart,
const TimeStamp& aCompositeEnd)
{
MOZ_ASSERT(mWidget);
// |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
// layers transaction.
if (aTransactionId) {
nsIWidgetListener *listener = mWidget->GetWidgetListener();
if (listener) {
listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
listener = mWidget->GetAttachedWidgetListener();
if (listener) {
listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
}
// These observers fire whether or not we were in a transaction.
for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
mDidCompositeObservers[i]->DidComposite();
}
}
void
ClientLayerManager::GetCompositorSideAPZTestData(APZTestData* aData) const
{
if (mForwarder->HasShadowManager()) {
if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) {
NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
}
}
}
float
ClientLayerManager::RequestProperty(const nsAString& aProperty)
{
if (mForwarder->HasShadowManager()) {
float value;
if (!mForwarder->GetShadowManager()->SendRequestProperty(nsString(aProperty), &value)) {
NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
}
return value;
}
return -1;
}
void
ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber)
{
if (gfxPrefs::APZTestLoggingEnabled()) {
mApzTestData.StartNewRepaintRequest(aSequenceNumber);
}
}
void
ClientLayerManager::GetFrameUniformity(FrameUniformityData* aOutData)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Frame Uniformity only supported in parent process");
if (HasShadowManager()) {
CompositorBridgeChild* child = GetRemoteRenderer();
child->SendGetFrameUniformity(aOutData);
return;
}
return LayerManager::GetFrameUniformity(aOutData);
}
bool
ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback)
{
MOZ_ASSERT(aCallback != nullptr);
MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now");
if (HasShadowManager()) {
CompositorBridgeChild* child = GetRemoteRenderer();
NS_ASSERTION(child, "Could not get CompositorBridgeChild");
child->AddOverfillObserver(this);
child->SendRequestOverfill();
mOverfillCallbacks.AppendElement(aCallback);
}
return true;
}
void
ClientLayerManager::RunOverfillCallback(const uint32_t aOverfill)
{
for (size_t i = 0; i < mOverfillCallbacks.Length(); i++) {
ErrorResult error;
mOverfillCallbacks[i]->Call(aOverfill, error);
}
mOverfillCallbacks.Clear();
}
void
ClientLayerManager::MakeSnapshotIfRequired()
{
if (!mShadowTarget) {
return;
}
if (mWidget) {
if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
// The compositor doesn't draw to a different sized surface
// when there's a rotation. Instead we rotate the result
// when drawing into dt
LayoutDeviceIntRect outerBounds = mWidget->GetBounds();
IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents());
if (mTargetRotation) {
bounds =
RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation);
}
SurfaceDescriptor inSnapshot;
if (!bounds.IsEmpty() &&
mForwarder->AllocSurfaceDescriptor(bounds.Size(),
gfxContentType::COLOR_ALPHA,
&inSnapshot)) {
// Make a copy of |inSnapshot| because the call to send it over IPC
// will call forget() on the Shmem inside, and zero it out.
SurfaceDescriptor outSnapshot = inSnapshot;
if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot);
DrawTarget* dt = mShadowTarget->GetDrawTarget();
Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
Rect srcRect(0, 0, bounds.width, bounds.height);
gfx::Matrix rotate =
ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
mTargetRotation);
gfx::Matrix oldMatrix = dt->GetTransform();
dt->SetTransform(rotate * oldMatrix);
dt->DrawSurface(surf, dstRect, srcRect,
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_OVER));
dt->SetTransform(oldMatrix);
}
mForwarder->DestroySurfaceDescriptor(&outSnapshot);
}
}
}
mShadowTarget = nullptr;
}
void
ClientLayerManager::FlushRendering()
{
if (mWidget) {
if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
remoteRenderer->SendFlushRendering();
}
}
}
void
ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
{
mForwarder->IdentifyTextureHost(aNewIdentifier);
}
void
ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
{
if (mWidget) {
if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
remoteRenderer->SendNotifyRegionInvalidated(aRegion);
}
}
}
uint32_t
ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
{
CompositorBridgeChild* renderer = GetRemoteRenderer();
if (renderer) {
uint32_t startIndex;
renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
return startIndex;
}
return -1;
}
void
ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
nsTArray<float>& aFrameIntervals)
{
CompositorBridgeChild* renderer = GetRemoteRenderer();
if (renderer) {
renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
}
}
void
ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
{
TimeStamp start = TimeStamp::Now();
// Skip the synchronization for buffer since we also skip the painting during
// device-reset status.
if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
if (mForwarder->GetSyncObject() &&
mForwarder->GetSyncObject()->IsSyncObjectValid()) {
mForwarder->GetSyncObject()->FinalizeFrame();
}
}
mPhase = PHASE_FORWARD;
mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
TimeStamp transactionStart;
if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
transactionStart = mTransactionIdAllocator->GetTransactionStart();
} else {
transactionStart = mTransactionStart;
}
if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
}
// forward this transaction's changeset to our LayerManagerComposite
bool sent;
AutoTArray<EditReply, 10> replies;
if (mForwarder->EndTransaction(&replies, mRegionToClear,
mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
mIsRepeatTransaction, transactionStart, &sent)) {
for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
const EditReply& reply = replies[i];
switch (reply.type()) {
case EditReply::TOpContentBufferSwap: {
MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
RefPtr<CompositableClient> compositable =
CompositableClient::FromIPDLActor(obs.compositableChild());
ContentClientRemote* contentClient =
static_cast<ContentClientRemote*>(compositable.get());
MOZ_ASSERT(contentClient);
contentClient->SwapBuffers(obs.frontUpdatedRegion());
break;
}
default:
NS_RUNTIMEABORT("not reached");
}
}
if (sent) {
mNeedsComposite = false;
}
} else if (HasShadowManager()) {
NS_WARNING("failed to forward Layers transaction");
}
if (!sent) {
// Clear the transaction id so that it doesn't get returned
// unless we forwarded to somewhere that doesn't actually
// have a compositor.
mTransactionIdAllocator->RevokeTransactionId(mLatestTransactionId);
}
mPhase = PHASE_NONE;
// this may result in Layers being deleted, which results in
// PLayer::Send__delete__() and DeallocShmem()
mKeepAlive.Clear();
TabChild* window = mWidget ? mWidget->GetOwningTabChild() : nullptr;
if (window) {
TimeStamp end = TimeStamp::Now();
window->DidRequestComposite(start, end);
}
}
ShadowableLayer*
ClientLayerManager::Hold(Layer* aLayer)
{
MOZ_ASSERT(HasShadowManager(),
"top-level tree, no shadow tree to remote to");
ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer);
MOZ_ASSERT(shadowable, "trying to remote an unshadowable layer");
mKeepAlive.AppendElement(aLayer);
return shadowable;
}
bool
ClientLayerManager::IsCompositingCheap()
{
// Whether compositing is cheap depends on the parent backend.
return mForwarder->mShadowManager &&
LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
}
bool
ClientLayerManager::AreComponentAlphaLayersEnabled()
{
return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
AsShadowForwarder()->SupportsComponentAlpha() &&
LayerManager::AreComponentAlphaLayersEnabled();
}
void
ClientLayerManager::SetIsFirstPaint()
{
mForwarder->SetIsFirstPaint();
}
void
ClientLayerManager::ClearCachedResources(Layer* aSubtree)
{
if (mDestroyed) {
// ClearCachedResource was already called by ClientLayerManager::Destroy
return;
}
MOZ_ASSERT(!HasShadowManager() || !aSubtree);
mForwarder->ClearCachedResources();
if (aSubtree) {
ClearLayer(aSubtree);
} else if (mRoot) {
ClearLayer(mRoot);
}
}
void
ClientLayerManager::HandleMemoryPressure()
{
if (mRoot) {
HandleMemoryPressureLayer(mRoot);
}
if (GetCompositorBridgeChild()) {
GetCompositorBridgeChild()->HandleMemoryPressure();
}
}
void
ClientLayerManager::ClearLayer(Layer* aLayer)
{
ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ClearLayer(child);
}
}
void
ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
{
ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
HandleMemoryPressureLayer(child);
}
}
void
ClientLayerManager::GetBackendName(nsAString& aName)
{
switch (mForwarder->GetCompositorBackendType()) {
case LayersBackend::LAYERS_NONE: aName.AssignLiteral("None"); return;
case LayersBackend::LAYERS_BASIC: aName.AssignLiteral("Basic"); return;
case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
case LayersBackend::LAYERS_D3D11: {
#ifdef XP_WIN
if (DeviceManagerDx::Get()->IsWARP()) {
aName.AssignLiteral("Direct3D 11 WARP");
} else {
aName.AssignLiteral("Direct3D 11");
}
#endif
return;
}
default: NS_RUNTIMEABORT("Invalid backend");
}
}
bool
ClientLayerManager::AsyncPanZoomEnabled() const
{
return mWidget && mWidget->AsyncPanZoomEnabled();
}
void
ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
{
mForwarder->SetPaintSyncId(aSyncId);
}
void
ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
{
mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
}
void
ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
{
if (!mDidCompositeObservers.Contains(aObserver)) {
mDidCompositeObservers.AppendElement(aObserver);
}
}
void
ClientLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
{
mDidCompositeObservers.RemoveElement(aObserver);
}
bool
ClientLayerManager::DependsOnStaleDevice() const
{
return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
}
already_AddRefed<PersistentBufferProvider>
ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat)
{
// Don't use a shared buffer provider if compositing is considered "not cheap"
// because the canvas will most likely be flattened into a thebes layer instead
// of being sent to the compositor, in which case rendering into shared memory
// is wasteful.
if (IsCompositingCheap() &&
gfxPrefs::PersistentBufferProviderSharedEnabled()) {
RefPtr<PersistentBufferProvider> provider
= PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
if (provider) {
return provider.forget();
}
}
return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
}
ClientLayer::~ClientLayer()
{
if (HasShadow()) {
PLayerChild::Send__delete__(GetShadow());
}
MOZ_COUNT_DTOR(ClientLayer);
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,422 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef GFX_CLIENTLAYERMANAGER_H
#define GFX_CLIENTLAYERMANAGER_H
#include <stdint.h> // for int32_t
#include "Layers.h"
#include "gfxContext.h" // for gfxContext
#include "mozilla/Attributes.h" // for override
#include "mozilla/LinkedList.h" // For LinkedList
#include "mozilla/WidgetUtils.h" // for ScreenRotation
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
#include "mozilla/layers/APZTestData.h" // for APZTestData
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsIObserver.h" // for nsIObserver
#include "nsISupportsImpl.h" // for Layer::Release, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsTArray.h" // for nsTArray
#include "nscore.h" // for nsAString
#include "mozilla/layers/TransactionIdAllocator.h"
#include "nsIWidget.h" // For plugin window configuration information structs
namespace mozilla {
namespace layers {
class ClientPaintedLayer;
class CompositorBridgeChild;
class ImageLayer;
class PLayerChild;
class FrameUniformityData;
class ClientLayerManager final : public LayerManager
{
typedef nsTArray<RefPtr<Layer> > LayerRefArray;
public:
explicit ClientLayerManager(nsIWidget* aWidget);
virtual void Destroy() override;
protected:
virtual ~ClientLayerManager();
public:
virtual ShadowLayerForwarder* AsShadowForwarder() override
{
return mForwarder;
}
virtual ClientLayerManager* AsClientLayerManager() override
{
return this;
}
virtual int32_t GetMaxTextureSize() const override;
virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
virtual bool BeginTransaction() override;
virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags = END_DEFAULT) override;
virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_CLIENT; }
virtual LayersBackend GetCompositorBackendType() override
{
return AsShadowForwarder()->GetCompositorBackendType();
}
virtual void GetBackendName(nsAString& name) override;
virtual const char* Name() const override { return "Client"; }
virtual void SetRoot(Layer* aLayer) override;
virtual void Mutated(Layer* aLayer) override;
virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override;
virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
virtual already_AddRefed<RefLayer> CreateRefLayer() override;
void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier);
TextureFactoryIdentifier GetTextureFactoryIdentifier()
{
return AsShadowForwarder()->GetTextureFactoryIdentifier();
}
virtual void FlushRendering() override;
void SendInvalidRegion(const nsIntRegion& aRegion);
virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
virtual void StopFrameTimeRecording(uint32_t aStartIndex,
nsTArray<float>& aFrameIntervals) override;
virtual bool NeedsWidgetInvalidation() override { return false; }
ShadowableLayer* Hold(Layer* aLayer);
bool HasShadowManager() const { return mForwarder->HasShadowManager(); }
virtual bool IsCompositingCheap() override;
virtual bool HasShadowManagerInternal() const override { return HasShadowManager(); }
virtual void SetIsFirstPaint() override;
/**
* Pass through call to the forwarder for nsPresContext's
* CollectPluginGeometryUpdates. Passes widget configuration information
* to the compositor for transmission to the chrome process. This
* configuration gets set when the window paints.
*/
void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
aConfigurations) override;
// Drop cached resources and ask our shadow manager to do the same,
// if we have one.
virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
void HandleMemoryPressure();
void SetRepeatTransaction() { mRepeatTransaction = true; }
bool GetRepeatTransaction() { return mRepeatTransaction; }
bool IsRepeatTransaction() { return mIsRepeatTransaction; }
void SetTransactionIncomplete() { mTransactionIncomplete = true; }
bool HasShadowTarget() { return !!mShadowTarget; }
void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
bool CompositorMightResample() { return mCompositorMightResample; }
DrawPaintedLayerCallback GetPaintedLayerCallback() const
{ return mPaintedLayerCallback; }
void* GetPaintedLayerCallbackData() const
{ return mPaintedLayerCallbackData; }
CompositorBridgeChild* GetRemoteRenderer();
CompositorBridgeChild* GetCompositorBridgeChild();
// Disable component alpha layers with the software compositor.
virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
#ifdef DEBUG
bool InDrawing() { return mPhase == PHASE_DRAWING; }
bool InForward() { return mPhase == PHASE_FORWARD; }
#endif
bool InTransaction() { return mPhase != PHASE_NONE; }
void SetNeedsComposite(bool aNeedsComposite)
{
mNeedsComposite = aNeedsComposite;
}
bool NeedsComposite() const { return mNeedsComposite; }
virtual void Composite() override;
virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
virtual void RunOverfillCallback(const uint32_t aOverfill) override;
void DidComposite(uint64_t aTransactionId,
const mozilla::TimeStamp& aCompositeStart,
const mozilla::TimeStamp& aCompositeEnd);
virtual bool AreComponentAlphaLayersEnabled() override;
// Log APZ test data for the current paint. We supply the paint sequence
// number ourselves, and take care of calling APZTestData::StartNewPaint()
// when a new paint is started.
void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
const std::string& aKey,
const std::string& aValue)
{
mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
}
// Log APZ test data for a repaint request. The sequence number must be
// passed in from outside, and APZTestData::StartNewRepaintRequest() needs
// to be called from the outside as well when a new repaint request is started.
void StartNewRepaintRequest(SequenceNumber aSequenceNumber);
// TODO(botond): When we start using this and write a wrapper similar to
// nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks
// gfxPrefs::APZTestLoggingEnabled().
void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
FrameMetrics::ViewID aScrollId,
const std::string& aKey,
const std::string& aValue)
{
mApzTestData.LogTestDataForRepaintRequest(aSequenceNumber, aScrollId, aKey, aValue);
}
// Get the content-side APZ test data for reading. For writing, use the
// LogTestData...() functions.
const APZTestData& GetAPZTestData() const {
return mApzTestData;
}
// Get a copy of the compositor-side APZ test data for our layers ID.
void GetCompositorSideAPZTestData(APZTestData* aData) const;
void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
float RequestProperty(const nsAString& aProperty) override;
bool AsyncPanZoomEnabled() const override;
void SetNextPaintSyncId(int32_t aSyncId);
void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
class DidCompositeObserver {
public:
virtual void DidComposite() = 0;
};
void AddDidCompositeObserver(DidCompositeObserver* aObserver);
void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
virtual already_AddRefed<PersistentBufferProvider>
CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
protected:
enum TransactionPhase {
PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
};
TransactionPhase mPhase;
private:
// Listen memory-pressure event for ClientLayerManager
class MemoryPressureObserver final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit MemoryPressureObserver(ClientLayerManager* aClientLayerManager)
: mClientLayerManager(aClientLayerManager)
{
RegisterMemoryPressureEvent();
}
void Destroy();
private:
virtual ~MemoryPressureObserver() {}
void RegisterMemoryPressureEvent();
void UnregisterMemoryPressureEvent();
ClientLayerManager* mClientLayerManager;
};
/**
* Forward transaction results to the parent context.
*/
void ForwardTransaction(bool aScheduleComposite);
/**
* Take a snapshot of the parent context, and copy
* it into mShadowTarget.
*/
void MakeSnapshotIfRequired();
void ClearLayer(Layer* aLayer);
void HandleMemoryPressureLayer(Layer* aLayer);
bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags);
bool DependsOnStaleDevice() const;
LayerRefArray mKeepAlive;
nsIWidget* mWidget;
/* PaintedLayer callbacks; valid at the end of a transaciton,
* while rendering */
DrawPaintedLayerCallback mPaintedLayerCallback;
void *mPaintedLayerCallbackData;
// When we're doing a transaction in order to draw to a non-default
// target, the layers transaction is only performed in order to send
// a PLayers:Update. We save the original non-default target to
// mShadowTarget, and then perform the transaction using
// mDummyTarget as the render target. After the transaction ends,
// we send a message to our remote side to capture the actual pixels
// being drawn to the default target, and then copy those pixels
// back to mShadowTarget.
RefPtr<gfxContext> mShadowTarget;
RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
uint64_t mLatestTransactionId;
TimeDuration mLastPaintTime;
// Sometimes we draw to targets that don't natively support
// landscape/portrait orientation. When we need to implement that
// ourselves, |mTargetRotation| describes the induced transform we
// need to apply when compositing content to our target.
ScreenRotation mTargetRotation;
// Used to repeat the transaction right away (to avoid rebuilding
// a display list) to support progressive drawing.
bool mRepeatTransaction;
bool mIsRepeatTransaction;
bool mTransactionIncomplete;
bool mCompositorMightResample;
bool mNeedsComposite;
// An incrementing sequence number for paints.
// Incremented in BeginTransaction(), but not for repeat transactions.
uint32_t mPaintSequenceNumber;
APZTestData mApzTestData;
RefPtr<ShadowLayerForwarder> mForwarder;
AutoTArray<dom::OverfillCallback*,0> mOverfillCallbacks;
mozilla::TimeStamp mTransactionStart;
nsTArray<DidCompositeObserver*> mDidCompositeObservers;
RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
uint64_t mDeviceCounter;
};
class ClientLayer : public ShadowableLayer
{
public:
ClientLayer()
{
MOZ_COUNT_CTOR(ClientLayer);
}
~ClientLayer();
void SetShadow(PLayerChild* aShadow)
{
MOZ_ASSERT(!mShadow, "can't have two shadows (yet)");
mShadow = aShadow;
}
virtual void Disconnect()
{
// This is an "emergency Disconnect()", called when the compositing
// process has died. |mShadow| and our Shmem buffers are
// automatically managed by IPDL, so we don't need to explicitly
// free them here (it's hard to get that right on emergency
// shutdown anyway).
mShadow = nullptr;
}
virtual void ClearCachedResources() { }
// Shrink memory usage.
// Called when "memory-pressure" is observed.
virtual void HandleMemoryPressure() { }
virtual void RenderLayer() = 0;
virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
virtual ClientPaintedLayer* AsThebes() { return nullptr; }
static inline ClientLayer *
ToClientLayer(Layer* aLayer)
{
return static_cast<ClientLayer*>(aLayer->ImplData());
}
template <typename LayerType>
static inline void RenderMaskLayers(LayerType* aLayer) {
if (aLayer->GetMaskLayer()) {
ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
}
for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
}
}
};
// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
// our layer tree to a parent process. Record the new layer creation
// in the current open transaction as a side effect.
template<typename CreatedMethod> void
CreateShadowFor(ClientLayer* aLayer,
ClientLayerManager* aMgr,
CreatedMethod aMethod)
{
PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
if (!shadow) {
return;
}
aLayer->SetShadow(shadow);
(aMgr->AsShadowForwarder()->*aMethod)(aLayer);
aMgr->Hold(aLayer->AsLayer());
}
#define CREATE_SHADOW(_type) \
CreateShadowFor(layer, this, \
&ShadowLayerForwarder::Created ## _type ## Layer)
} // namespace layers
} // namespace mozilla
#endif /* GFX_CLIENTLAYERMANAGER_H */

View file

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 2; 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 "ClientPaintedLayer.h"
#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
#include <stdint.h> // for uint32_t
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
#include "gfxContext.h" // for gfxContext
#include "gfxRect.h" // for gfxRect
#include "gfxPrefs.h" // for gfxPrefs
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/gfx/2D.h" // for DrawTarget
#include "mozilla/gfx/Matrix.h" // for Matrix
#include "mozilla/gfx/Rect.h" // for Rect, IntRect
#include "mozilla/gfx/Types.h" // for Float, etc
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/Preferences.h"
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "gfx2DGlue.h"
#include "ReadbackProcessor.h"
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
void
ClientPaintedLayer::PaintThebes()
{
PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
js::ProfileEntry::Category::GRAPHICS);
NS_ASSERTION(ClientManager()->InDrawing(),
"Can only draw in drawing phase");
uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
if (ClientManager()->CompositorMightResample()) {
flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
}
if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
if (MayResample()) {
flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
}
}
#endif
PaintState state =
mContentClient->BeginPaintBuffer(this, flags);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
ClientManager()->SetTransactionIncomplete();
return;
}
// The area that became invalid and is visible needs to be repainted
// (this could be the whole visible area if our buffer switched
// from RGB to RGBA, because we might need to repaint with
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate,
GetLocalVisibleRegion().ToUnknownRegion());
bool didUpdate = false;
RotatedContentBuffer::DrawIterator iter;
while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
if (!target || !target->IsValid()) {
if (target) {
mContentClient->ReturnDrawTargetToBuffer(target);
}
continue;
}
SetAntialiasingFlags(this, target);
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
MOZ_ASSERT(ctx); // already checked the target above
ClientManager()->GetPaintedLayerCallback()(this,
ctx,
iter.mDrawRegion,
iter.mDrawRegion,
state.mClip,
state.mRegionToInvalidate,
ClientManager()->GetPaintedLayerCallbackData());
ctx = nullptr;
mContentClient->ReturnDrawTargetToBuffer(target);
didUpdate = true;
}
if (didUpdate) {
Mutated();
mValidRegion.Or(mValidRegion, state.mRegionToDraw);
ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
MOZ_ASSERT(contentClientRemote->GetIPDLActor());
// Hold(this) ensures this layer is kept alive through the current transaction
// The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
// so deleting this Hold for whatever reason will break things.
ClientManager()->Hold(this);
contentClientRemote->Updated(state.mRegionToDraw,
mVisibleRegion.ToUnknownRegion(),
state.mDidSelfCopy);
}
}
void
ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
{
RenderMaskLayers(this);
if (!mContentClient) {
mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
if (!mContentClient) {
return;
}
mContentClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
MOZ_ASSERT(mContentClient->GetForwarder());
}
nsTArray<ReadbackProcessor::Update> readbackUpdates;
nsIntRegion readbackRegion;
if (aReadback && UsedForReadback()) {
aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
}
IntPoint origin(mVisibleRegion.GetBounds().x, mVisibleRegion.GetBounds().y);
mContentClient->BeginPaint();
PaintThebes();
mContentClient->EndPaint(&readbackUpdates);
}
already_AddRefed<PaintedLayer>
ClientLayerManager::CreatePaintedLayer()
{
return CreatePaintedLayerWithHint(NONE);
}
already_AddRefed<PaintedLayer>
ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
if (gfxPrefs::LayersTilesEnabled()) {
RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
CREATE_SHADOW(Painted);
return layer.forget();
} else {
RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
CREATE_SHADOW(Painted);
return layer.forget();
}
}
void
ClientPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
PaintedLayer::PrintInfo(aStream, aPrefix);
if (mContentClient) {
aStream << "\n";
nsAutoCString pfx(aPrefix);
pfx += " ";
mContentClient->PrintInfo(aStream, pfx.get());
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef GFX_CLIENTPAINTEDLAYER_H
#define GFX_CLIENTPAINTEDLAYER_H
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "Layers.h" // for PaintedLayer, etc
#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/ContentClient.h" // for ContentClient
#include "mozilla/mozalloc.h" // for operator delete
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsRegion.h" // for nsIntRegion
#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
namespace mozilla {
namespace layers {
class CompositableClient;
class ShadowableLayer;
class SpecificLayerAttributes;
class ClientPaintedLayer : public PaintedLayer,
public ClientLayer {
public:
typedef RotatedContentBuffer::PaintState PaintState;
typedef RotatedContentBuffer::ContentType ContentType;
explicit ClientPaintedLayer(ClientLayerManager* aLayerManager,
LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) :
PaintedLayer(aLayerManager, static_cast<ClientLayer*>(this), aCreationHint),
mContentClient(nullptr)
{
MOZ_COUNT_CTOR(ClientPaintedLayer);
}
protected:
virtual ~ClientPaintedLayer()
{
if (mContentClient) {
mContentClient->OnDetach();
mContentClient = nullptr;
}
MOZ_COUNT_DTOR(ClientPaintedLayer);
}
public:
virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
PaintedLayer::SetVisibleRegion(aRegion);
}
virtual void InvalidateRegion(const nsIntRegion& aRegion) override
{
NS_ASSERTION(ClientManager()->InConstruction(),
"Can only set properties in construction phase");
mInvalidRegion.Add(aRegion);
mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
}
virtual void RenderLayer() override { RenderLayerWithReadback(nullptr); }
virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) override;
virtual void ClearCachedResources() override
{
if (mContentClient) {
mContentClient->Clear();
}
mValidRegion.SetEmpty();
DestroyBackBuffer();
}
virtual void HandleMemoryPressure() override
{
if (mContentClient) {
mContentClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = PaintedLayerAttributes(GetValidRegion());
}
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
virtual Layer* AsLayer() override { return this; }
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual CompositableClient* GetCompositableClient() override
{
return mContentClient;
}
virtual void Disconnect() override
{
mContentClient = nullptr;
ClientLayer::Disconnect();
}
protected:
void PaintThebes();
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
void DestroyBackBuffer()
{
mContentClient = nullptr;
}
RefPtr<ContentClient> mContentClient;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,34 @@
/* -*- 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/. */
#ifndef GFX_CLIENTREADBACKLAYER_H
#define GFX_CLIENTREADBACKLAYER_H
#include "ClientLayerManager.h"
#include "ReadbackLayer.h"
namespace mozilla {
namespace layers {
class ClientReadbackLayer :
public ReadbackLayer,
public ClientLayer
{
public:
explicit ClientReadbackLayer(ClientLayerManager *aManager)
: ReadbackLayer(aManager, nullptr)
{
mImplData = static_cast<ClientLayer*>(this);
}
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual Layer* AsLayer() override { return this; }
virtual void RenderLayer() override {}
};
} // namespace layers
} // namespace mozilla
#endif /* GFX_CLIENTREADBACKLAYER_H */

View file

@ -0,0 +1,615 @@
/* 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 "ClientTiledPaintedLayer.h"
#include "FrameMetrics.h" // for FrameMetrics
#include "Units.h" // for ScreenIntRect, CSSPoint, etc
#include "UnitTransforms.h" // for TransformTo
#include "ClientLayerManager.h" // for ClientLayerManager, etc
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxPrefs.h" // for gfxPrefs
#include "gfxRect.h" // for gfxRect
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/gfx/BaseSize.h" // for BaseSize
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Rect.h" // for Rect, RectTyped
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
#include "mozilla/layers/LayersMessages.h"
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "LayersLogging.h"
#include "mozilla/layers/SingleTiledContentClient.h"
namespace mozilla {
namespace layers {
using gfx::Rect;
using gfx::IntRect;
using gfx::IntSize;
ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aManager,
ClientLayerManager::PaintedLayerCreationHint aCreationHint)
: PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint)
, mContentClient()
, mHaveSingleTiledContentClient(false)
{
MOZ_COUNT_CTOR(ClientTiledPaintedLayer);
mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
mPaintData.mFirstPaint = true;
}
ClientTiledPaintedLayer::~ClientTiledPaintedLayer()
{
MOZ_COUNT_DTOR(ClientTiledPaintedLayer);
}
void
ClientTiledPaintedLayer::ClearCachedResources()
{
if (mContentClient) {
mContentClient->ClearCachedResources();
}
mValidRegion.SetEmpty();
mContentClient = nullptr;
}
void
ClientTiledPaintedLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = PaintedLayerAttributes(GetValidRegion());
}
static Maybe<LayerRect>
ApplyParentLayerToLayerTransform(const ParentLayerToLayerMatrix4x4& aTransform,
const ParentLayerRect& aParentLayerRect,
const LayerRect& aClip)
{
return UntransformBy(aTransform, aParentLayerRect, aClip);
}
static LayerToParentLayerMatrix4x4
GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor)
{
gfx::Matrix4x4 transform;
const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent();
for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM);
ancestorParent ? iter != ancestorParent : iter.IsValid();
iter = iter.GetParent()) {
transform = transform * iter.GetTransform();
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
// When scrolling containers, layout adds a post-scale into the transform
// of the displayport-ancestor (which we pick up in GetTransform() above)
// to cancel out the pres shell resolution (for historical reasons). The
// compositor in turn cancels out this post-scale (i.e., scales by the
// pres shell resolution), and to get correct calculations, we need to do
// so here, too.
//
// With containerless scrolling, the offending post-scale is on the
// parent layer of the displayport-ancestor, which we don't reach in this
// loop, so we don't need to worry about it.
float presShellResolution = iter.GetPresShellResolution();
transform.PostScale(presShellResolution, presShellResolution, 1.0f);
}
}
return ViewAs<LayerToParentLayerMatrix4x4>(transform);
}
void
ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
LayerMetricsWrapper* aOutDisplayPortAncestor,
bool* aOutHasTransformAnimation)
{
LayerMetricsWrapper scrollAncestor;
LayerMetricsWrapper displayPortAncestor;
bool hasTransformAnimation = false;
for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
hasTransformAnimation |= ancestor.HasTransformAnimation();
const FrameMetrics& metrics = ancestor.Metrics();
if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
scrollAncestor = ancestor;
}
if (!metrics.GetDisplayPort().IsEmpty()) {
displayPortAncestor = ancestor;
// Any layer that has a displayport must be scrollable, so we can break
// here.
break;
}
}
if (aOutScrollAncestor) {
*aOutScrollAncestor = scrollAncestor;
}
if (aOutDisplayPortAncestor) {
*aOutDisplayPortAncestor = displayPortAncestor;
}
if (aOutHasTransformAnimation) {
*aOutHasTransformAnimation = hasTransformAnimation;
}
}
void
ClientTiledPaintedLayer::BeginPaint()
{
mPaintData.ResetPaintData();
if (!GetBaseTransform().Is2D()) {
// Give up if there is a complex CSS transform on the layer. We might
// eventually support these but for now it's too complicated to handle
// given that it's a pretty rare scenario.
return;
}
// Get the metrics of the nearest scrollable layer and the nearest layer
// with a displayport.
LayerMetricsWrapper scrollAncestor;
LayerMetricsWrapper displayPortAncestor;
bool hasTransformAnimation;
GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation);
if (!displayPortAncestor || !scrollAncestor) {
// No displayport or scroll ancestor, so we can't do progressive rendering.
#if defined(MOZ_WIDGET_ANDROID)
// Both Android and b2g on phones are guaranteed to have a displayport set, so this
// should never happen.
NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor");
#endif
return;
}
TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this,
scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation);
const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();
// Calculate the transform required to convert ParentLayer space of our
// display port ancestor to the Layer space of this layer.
ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer =
GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse();
LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));
// Compute the critical display port that applies to this layer in the
// LayoutDevice space of this layer, but only if there is no OMT animation
// on this layer. If there is an OMT animation then we need to draw the whole
// visible region of this layer as determined by layout, because we don't know
// what parts of it might move into view in the compositor.
mPaintData.mHasTransformAnimation = hasTransformAnimation;
if (!mPaintData.mHasTransformAnimation &&
mContentClient->GetLowPrecisionTiledBuffer()) {
ParentLayerRect criticalDisplayPort =
(displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
+ displayportMetrics.GetCompositionBounds().TopLeft();
Maybe<LayerRect> criticalDisplayPortTransformed =
ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds);
if (criticalDisplayPortTransformed) {
mPaintData.mCriticalDisplayPort = Some(RoundedToInt(*criticalDisplayPortTransformed));
} else {
mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0));
}
}
TILING_LOG("TILING %p: Critical displayport %s\n", this,
mPaintData.mCriticalDisplayPort ?
Stringify(*mPaintData.mCriticalDisplayPort).c_str() : "not set");
// Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
// before any async transforms have occurred, we can use the zoom for this.
mPaintData.mResolution = displayportMetrics.GetZoom();
TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str());
// Store the applicable composition bounds in this layer's Layer units.
mPaintData.mTransformToCompBounds =
GetTransformToAncestorsParentLayer(this, scrollAncestor);
ParentLayerToLayerMatrix4x4 transformToBounds = mPaintData.mTransformToCompBounds.Inverse();
Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform(
transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds);
if (compositionBoundsTransformed) {
mPaintData.mCompositionBounds = *compositionBoundsTransformed;
} else {
mPaintData.mCompositionBounds.SetEmpty();
}
TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());
// Calculate the scroll offset since the last transaction
mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
}
bool
ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics)
{
CompositorBridgeChild* compositor = nullptr;
if (Manager() && Manager()->AsClientLayerManager()) {
compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
}
if (!compositor) {
return false;
}
FrameMetrics compositorMetrics;
if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(),
compositorMetrics)) {
return false;
}
// 1 is a tad high for a fuzzy equals epsilon however if our scroll delta
// is so small then we have nothing to gain from using paint heuristics.
float COORDINATE_EPSILON = 1.f;
return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x,
aParentMetrics.GetScrollOffset().x,
COORDINATE_EPSILON) ||
!FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y,
aParentMetrics.GetScrollOffset().y,
COORDINATE_EPSILON);
}
bool
ClientTiledPaintedLayer::UseProgressiveDraw() {
if (!gfxPrefs::ProgressivePaint()) {
// pref is disabled, so never do progressive
return false;
}
if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) {
return false;
}
if (ClientManager()->HasShadowTarget()) {
// This condition is true when we are in a reftest scenario. We don't want
// to draw progressively here because it can cause intermittent reftest
// failures because the harness won't wait for all the tiles to be drawn.
return false;
}
if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
// This layer is fixed-position and so even if it does have a scrolling
// ancestor it will likely be entirely on-screen all the time, so we
// should draw it all at once
return false;
}
if (mPaintData.mHasTransformAnimation) {
// The compositor is going to animate this somehow, so we want it all
// on the screen at once.
return false;
}
if (ClientManager()->AsyncPanZoomEnabled()) {
LayerMetricsWrapper scrollAncestor;
GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set
if (!scrollAncestor) {
return false;
}
const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
if (!IsScrollingOnCompositor(parentMetrics)) {
return false;
}
}
return true;
}
bool
ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
const nsIntRegion& aVisibleRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData)
{
// If we have started drawing low-precision already, then we
// shouldn't do anything there.
if (mPaintData.mLowPrecisionPaintCount != 0) {
return false;
}
// Only draw progressively when there is something to paint and the
// resolution is unchanged
if (!aInvalidRegion.IsEmpty() &&
UseProgressiveDraw() &&
mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) {
// Store the old valid region, then clear it before painting.
// We clip the old valid region to the visible region, as it only gets
// used to decide stale content (currently valid and previously visible)
nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion();
oldValidRegion.And(oldValidRegion, aVisibleRegion);
if (mPaintData.mCriticalDisplayPort) {
oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
}
TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());
return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion,
oldValidRegion, &mPaintData, aCallback, aCallbackData);
}
// Otherwise do a non-progressive paint. We must do this even when
// the region to paint is empty as the valid region may have shrunk.
mValidRegion = aVisibleRegion;
if (mPaintData.mCriticalDisplayPort) {
mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
}
TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());
mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion,
aCallback, aCallbackData);
mPaintData.mPaintFinished = true;
return true;
}
bool
ClientTiledPaintedLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
const nsIntRegion& aVisibleRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData)
{
// Render the low precision buffer, if the visible region is larger than the
// critical display port.
if (!mPaintData.mCriticalDisplayPort ||
!nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()).Contains(aVisibleRegion)) {
nsIntRegion oldValidRegion = mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion();
oldValidRegion.And(oldValidRegion, aVisibleRegion);
bool updatedBuffer = false;
// If the frame resolution or format have changed, invalidate the buffer
if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != mPaintData.mResolution ||
mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) {
if (!mLowPrecisionValidRegion.IsEmpty()) {
updatedBuffer = true;
}
oldValidRegion.SetEmpty();
mLowPrecisionValidRegion.SetEmpty();
mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
aInvalidRegion = aVisibleRegion;
}
// Invalidate previously valid content that is no longer visible
if (mPaintData.mLowPrecisionPaintCount == 1) {
mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion);
}
mPaintData.mLowPrecisionPaintCount++;
// Remove the valid high-precision region from the invalid low-precision
// region. We don't want to spend time drawing things twice.
aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str());
TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str());
if (!aInvalidRegion.IsEmpty()) {
updatedBuffer = mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate(
mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
&mPaintData, aCallback, aCallbackData);
}
TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
return updatedBuffer;
}
if (!mLowPrecisionValidRegion.IsEmpty()) {
TILING_LOG("TILING %p: Clearing low-precision buffer\n", this);
// Clear the low precision tiled buffer.
mLowPrecisionValidRegion.SetEmpty();
mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
// Return true here so we send a Painted callback after clearing the valid
// region of the low precision buffer. This allows the shadow buffer's valid
// region to be updated and the associated resources to be freed.
return true;
}
return false;
}
void
ClientTiledPaintedLayer::EndPaint()
{
mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
mPaintData.mPaintFinished = true;
mPaintData.mFirstPaint = false;
TILING_LOG("TILING %p: Paint finished\n", this);
}
void
ClientTiledPaintedLayer::RenderLayer()
{
LayerManager::DrawPaintedLayerCallback callback =
ClientManager()->GetPaintedLayerCallback();
void *data = ClientManager()->GetPaintedLayerCallbackData();
IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
IntSize tileSize = gfx::gfxVars::TileSize();
bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 ||
layerSize.height <= tileSize.height / 2;
// Use single tile when layer is not scrollable, is smaller than one
// tile, or when more than half of the tiles' pixels in either
// dimension would be wasted.
bool wantSingleTiledContentClient =
(mCreationHint == LayerManager::NONE ||
layerSize <= tileSize ||
isHalfTileWidthOrHeight) &&
SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
gfxPrefs::LayersSingleTileEnabled();
if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) {
mContentClient = nullptr;
mValidRegion.SetEmpty();
}
if (!mContentClient) {
if (wantSingleTiledContentClient) {
mContentClient = new SingleTiledContentClient(*this, ClientManager());
mHaveSingleTiledContentClient = true;
} else {
mContentClient = new MultiTiledContentClient(*this, ClientManager());
mHaveSingleTiledContentClient = false;
}
mContentClient->Connect();
ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
MOZ_ASSERT(mContentClient->GetForwarder());
}
if (mContentClient->GetTiledBuffer()->HasFormatChanged()) {
mValidRegion = nsIntRegion();
mContentClient->GetTiledBuffer()->ResetPaintedAndValidState();
}
TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion();
#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
// This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
if (MayResample()) {
// If we're resampling then bilinear filtering can read up to 1 pixel
// outside of our texture coords. Make the visible region a single rect,
// and pad it out by 1 pixel (restricted to tile boundaries) so that
// we always have valid content or transparent pixels to sample from.
IntRect bounds = neededRegion.GetBounds();
IntRect wholeTiles = bounds;
wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize());
IntRect padded = bounds;
padded.Inflate(1);
padded.IntersectRect(padded, wholeTiles);
neededRegion = padded;
}
#endif
nsIntRegion invalidRegion;
invalidRegion.Sub(neededRegion, mValidRegion);
if (invalidRegion.IsEmpty()) {
EndPaint();
return;
}
if (!callback) {
ClientManager()->SetTransactionIncomplete();
return;
}
if (!ClientManager()->IsRepeatTransaction()) {
// Only paint the mask layers on the first transaction.
RenderMaskLayers(this);
// For more complex cases we need to calculate a bunch of metrics before we
// can do the paint.
BeginPaint();
if (mPaintData.mPaintFinished) {
return;
}
// Make sure that tiles that fall outside of the visible region or outside of the
// critical displayport are discarded on the first update. Also make sure that we
// only draw stuff inside the critical displayport on the first update.
mValidRegion.And(mValidRegion, neededRegion);
if (mPaintData.mCriticalDisplayPort) {
mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
}
TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str());
TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
} else {
if (mPaintData.mCriticalDisplayPort) {
invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
}
TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
}
nsIntRegion lowPrecisionInvalidRegion;
if (mContentClient->GetLowPrecisionTiledBuffer()) {
// Calculate the invalid region for the low precision buffer. Make sure
// to remove the valid high-precision area so we don't double-paint it.
lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion);
lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
}
TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str());
bool updatedHighPrecision = RenderHighPrecision(invalidRegion,
neededRegion,
callback, data);
if (updatedHighPrecision) {
ClientManager()->Hold(this);
mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER);
if (!mPaintData.mPaintFinished) {
// There is still more high-res stuff to paint, so we're not
// done yet. A subsequent transaction will take care of this.
ClientManager()->SetRepeatTransaction();
return;
}
}
// If there is nothing to draw in low-precision, then we're done.
if (lowPrecisionInvalidRegion.IsEmpty()) {
EndPaint();
return;
}
if (updatedHighPrecision) {
// If there are low precision updates, but we just did some high-precision
// updates, then mark the paint as unfinished and request a repeat transaction.
// This is so that we don't perform low-precision updates in the same transaction
// as high-precision updates.
TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this);
ClientManager()->SetRepeatTransaction();
mPaintData.mLowPrecisionPaintCount = 1;
mPaintData.mPaintFinished = false;
return;
}
bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion,
neededRegion,
callback, data);
if (updatedLowPrecision) {
ClientManager()->Hold(this);
mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
if (!mPaintData.mPaintFinished) {
// There is still more low-res stuff to paint, so we're not
// done yet. A subsequent transaction will take care of this.
ClientManager()->SetRepeatTransaction();
return;
}
}
// If we get here, we've done all the high- and low-precision
// paints we wanted to do, so we can finish the paint and chill.
EndPaint();
}
bool
ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint)
{
// The only creation hint is whether the layer is scrollable or not, and this
// is only respected on B2G and OSX, where it's used to determine whether to
// use a tiled content client or not.
// There are pretty nasty performance consequences for not using tiles on
// large, scrollable layers, so we want the layer to be recreated in this
// situation.
return aHint == GetCreationHint();
}
void
ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
PaintedLayer::PrintInfo(aStream, aPrefix);
if (mContentClient) {
aStream << "\n";
nsAutoCString pfx(aPrefix);
pfx += " ";
mContentClient->PrintInfo(aStream, pfx.get());
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,153 @@
/* 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/. */
#ifndef GFX_CLIENTTILEDPAINTEDLAYER_H
#define GFX_CLIENTTILEDPAINTEDLAYER_H
#include "ClientLayerManager.h" // for ClientLayer, etc
#include "Layers.h" // for PaintedLayer, etc
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/TiledContentClient.h"
#include "nsDebug.h" // for NS_RUNTIMEABORT
#include "nsRegion.h" // for nsIntRegion
namespace mozilla {
namespace layers {
class ShadowableLayer;
class SpecificLayerAttributes;
/**
* An implementation of PaintedLayer that ONLY supports remote
* composition that is backed by tiles. This painted layer implementation
* is better suited to mobile hardware to work around slow implementation
* of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
*
* Tiled PaintedLayers use a different protocol compared with other
* layers. A copy of the tiled buffer is made and sent to the compositing
* thread via the layers protocol. Tiles are uploaded by the buffers
* asynchonously without using IPC, that means they are not safe for cross-
* process use (bug 747811). Each tile has a TextureHost/Client pair but
* they communicate directly rather than using the Texture protocol.
*
* There is no ContentClient for tiled layers. There is a ContentHost, however.
*/
class ClientTiledPaintedLayer : public PaintedLayer,
public ClientLayer
{
typedef PaintedLayer Base;
public:
explicit ClientTiledPaintedLayer(ClientLayerManager* const aManager,
ClientLayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE);
protected:
~ClientTiledPaintedLayer();
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
public:
// Override name to distinguish it from ClientPaintedLayer in layer dumps
virtual const char* Name() const override { return "TiledPaintedLayer"; }
// PaintedLayer
virtual Layer* AsLayer() override { return this; }
virtual void InvalidateRegion(const nsIntRegion& aRegion) override {
mInvalidRegion.Add(aRegion);
nsIntRegion invalidRegion = mInvalidRegion.GetRegion();
mValidRegion.Sub(mValidRegion, invalidRegion);
mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, invalidRegion);
}
// Shadow methods
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
virtual ShadowableLayer* AsShadowableLayer() override { return this; }
virtual void Disconnect() override
{
ClientLayer::Disconnect();
}
virtual void RenderLayer() override;
virtual void ClearCachedResources() override;
virtual void HandleMemoryPressure() override
{
if (mContentClient) {
mContentClient->HandleMemoryPressure();
}
}
/**
* Helper method to find the nearest ancestor layers which
* scroll and have a displayport. The parameters are out-params
* which hold the return values; the values passed in may be null.
*/
void GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
LayerMetricsWrapper* aOutDisplayPortAncestor,
bool* aOutHasTransformAnimation);
virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) override;
private:
ClientLayerManager* ClientManager()
{
return static_cast<ClientLayerManager*>(mManager);
}
/**
* For the initial PaintThebes of a transaction, calculates all the data
* needed for that paint and any repeated transactions.
*/
void BeginPaint();
/**
* Check if the layer is being scrolled by APZ on the compositor.
*/
bool IsScrollingOnCompositor(const FrameMetrics& aParentMetrics);
/**
* Check if we should use progressive draw on this layer. We will
* disable progressive draw based on a preference or if the layer
* is not being scrolled.
*/
bool UseProgressiveDraw();
/**
* Helper function to do the high-precision paint.
* This function returns true if it updated the paint buffer.
*/
bool RenderHighPrecision(nsIntRegion& aInvalidRegion,
const nsIntRegion& aVisibleRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData);
/**
* Helper function to do the low-precision paint.
* This function returns true if it updated the paint buffer.
*/
bool RenderLowPrecision(nsIntRegion& aInvalidRegion,
const nsIntRegion& aVisibleRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData);
/**
* This causes the paint to be marked as finished, and updates any data
* necessary to persist until the next paint.
*/
void EndPaint();
RefPtr<TiledContentClient> mContentClient;
// Flag to indicate if mContentClient is a SingleTiledContentClient. This is
// only valid when mContentClient is non-null.
bool mHaveSingleTiledContentClient;
nsIntRegion mLowPrecisionValidRegion;
BasicTiledLayerPaintData mPaintData;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,119 @@
/* -*- 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 "CompositableChild.h"
#include "CompositableClient.h"
namespace mozilla {
namespace layers {
/* static */ PCompositableChild*
CompositableChild::CreateActor()
{
CompositableChild* child = new CompositableChild();
child->AddRef();
return child;
}
/* static */ void
CompositableChild::DestroyActor(PCompositableChild* aChild)
{
static_cast<CompositableChild*>(aChild)->Release();
}
CompositableChild::CompositableChild()
: mCompositableClient(nullptr),
mAsyncID(0),
mCanSend(true)
{
MOZ_COUNT_CTOR(CompositableChild);
}
CompositableChild::~CompositableChild()
{
MOZ_COUNT_DTOR(CompositableChild);
}
bool
CompositableChild::IsConnected() const
{
return mCompositableClient && mCanSend;
}
void
CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID)
{
mCompositableClient = aCompositable;
mAsyncID = aAsyncID;
}
void
CompositableChild::RevokeCompositableClient()
{
mCompositableClient = nullptr;
}
RefPtr<CompositableClient>
CompositableChild::GetCompositableClient()
{
return mCompositableClient;
}
void
CompositableChild::ActorDestroy(ActorDestroyReason)
{
MOZ_ASSERT(NS_IsMainThread());
mCanSend = false;
if (mCompositableClient) {
mCompositableClient->mCompositableChild = nullptr;
mCompositableClient = nullptr;
}
}
/* static */ PCompositableChild*
AsyncCompositableChild::CreateActor()
{
AsyncCompositableChild* child = new AsyncCompositableChild();
child->AddRef();
return child;
}
AsyncCompositableChild::AsyncCompositableChild()
: mLock("AsyncCompositableChild.mLock")
{
}
AsyncCompositableChild::~AsyncCompositableChild()
{
}
void
AsyncCompositableChild::ActorDestroy(ActorDestroyReason)
{
mCanSend = false;
// We do not revoke CompositableClient::mCompositableChild here, since that
// could race with the main thread.
RevokeCompositableClient();
}
void
AsyncCompositableChild::RevokeCompositableClient()
{
MutexAutoLock lock(mLock);
mCompositableClient = nullptr;
}
RefPtr<CompositableClient>
AsyncCompositableChild::GetCompositableClient()
{
MutexAutoLock lock(mLock);
return CompositableChild::GetCompositableClient();
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,91 @@
/* -*- 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/. */
#ifndef mozilla_gfx_layers_client_CompositableChild_h
#define mozilla_gfx_layers_client_CompositableChild_h
#include <stdint.h>
#include "IPDLActor.h"
#include "mozilla/Mutex.h"
#include "mozilla/layers/PCompositableChild.h"
namespace mozilla {
namespace layers {
class CompositableClient;
class AsyncCompositableChild;
/**
* IPDL actor used by CompositableClient to match with its corresponding
* CompositableHost on the compositor side.
*
* CompositableChild is owned by a CompositableClient.
*/
class CompositableChild : public PCompositableChild
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild)
static PCompositableChild* CreateActor();
static void DestroyActor(PCompositableChild* aChild);
void Init(CompositableClient* aCompositable, uint64_t aAsyncID);
virtual void RevokeCompositableClient();
virtual void ActorDestroy(ActorDestroyReason) override;
virtual RefPtr<CompositableClient> GetCompositableClient();
virtual AsyncCompositableChild* AsAsyncCompositableChild() {
return nullptr;
}
uint64_t GetAsyncID() const {
return mAsyncID;
}
// These should only be called on the IPDL thread.
bool IsConnected() const;
bool CanSend() const {
return mCanSend;
}
protected:
CompositableChild();
virtual ~CompositableChild();
protected:
CompositableClient* mCompositableClient;
uint64_t mAsyncID;
bool mCanSend;
};
// This CompositableChild can be used off the main thread.
class AsyncCompositableChild final : public CompositableChild
{
public:
static PCompositableChild* CreateActor();
void RevokeCompositableClient() override;
RefPtr<CompositableClient> GetCompositableClient() override;
void ActorDestroy(ActorDestroyReason) override;
AsyncCompositableChild* AsAsyncCompositableChild() override {
return this;
}
protected:
AsyncCompositableChild();
~AsyncCompositableChild() override;
private:
Mutex mLock;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_gfx_layers_client_CompositableChild_h

View file

@ -0,0 +1,274 @@
/* -*- 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 "mozilla/layers/CompositableClient.h"
#include <stdint.h> // for uint64_t, uint32_t
#include "gfxPlatform.h" // for gfxPlatform
#include "mozilla/layers/CompositableChild.h"
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
#include "mozilla/layers/TextureClientOGL.h"
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "mozilla/layers/PCompositableChild.h"
#include "mozilla/layers/TextureClientRecycleAllocator.h"
#ifdef XP_WIN
#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform
#include "mozilla/layers/TextureD3D11.h"
#include "mozilla/layers/TextureD3D9.h"
#endif
#include "gfxUtils.h"
#include "IPDLActor.h"
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
void
CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
{
MOZ_ASSERT(aActor);
mForwarder->AssertInForwarderThread();
mCompositableChild = static_cast<CompositableChild*>(aActor);
mCompositableChild->Init(this, aAsyncID);
}
/* static */ RefPtr<CompositableClient>
CompositableClient::FromIPDLActor(PCompositableChild* aActor)
{
MOZ_ASSERT(aActor);
RefPtr<CompositableClient> client = static_cast<CompositableChild*>(aActor)->GetCompositableClient();
if (!client) {
return nullptr;
}
client->mForwarder->AssertInForwarderThread();
return client;
}
CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
TextureFlags aTextureFlags)
: mForwarder(aForwarder)
, mTextureFlags(aTextureFlags)
{
MOZ_COUNT_CTOR(CompositableClient);
}
CompositableClient::~CompositableClient()
{
MOZ_COUNT_DTOR(CompositableClient);
Destroy();
}
LayersBackend
CompositableClient::GetCompositorBackendType() const
{
return mForwarder->GetCompositorBackendType();
}
PCompositableChild*
CompositableClient::GetIPDLActor() const
{
return mCompositableChild;
}
bool
CompositableClient::Connect(ImageContainer* aImageContainer)
{
MOZ_ASSERT(!mCompositableChild);
if (!GetForwarder() || GetIPDLActor()) {
return false;
}
GetForwarder()->AssertInForwarderThread();
GetForwarder()->Connect(this, aImageContainer);
return true;
}
bool
CompositableClient::IsConnected() const
{
// CanSend() is only reliable in the same thread as the IPDL channel.
mForwarder->AssertInForwarderThread();
return mCompositableChild && mCompositableChild->IsConnected();
}
void
CompositableClient::Destroy()
{
if (!mCompositableChild) {
return;
}
if (mTextureClientRecycler) {
mTextureClientRecycler->Destroy();
}
// Take away our IPDL's actor reference back to us.
mCompositableChild->RevokeCompositableClient();
// Schedule the IPDL actor to be destroyed on the forwarder's thread.
mForwarder->Destroy(mCompositableChild);
mCompositableChild = nullptr;
}
uint64_t
CompositableClient::GetAsyncID() const
{
if (mCompositableChild) {
return mCompositableChild->GetAsyncID();
}
return 0; // zero is always an invalid async ID
}
already_AddRefed<TextureClient>
CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
gfx::BackendType aMoz2DBackend,
TextureFlags aTextureFlags)
{
return TextureClient::CreateForRawBufferAccess(GetForwarder(),
aFormat, aSize, aMoz2DBackend,
aTextureFlags | mTextureFlags);
}
already_AddRefed<TextureClient>
CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags)
{
return TextureClient::CreateForDrawing(GetForwarder(),
aFormat, aSize, aSelector,
aTextureFlags | mTextureFlags,
aAllocFlags);
}
already_AddRefed<TextureClient>
CompositableClient::CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags)
{
return TextureClient::CreateFromSurface(GetForwarder(),
aSurface,
aSelector,
aTextureFlags | mTextureFlags,
aAllocFlags);
}
bool
CompositableClient::AddTextureClient(TextureClient* aClient)
{
if(!aClient) {
return false;
}
aClient->SetAddedToCompositableClient();
return aClient->InitIPDLActor(mForwarder);
}
void
CompositableClient::ClearCachedResources()
{
if (mTextureClientRecycler) {
mTextureClientRecycler->ShrinkToMinimumSize();
}
}
void
CompositableClient::HandleMemoryPressure()
{
if (mTextureClientRecycler) {
mTextureClientRecycler->ShrinkToMinimumSize();
}
}
void
CompositableClient::RemoveTexture(TextureClient* aTexture)
{
mForwarder->RemoveTextureFromCompositable(this, aTexture);
}
TextureClientRecycleAllocator*
CompositableClient::GetTextureClientRecycler()
{
if (mTextureClientRecycler) {
return mTextureClientRecycler;
}
if (!mForwarder) {
return nullptr;
}
if(!mForwarder->GetTextureForwarder()->UsesImageBridge()) {
MOZ_ASSERT(NS_IsMainThread());
mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
return mTextureClientRecycler;
}
// Handle a case that mForwarder is ImageBridge
if (InImageBridgeChildThread()) {
mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
return mTextureClientRecycler;
}
ReentrantMonitor barrier("CompositableClient::GetTextureClientRecycler");
ReentrantMonitorAutoEnter mainThreadAutoMon(barrier);
bool done = false;
RefPtr<Runnable> runnable =
NS_NewRunnableFunction([&]() {
if (!mTextureClientRecycler) {
mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
}
ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
done = true;
barrier.NotifyAll();
});
ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
// should stop the thread until done.
while (!done) {
barrier.Wait();
}
return mTextureClientRecycler;
}
void
CompositableClient::DumpTextureClient(std::stringstream& aStream,
TextureClient* aTexture,
TextureDumpMode aCompress)
{
if (!aTexture) {
return;
}
RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
if (!dSurf) {
return;
}
if (aCompress == TextureDumpMode::Compress) {
aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
} else {
aStream << gfxUtils::GetAsDataURI(dSurf).get();
}
}
AutoRemoveTexture::~AutoRemoveTexture()
{
if (mCompositable && mTexture && mCompositable->IsConnected()) {
mCompositable->RemoveTexture(mTexture);
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,209 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_BUFFERCLIENT_H
#define MOZILLA_GFX_BUFFERCLIENT_H
#include <stdint.h> // for uint64_t
#include <vector> // for vector
#include <map> // for map
#include "mozilla/Assertions.h" // for MOZ_CRASH
#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
#include "mozilla/gfx/Types.h" // for SurfaceFormat
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/LayersTypes.h" // for LayersBackend, TextureDumpMode
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
namespace mozilla {
namespace layers {
class CompositableClient;
class ImageBridgeChild;
class ImageContainer;
class CompositableForwarder;
class CompositableChild;
class PCompositableChild;
class TextureClientRecycleAllocator;
/**
* CompositableClient manages the texture-specific logic for composite layers,
* independently of the layer. It is the content side of a CompositableClient/
* CompositableHost pair.
*
* CompositableClient's purpose is to send texture data to the compositor side
* along with any extra information about how the texture is to be composited.
* Things like opacity or transformation belong to layer and not compositable.
*
* Since Compositables are independent of layers it is possible to create one,
* connect it to the compositor side, and start sending images to it. This alone
* is arguably not very useful, but it means that as long as a shadow layer can
* do the proper magic to find a reference to the right CompositableHost on the
* Compositor side, a Compositable client can be used outside of the main
* shadow layer forwarder machinery that is used on the main thread.
*
* The first step is to create a Compositable client and call Connect().
* Connect() creates the underlying IPDL actor (see CompositableChild) and the
* corresponding CompositableHost on the other side.
*
* To do in-transaction texture transfer (the default), call
* ShadowLayerForwarder::Attach(CompositableClient*, ShadowableLayer*). This
* will let the LayerComposite on the compositor side know which CompositableHost
* to use for compositing.
*
* To do async texture transfer (like async-video), the CompositableClient
* should be created with a different CompositableForwarder (like
* ImageBridgeChild) and attachment is done with
* CompositableForwarder::AttachAsyncCompositable that takes an identifier
* instead of a CompositableChild, since the CompositableClient is not managed
* by this layer forwarder (the matching uses a global map on the compositor side,
* see CompositableMap in ImageBridgeParent.cpp)
*
* Subclasses: Painted layers use ContentClients, ImageLayers use ImageClients,
* Canvas layers use CanvasClients (but ImageHosts). We have a different subclass
* where we have a different way of interfacing with the textures - in terms of
* drawing into the compositable and/or passing its contents to the compostior.
*/
class CompositableClient
{
protected:
virtual ~CompositableClient();
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableClient)
explicit CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = TextureFlags::NO_FLAGS);
virtual void Dump(std::stringstream& aStream,
const char* aPrefix="",
bool aDumpHtml=false,
TextureDumpMode aCompress=TextureDumpMode::Compress) {};
virtual TextureInfo GetTextureInfo() const = 0;
LayersBackend GetCompositorBackendType() const;
already_AddRefed<TextureClient>
CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE,
TextureFlags aFlags = TextureFlags::DEFAULT);
already_AddRefed<TextureClient>
CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
already_AddRefed<TextureClient>
CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
/**
* Establishes the connection with compositor side through IPDL
*/
virtual bool Connect(ImageContainer* aImageContainer = nullptr);
void Destroy();
bool IsConnected() const;
PCompositableChild* GetIPDLActor() const;
CompositableForwarder* GetForwarder() const
{
return mForwarder;
}
/**
* This identifier is what lets us attach async compositables with a shadow
* layer. It is not used if the compositable is used with the regular shadow
* layer forwarder.
*
* If this returns zero, it means the compositable is not async (it is used
* on the main thread).
*/
uint64_t GetAsyncID() const;
/**
* Tells the Compositor to create a TextureHost for this TextureClient.
*/
virtual bool AddTextureClient(TextureClient* aClient);
/**
* A hook for the when the Compositable is detached from it's layer.
*/
virtual void OnDetach() {}
/**
* Clear any resources that are not immediately necessary. This may be called
* in low-memory conditions.
*/
virtual void ClearCachedResources();
/**
* Shrink memory usage.
* Called when "memory-pressure" is observed.
*/
virtual void HandleMemoryPressure();
/**
* Should be called when deataching a TextureClient from a Compositable, because
* some platforms need to do some extra book keeping when this happens (for
* example to properly keep track of fences on Gonk).
*
* See AutoRemoveTexture to automatically invoke this at the end of a scope.
*/
virtual void RemoveTexture(TextureClient* aTexture);
static RefPtr<CompositableClient> FromIPDLActor(PCompositableChild* aActor);
void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
TextureFlags GetTextureFlags() const { return mTextureFlags; }
TextureClientRecycleAllocator* GetTextureClientRecycler();
bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
static void DumpTextureClient(std::stringstream& aStream,
TextureClient* aTexture,
TextureDumpMode aCompress);
protected:
RefPtr<CompositableChild> mCompositableChild;
RefPtr<CompositableForwarder> mForwarder;
// Some layers may want to enforce some flags to all their textures
// (like disallowing tiling)
TextureFlags mTextureFlags;
RefPtr<TextureClientRecycleAllocator> mTextureClientRecycler;
friend class CompositableChild;
};
/**
* Helper to call RemoveTexture at the end of a scope.
*/
struct AutoRemoveTexture
{
explicit AutoRemoveTexture(CompositableClient* aCompositable,
TextureClient* aTexture = nullptr)
: mTexture(aTexture)
, mCompositable(aCompositable)
{}
~AutoRemoveTexture();
RefPtr<TextureClient> mTexture;
private:
CompositableClient* mCompositable;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,668 @@
/* -*- 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 "mozilla/layers/ContentClient.h"
#include "BasicLayers.h" // for BasicLayerManager
#include "gfxContext.h" // for gfxContext, etc
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxEnv.h" // for gfxEnv
#include "gfxPrefs.h" // for gfxPrefs
#include "gfxPoint.h" // for IntSize, gfxPoint
#include "gfxUtils.h" // for gfxUtils
#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
#include "mozilla/ArrayUtils.h" // for ArrayLength
#include "mozilla/Maybe.h"
#include "mozilla/gfx/2D.h" // for DrawTarget, Factory
#include "mozilla/gfx/BasePoint.h" // for BasePoint
#include "mozilla/gfx/BaseSize.h" // for BaseSize
#include "mozilla/gfx/Rect.h" // for Rect
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
#include "mozilla/layers/LayerManagerComposite.h"
#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
#include "mozilla/layers/LayersTypes.h"
#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
#include "nsISupportsImpl.h" // for gfxContext::Release, etc
#include "nsIWidget.h" // for nsIWidget
#include "nsLayoutUtils.h"
#ifdef XP_WIN
#include "gfxWindowsPlatform.h"
#endif
#ifdef MOZ_WIDGET_GTK
#include "gfxPlatformGtk.h"
#endif
#include "ReadbackLayer.h"
#include <vector>
using namespace std;
namespace mozilla {
using namespace gfx;
namespace layers {
static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
{
TextureFlags result = TextureFlags::NO_FLAGS;
if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
result |= TextureFlags::COMPONENT_ALPHA;
}
return result;
}
/* static */ already_AddRefed<ContentClient>
ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
{
LayersBackend backend = aForwarder->GetCompositorBackendType();
if (backend != LayersBackend::LAYERS_OPENGL &&
backend != LayersBackend::LAYERS_D3D9 &&
backend != LayersBackend::LAYERS_D3D11 &&
backend != LayersBackend::LAYERS_BASIC) {
return nullptr;
}
bool useDoubleBuffering = false;
#ifdef XP_WIN
if (backend == LayersBackend::LAYERS_D3D11) {
useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
} else
#endif
#ifdef MOZ_WIDGET_GTK
// We can't use double buffering when using image content with
// Xrender support on Linux, as ContentHostDoubleBuffered is not
// suited for direct uploads to the server.
if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
!gfxVars::UseXRender())
#endif
{
useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() &&
backend != LayersBackend::LAYERS_D3D9) ||
backend == LayersBackend::LAYERS_BASIC;
}
if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
}
return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
}
void
ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
{
}
void
ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
aStream << aPrefix;
aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
if (profiler_feature_active("displaylistdump")) {
nsAutoCString pfx(aPrefix);
pfx += " ";
Dump(aStream, pfx.get(), false);
}
}
// We pass a null pointer for the ContentClient Forwarder argument, which means
// this client will not have a ContentHost on the other side.
ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
: ContentClient(nullptr)
, RotatedContentBuffer(ContainsVisibleBounds)
, mBackend(aBackend)
{}
void
ContentClientBasic::CreateBuffer(ContentType aType,
const IntRect& aRect,
uint32_t aFlags,
RefPtr<gfx::DrawTarget>* aBlackDT,
RefPtr<gfx::DrawTarget>* aWhiteDT)
{
MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
if (aFlags & BUFFER_COMPONENT_ALPHA) {
gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
}
IntSize size(aRect.width, aRect.height);
#ifdef XP_WIN
if (mBackend == BackendType::CAIRO &&
(aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) {
RefPtr<gfxASurface> surf =
new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 :
gfxImageFormat::A8R8G8B8_UINT32);
*aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
if (*aBlackDT) {
return;
}
}
#endif
*aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
mBackend, size,
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
}
void
ContentClientRemoteBuffer::DestroyBuffers()
{
if (!mTextureClient) {
return;
}
mOldTextures.AppendElement(mTextureClient);
mTextureClient = nullptr;
if (mTextureClientOnWhite) {
mOldTextures.AppendElement(mTextureClientOnWhite);
mTextureClientOnWhite = nullptr;
}
DestroyFrontBuffer();
}
class RemoteBufferReadbackProcessor : public TextureReadbackSink
{
public:
RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
: mReadbackUpdates(*aReadbackUpdates)
, mBufferRect(aBufferRect)
, mBufferRotation(aBufferRotation)
{
for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
mLayerRefs.push_back(mReadbackUpdates[i].mLayer);
}
}
virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface)
{
SourceRotatedBuffer rotBuffer(aSourceSurface, nullptr, mBufferRect, mBufferRotation);
for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
ReadbackProcessor::Update& update = mReadbackUpdates[i];
nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
ReadbackSink* sink = update.mLayer->GetSink();
if (!sink) {
continue;
}
if (!aSourceSurface) {
sink->SetUnknown(update.mSequenceCounter);
continue;
}
RefPtr<DrawTarget> dt =
sink->BeginUpdate(update.mUpdateRect + offset, update.mSequenceCounter);
if (!dt) {
continue;
}
dt->SetTransform(Matrix::Translation(offset.x, offset.y));
rotBuffer.DrawBufferWithRotation(dt, RotatedBuffer::BUFFER_BLACK);
update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
}
}
private:
nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
// This array is used to keep the layers alive until the callback.
vector<RefPtr<Layer>> mLayerRefs;
IntRect mBufferRect;
nsIntPoint mBufferRotation;
};
void
ContentClientRemoteBuffer::BeginPaint()
{
EnsureBackBufferIfFrontBuffer();
// XXX: So we might not have a TextureClient yet.. because it will
// only be created by CreateBuffer.. which will deliver a locked surface!.
if (mTextureClient) {
SetBufferProvider(mTextureClient);
}
if (mTextureClientOnWhite) {
SetBufferProviderOnWhite(mTextureClientOnWhite);
}
}
void
ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
{
MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
// XXX: We might still not have a texture client if PaintThebes
// decided we didn't need one yet because the region to draw was empty.
SetBufferProvider(nullptr);
SetBufferProviderOnWhite(nullptr);
for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
if (mOldTextures[i]->IsLocked()) {
mOldTextures[i]->Unlock();
}
}
mOldTextures.Clear();
if (mTextureClient && mTextureClient->IsLocked()) {
if (aReadbackUpdates->Length() > 0) {
RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
mTextureClient->SetReadbackSink(readbackSink);
}
mTextureClient->Unlock();
mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
}
if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
mTextureClientOnWhite->Unlock();
mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject());
}
ContentClientRemote::EndPaint(aReadbackUpdates);
}
void
ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
const IntRect& aRect,
uint32_t aFlags)
{
// If we hit this assertion, then it might be due to an empty transaction
// followed by a real transaction. Our buffers should be created (but not
// painted in the empty transaction) and then painted (but not created) in the
// real transaction. That is kind of fragile, and this assert will catch
// circumstances where we screw that up, e.g., by unnecessarily recreating our
// buffers.
MOZ_ASSERT(!mIsNewBuffer,
"Bad! Did we create a buffer twice without painting?");
mIsNewBuffer = true;
DestroyBuffers();
mSurfaceFormat = aFormat;
mSize = IntSize(aRect.width, aRect.height);
mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
if (aFlags & BUFFER_COMPONENT_ALPHA) {
mTextureFlags |= TextureFlags::COMPONENT_ALPHA;
}
CreateBackBuffer(mBufferRect);
}
void
ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
{
// gfx::BackendType::NONE means fallback to the content backend
TextureAllocationFlags textureAllocFlags
= (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
mTextureClient = CreateTextureClientForDrawing(
mSurfaceFormat, mSize, BackendSelector::Content,
mTextureFlags | ExtraTextureFlags(),
textureAllocFlags
);
if (!mTextureClient || !AddTextureClient(mTextureClient)) {
AbortTextureClientCreation();
return;
}
if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
mTextureClientOnWhite = mTextureClient->CreateSimilar(
mForwarder->GetCompositorBackendType(),
mTextureFlags | ExtraTextureFlags(),
TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
);
if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
AbortTextureClientCreation();
return;
}
}
}
void
ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
const IntRect& aRect,
uint32_t aFlags,
RefPtr<gfx::DrawTarget>* aBlackDT,
RefPtr<gfx::DrawTarget>* aWhiteDT)
{
BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
if (!mTextureClient) {
return;
}
// We just created the textures and we are about to get their draw targets
// so we have to lock them here.
DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked, "Could not lock the TextureClient");
*aBlackDT = mTextureClient->BorrowDrawTarget();
if (aFlags & BUFFER_COMPONENT_ALPHA) {
locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
*aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
}
}
nsIntRegion
ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy)
{
nsIntRegion updatedRegion;
if (mIsNewBuffer || aDidSelfCopy) {
// A buffer reallocation clears both buffers. The front buffer has all the
// content by now, but the back buffer is still clear. Here, in effect, we
// are saying to copy all of the pixels of the front buffer to the back.
// Also when we self-copied in the buffer, the buffer space
// changes and some changed buffer content isn't reflected in the
// draw or invalidate region (on purpose!). When this happens, we
// need to read back the entire buffer too.
updatedRegion = aVisibleRegion.GetBounds();
mIsNewBuffer = false;
} else {
updatedRegion = aRegionToDraw;
}
NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
"Update outside of buffer rect!");
MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
return updatedRegion;
}
void
ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy)
{
nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
aVisibleRegion,
aDidSelfCopy);
MOZ_ASSERT(mTextureClient);
if (mTextureClientOnWhite) {
mForwarder->UseComponentAlphaTextures(this, mTextureClient,
mTextureClientOnWhite);
} else {
AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
t->mTextureClient = mTextureClient;
IntSize size = mTextureClient->GetSize();
t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
GetForwarder()->UseTextures(this, textures);
}
mForwarder->UpdateTextureRegion(this,
ThebesBufferData(BufferRect(),
BufferRotation()),
updatedRegion);
}
void
ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
{
mFrontAndBackBufferDiffer = true;
}
void
ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml, TextureDumpMode aCompress)
{
// TODO We should combine the OnWhite/OnBlack here an just output a single image.
if (!aDumpHtml) {
aStream << "\n" << aPrefix << "Surface: ";
}
CompositableClient::DumpTextureClient(aStream, mTextureClient, aCompress);
}
void
ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml, TextureDumpMode aCompress)
{
// TODO We should combine the OnWhite/OnBlack here an just output a single image.
if (!aDumpHtml) {
aStream << "\n" << aPrefix << "Surface: ";
}
CompositableClient::DumpTextureClient(aStream, mFrontClient, aCompress);
}
void
ContentClientDoubleBuffered::DestroyFrontBuffer()
{
if (mFrontClient) {
mOldTextures.AppendElement(mFrontClient);
mFrontClient = nullptr;
}
if (mFrontClientOnWhite) {
mOldTextures.AppendElement(mFrontClientOnWhite);
mFrontClientOnWhite = nullptr;
}
}
void
ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy)
{
ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
}
void
ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
{
mFrontUpdatedRegion = aFrontUpdatedRegion;
RefPtr<TextureClient> oldBack = mTextureClient;
mTextureClient = mFrontClient;
mFrontClient = oldBack;
oldBack = mTextureClientOnWhite;
mTextureClientOnWhite = mFrontClientOnWhite;
mFrontClientOnWhite = oldBack;
IntRect oldBufferRect = mBufferRect;
mBufferRect = mFrontBufferRect;
mFrontBufferRect = oldBufferRect;
nsIntPoint oldBufferRotation = mBufferRotation;
mBufferRotation = mFrontBufferRotation;
mFrontBufferRotation = oldBufferRotation;
MOZ_ASSERT(mFrontClient);
ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion);
}
void
ContentClientDoubleBuffered::BeginPaint()
{
ContentClientRemoteBuffer::BeginPaint();
mIsNewBuffer = false;
if (!mFrontAndBackBufferDiffer) {
return;
}
if (mDidSelfCopy) {
// We can't easily draw our front buffer into us, since we're going to be
// copying stuff around anyway it's easiest if we just move our situation
// to non-rotated while we're at it. If this situation occurs we'll have
// hit a self-copy path in PaintThebes before as well anyway.
mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
mBufferRotation = nsIntPoint();
return;
}
mBufferRect = mFrontBufferRect;
mBufferRotation = mFrontBufferRotation;
}
// Sync front/back buffers content
// After executing, the new back buffer has the same (interesting) pixels as
// the new front buffer, and mValidRegion et al. are correct wrt the new
// back buffer (i.e. as they were for the old back buffer)
void
ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
{
if (mTextureClient) {
DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked);
}
if (mTextureClientOnWhite) {
DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked);
}
if (!mFrontAndBackBufferDiffer) {
MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
return;
}
MOZ_ASSERT(mFrontClient);
if (!mFrontClient) {
return;
}
MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
this,
mFrontUpdatedRegion.GetBounds().x,
mFrontUpdatedRegion.GetBounds().y,
mFrontUpdatedRegion.GetBounds().width,
mFrontUpdatedRegion.GetBounds().height));
mFrontAndBackBufferDiffer = false;
nsIntRegion updateRegion = mFrontUpdatedRegion;
if (mDidSelfCopy) {
mDidSelfCopy = false;
updateRegion = mBufferRect;
}
// No point in sync'ing what we are going to draw over anyway. And if there is
// nothing to sync at all, there is nothing to do and we can go home early.
updateRegion.Sub(updateRegion, aRegionToDraw);
if (updateRegion.IsEmpty()) {
return;
}
// We need to ensure that we lock these two buffers in the same
// order as the compositor to prevent deadlocks.
TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
if (!frontLock.Succeeded()) {
return;
}
Maybe<TextureClientAutoLock> frontOnWhiteLock;
if (mFrontClientOnWhite) {
frontOnWhiteLock.emplace(mFrontClientOnWhite, OpenMode::OPEN_READ_ONLY);
if (!frontOnWhiteLock->Succeeded()) {
return;
}
}
// Restrict the DrawTargets and frontBuffer to a scope to make
// sure there is no more external references to the DrawTargets
// when we Unlock the TextureClients.
gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget();
gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr;
if (dt && dt->IsValid()) {
RefPtr<SourceSurface> surf = dt->Snapshot();
RefPtr<SourceSurface> surfOnWhite = dtw ? dtw->Snapshot() : nullptr;
SourceRotatedBuffer frontBuffer(surf,
surfOnWhite,
mFrontBufferRect,
mFrontBufferRotation);
UpdateDestinationFrom(frontBuffer, updateRegion);
} else {
// We know this can happen, but we want to track it somewhat, in case it leads
// to other problems.
gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw);
}
}
void
ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
{
if (!mTextureClient && mFrontClient) {
CreateBackBuffer(mFrontBufferRect);
mBufferRect = mFrontBufferRect;
mBufferRotation = mFrontBufferRotation;
}
}
void
ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
const nsIntRegion& aUpdateRegion)
{
DrawIterator iter;
while (DrawTarget* destDT =
BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
if (isClippingCheap) {
gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
}
aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
if (isClippingCheap) {
destDT->PopClip();
}
// Flush the destination before the sources become inaccessible (Unlock).
destDT->Flush();
ReturnDrawTargetToBuffer(destDT);
}
if (aSource.HaveBufferOnWhite()) {
MOZ_ASSERT(HaveBufferOnWhite());
DrawIterator whiteIter;
while (DrawTarget* destDT =
BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
if (isClippingCheap) {
gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
}
aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
if (isClippingCheap) {
destDT->PopClip();
}
// Flush the destination before the sources become inaccessible (Unlock).
destDT->Flush();
ReturnDrawTargetToBuffer(destDT);
}
}
}
void
ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
{
if (mTextureClient) {
DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked);
}
if (mTextureClientOnWhite) {
DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
MOZ_ASSERT(locked);
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,412 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_CONTENTCLIENT_H
#define MOZILLA_GFX_CONTENTCLIENT_H
#include <stdint.h> // for uint32_t
#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
#include "gfxTypes.h"
#include "gfxPlatform.h" // for gfxPlatform
#include "mozilla/Assertions.h" // for MOZ_CRASH
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/mozalloc.h" // for operator delete
#include "ReadbackProcessor.h" // For ReadbackProcessor::Update
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsPoint.h" // for nsIntPoint
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsRegion.h" // for nsIntRegion
#include "nsTArray.h" // for nsTArray
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
namespace layers {
class PaintedLayer;
/**
* A compositable client for PaintedLayers. These are different to Image/Canvas
* clients due to sending a valid region across IPC and because we do a lot more
* optimisation work, encapsualted in RotatedContentBuffers.
*
* We use content clients for OMTC and non-OMTC, basic rendering so that
* BasicPaintedLayer has only one interface to deal with. We support single and
* double buffered flavours. For tiled layers, we do not use a ContentClient
* although we do have a ContentHost, and we do use texture clients and texture
* hosts.
*
* The interface presented by ContentClient is used by the BasicPaintedLayer
* methods - PaintThebes, which is the same for MT and OMTC, and PaintBuffer
* which is different (the OMTC one does a little more). The 'buffer' in the
* names of a lot of these method is actually the TextureClient. But, 'buffer'
* for the RotatedContentBuffer (as in SetBuffer) means a gfxSurface. See the
* comments for SetBuffer and SetBufferProvider in RotatedContentBuffer. To keep
* these mapped buffers alive, we store a pointer in mOldTextures if the
* RotatedContentBuffer's surface is not the one from our texture client, once we
* are done painting we unmap the surface/texture client and don't need to keep
* it alive anymore, so we clear mOldTextures.
*
* The sequence for painting is: call BeginPaint on the content client;
* call BeginPaintBuffer on the content client. That will initialise the buffer
* for painting, by calling RotatedContentBuffer::BeginPaint (usually) which
* will call back to ContentClient::FinalizeFrame to finalize update of the
* buffers before drawing (i.e., it finalizes the previous frame). Then call
* BorrowDrawTargetForPainting to get a DrawTarget to paint into. Then paint.
* Then return that DrawTarget using ReturnDrawTarget.
* Call EndPaint on the content client;
*
* SwapBuffers is called in response to the transaction reply from the compositor.
*/
class ContentClient : public CompositableClient
{
public:
/**
* Creates, configures, and returns a new content client. If necessary, a
* message will be sent to the compositor to create a corresponding content
* host.
*/
static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
explicit ContentClient(CompositableForwarder* aForwarder)
: CompositableClient(aForwarder)
{}
virtual ~ContentClient()
{}
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
virtual void Clear() = 0;
virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
uint32_t aFlags) = 0;
virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
// Called as part of the layers transation reply. Conveys data about our
// buffer(s) from the compositor. If appropriate we should swap references
// to our buffers.
virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
// call before and after painting into this content client
virtual void BeginPaint() {}
virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
};
/**
* A ContentClient for use with OMTC.
*/
class ContentClientRemote : public ContentClient
{
public:
explicit ContentClientRemote(CompositableForwarder* aForwarder)
: ContentClient(aForwarder)
{}
virtual void Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy) = 0;
};
// thin wrapper around RotatedContentBuffer, for on-mtc
class ContentClientBasic final : public ContentClient
, protected RotatedContentBuffer
{
public:
explicit ContentClientBasic(gfx::BackendType aBackend);
typedef RotatedContentBuffer::PaintState PaintState;
typedef RotatedContentBuffer::ContentType ContentType;
virtual void Clear() override { RotatedContentBuffer::Clear(); }
virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
uint32_t aFlags) override
{
return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
}
virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
RotatedContentBuffer::DrawIterator* aIter = nullptr) override
{
return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
}
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
{
BorrowDrawTarget::ReturnDrawTarget(aReturned);
}
void DrawTo(PaintedLayer* aLayer,
gfx::DrawTarget* aTarget,
float aOpacity,
gfx::CompositionOp aOp,
gfx::SourceSurface* aMask,
const gfx::Matrix* aMaskTransform)
{
RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp,
aMask, aMaskTransform);
}
virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
virtual TextureInfo GetTextureInfo() const override
{
MOZ_CRASH("GFX: Should not be called on non-remote ContentClient");
}
private:
gfx::BackendType mBackend;
};
/**
* A ContentClientRemote backed by a RotatedContentBuffer.
*
* When using a ContentClientRemote, SurfaceDescriptors are created on
* the rendering side and destroyed on the compositing side. They are only
* passed from one side to the other when the TextureClient/Hosts are created.
* *Ownership* of the SurfaceDescriptor moves from the rendering side to the
* compositing side with the create message (send from CreateBuffer) which
* tells the compositor that TextureClients have been created and that the
* compositor should assign the corresponding TextureHosts to our corresponding
* ContentHost.
*
* If the size or type of our buffer(s) change(s), then we simply destroy and
* create them.
*/
// Version using new texture clients
class ContentClientRemoteBuffer : public ContentClientRemote
, protected RotatedContentBuffer
{
using RotatedContentBuffer::BufferRect;
using RotatedContentBuffer::BufferRotation;
public:
explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder)
: ContentClientRemote(aForwarder)
, RotatedContentBuffer(ContainsVisibleBounds)
, mIsNewBuffer(false)
, mFrontAndBackBufferDiffer(false)
, mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8)
{}
typedef RotatedContentBuffer::PaintState PaintState;
typedef RotatedContentBuffer::ContentType ContentType;
virtual void Clear() override
{
RotatedContentBuffer::Clear();
mTextureClient = nullptr;
mTextureClientOnWhite = nullptr;
}
virtual void Dump(std::stringstream& aStream,
const char* aPrefix="",
bool aDumpHtml=false,
TextureDumpMode aCompress=TextureDumpMode::Compress) override;
virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
uint32_t aFlags) override
{
return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
}
virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
RotatedContentBuffer::DrawIterator* aIter = nullptr) override
{
return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
}
virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
{
BorrowDrawTarget::ReturnDrawTarget(aReturned);
}
/**
* Begin/End Paint map a gfxASurface from the texture client
* into the buffer of RotatedBuffer. The surface is only
* valid when the texture client is locked, so is mapped out
* of RotatedContentBuffer when we are done painting.
* None of the underlying buffer attributes (rect, rotation)
* are affected by mapping/unmapping.
*/
virtual void BeginPaint() override;
virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
virtual void Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy) override;
virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
// Expose these protected methods from the superclass.
virtual const gfx::IntRect& BufferRect() const
{
return RotatedContentBuffer::BufferRect();
}
virtual const nsIntPoint& BufferRotation() const
{
return RotatedContentBuffer::BufferRotation();
}
virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
virtual TextureFlags ExtraTextureFlags() const
{
return TextureFlags::NO_FLAGS;
}
protected:
void DestroyBuffers();
virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy);
void BuildTextureClients(gfx::SurfaceFormat aFormat,
const gfx::IntRect& aRect,
uint32_t aFlags);
void CreateBackBuffer(const gfx::IntRect& aBufferRect);
// Ensure we have a valid back buffer if we have a valid front buffer (i.e.
// if a backbuffer has been created.)
virtual void EnsureBackBufferIfFrontBuffer() {}
// Create the front buffer for the ContentClient/Host pair if necessary
// and notify the compositor that we have created the buffer(s).
virtual void DestroyFrontBuffer() {}
virtual void AbortTextureClientCreation()
{
mTextureClient = nullptr;
mTextureClientOnWhite = nullptr;
mIsNewBuffer = false;
}
RefPtr<TextureClient> mTextureClient;
RefPtr<TextureClient> mTextureClientOnWhite;
// keep a record of texture clients we have created and need to keep around
// (for RotatedBuffer to access), then unlock and remove them when we are done
// painting.
nsTArray<RefPtr<TextureClient> > mOldTextures;
bool mIsNewBuffer;
bool mFrontAndBackBufferDiffer;
gfx::IntSize mSize;
gfx::SurfaceFormat mSurfaceFormat;
};
/**
* A double buffered ContentClient. mTextureClient is the back buffer, which
* we draw into. mFrontClient is the front buffer which we may read from, but
* not write to, when the compositor does not have the 'soft' lock. We can write
* into mTextureClient at any time.
*
* The ContentHost keeps a reference to both corresponding texture hosts, in
* response to our UpdateTextureRegion message, the compositor swaps its
* references. In response to the compositor's reply we swap our references
* (in SwapBuffers).
*/
class ContentClientDoubleBuffered : public ContentClientRemoteBuffer
{
public:
explicit ContentClientDoubleBuffered(CompositableForwarder* aFwd)
: ContentClientRemoteBuffer(aFwd)
{}
virtual ~ContentClientDoubleBuffered() {}
virtual void Clear() override
{
ContentClientRemoteBuffer::Clear();
mFrontClient = nullptr;
mFrontClientOnWhite = nullptr;
}
virtual void Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy) override;
virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
virtual void BeginPaint() override;
virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
virtual void EnsureBackBufferIfFrontBuffer() override;
virtual TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
}
virtual void Dump(std::stringstream& aStream,
const char* aPrefix="",
bool aDumpHtml=false,
TextureDumpMode aCompress=TextureDumpMode::Compress) override;
protected:
virtual void DestroyFrontBuffer() override;
private:
void UpdateDestinationFrom(const RotatedBuffer& aSource,
const nsIntRegion& aUpdateRegion);
virtual void AbortTextureClientCreation() override
{
mTextureClient = nullptr;
mTextureClientOnWhite = nullptr;
mFrontClient = nullptr;
mFrontClientOnWhite = nullptr;
}
RefPtr<TextureClient> mFrontClient;
RefPtr<TextureClient> mFrontClientOnWhite;
nsIntRegion mFrontUpdatedRegion;
gfx::IntRect mFrontBufferRect;
nsIntPoint mFrontBufferRotation;
};
/**
* A single buffered ContentClient. We have a single TextureClient/Host
* which we update and then send a message to the compositor that we are
* done updating. It is not safe for the compositor to use the corresponding
* TextureHost's memory directly, it must upload it to video memory of some
* kind. We are free to modify the TextureClient once we receive reply from
* the compositor.
*/
class ContentClientSingleBuffered : public ContentClientRemoteBuffer
{
public:
explicit ContentClientSingleBuffered(CompositableForwarder* aFwd)
: ContentClientRemoteBuffer(aFwd)
{
}
virtual ~ContentClientSingleBuffered() {}
virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
virtual TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::CONTENT_SINGLE, mTextureFlags | ExtraTextureFlags());
}
virtual TextureFlags ExtraTextureFlags() const override
{
return TextureFlags::IMMEDIATE_UPLOAD;
}
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,72 @@
/* -*- 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 "GPUVideoTextureClient.h"
#include "mozilla/dom/VideoDecoderManagerChild.h"
#include "mozilla/gfx/2D.h"
namespace mozilla {
namespace layers {
using namespace gfx;
GPUVideoTextureData::GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
const SurfaceDescriptorGPUVideo& aSD,
const gfx::IntSize& aSize)
: mManager(aManager)
, mSD(aSD)
, mSize(aSize)
{}
GPUVideoTextureData::~GPUVideoTextureData()
{
}
bool
GPUVideoTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
{
aOutDescriptor = mSD;
return true;
}
void
GPUVideoTextureData::FillInfo(TextureData::Info& aInfo) const
{
aInfo.size = mSize;
// TODO: We should probably try do better for this.
// layers::Image doesn't expose a format, so it's hard
// to figure out in VideoDecoderParent.
aInfo.format = SurfaceFormat::B8G8R8X8;
aInfo.hasIntermediateBuffer = false;
aInfo.hasSynchronization = false;
aInfo.supportsMoz2D = false;
aInfo.canExposeMappedData = false;
}
already_AddRefed<SourceSurface>
GPUVideoTextureData::GetAsSourceSurface()
{
return mManager->Readback(mSD);
}
void
GPUVideoTextureData::Deallocate(LayersIPCChannel* aAllocator)
{
mManager->DeallocateSurfaceDescriptorGPUVideo(mSD);
mSD = SurfaceDescriptorGPUVideo();
}
void
GPUVideoTextureData::Forget(LayersIPCChannel* aAllocator)
{
// We always need to manually deallocate on the client side.
// Ideally we'd set up our TextureClient with the DEALLOCATE_CLIENT
// flag, but that forces texture destruction to be synchronous.
// Instead let's just deallocate from here as well.
Deallocate(aAllocator);
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,56 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
#define MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
#include "mozilla/layers/TextureClient.h"
namespace mozilla {
namespace gfx {
class SourceSurface;
}
namespace dom {
class VideoDecoderManagerChild;
}
namespace layers {
class GPUVideoTextureData : public TextureData
{
public:
GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
const SurfaceDescriptorGPUVideo& aSD,
const gfx::IntSize& aSize);
~GPUVideoTextureData();
virtual void FillInfo(TextureData::Info& aInfo) const override;
virtual bool Lock(OpenMode) override { return true; };
virtual void Unlock() override {};
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual void Deallocate(LayersIPCChannel* aAllocator) override;
virtual void Forget(LayersIPCChannel* aAllocator) override;
already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
virtual GPUVideoTextureData* AsGPUVideoTextureData() override
{
return this;
}
protected:
RefPtr<dom::VideoDecoderManagerChild> mManager;
SurfaceDescriptorGPUVideo mSD;
gfx::IntSize mSize;
};
} // namespace layers
} // namespace mozilla
#endif // MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H

View file

@ -0,0 +1,300 @@
/* -*- 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 "ImageClient.h"
#include <stdint.h> // for uint32_t
#include "ClientLayerManager.h" // for ClientLayer
#include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc
#include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc
#include "GLImages.h" // for SurfaceTextureImage::Data, etc
#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
#include "gfxPlatform.h" // for gfxPlatform
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/BaseSize.h" // for BaseSize
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
#include "mozilla/layers/TextureClientOGL.h" // for SurfaceTextureClient
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
#include "nsISupportsImpl.h" // for Image::Release, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
/* static */ already_AddRefed<ImageClient>
ImageClient::CreateImageClient(CompositableType aCompositableHostType,
CompositableForwarder* aForwarder,
TextureFlags aFlags)
{
RefPtr<ImageClient> result = nullptr;
switch (aCompositableHostType) {
case CompositableType::IMAGE:
result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
break;
case CompositableType::IMAGE_BRIDGE:
result = new ImageClientBridge(aForwarder, aFlags);
break;
case CompositableType::UNKNOWN:
result = nullptr;
break;
default:
MOZ_CRASH("GFX: unhandled program type image");
}
NS_ASSERTION(result, "Failed to create ImageClient");
return result.forget();
}
void
ImageClient::RemoveTexture(TextureClient* aTexture)
{
GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
}
ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
TextureFlags aFlags,
CompositableType aType)
: ImageClient(aFwd, aFlags, aType)
{
}
TextureInfo ImageClientSingle::GetTextureInfo() const
{
return TextureInfo(CompositableType::IMAGE);
}
void
ImageClientSingle::FlushAllImages()
{
MOZ_ASSERT(GetForwarder()->GetTextureForwarder()->UsesImageBridge());
for (auto& b : mBuffers) {
RemoveTexture(b.mTextureClient);
}
mBuffers.Clear();
}
/* static */ already_AddRefed<TextureClient>
ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder)
{
RefPtr<TextureClient> texture;
if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
const PlanarYCbCrData* data = ycbcr->GetData();
if (!data) {
return nullptr;
}
texture = TextureClient::CreateForYCbCr(aForwarder,
data->mYSize, data->mCbCrSize, data->mStereoMode,
data->mYUVColorSpace,
TextureFlags::DEFAULT);
if (!texture) {
return nullptr;
}
TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
if (!autoLock.Succeeded()) {
return nullptr;
}
bool status = UpdateYCbCrTextureClient(texture, *data);
MOZ_ASSERT(status);
if (!status) {
return nullptr;
}
} else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
aImage->GetFormat() == ImageFormat::EGLIMAGE) {
gfx::IntSize size = aImage->GetSize();
if (aImage->GetFormat() == ImageFormat::EGLIMAGE) {
EGLImageImage* typedImage = aImage->AsEGLImageImage();
texture = EGLImageTextureData::CreateTextureClient(
typedImage, size, aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
#ifdef MOZ_WIDGET_ANDROID
} else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE) {
SurfaceTextureImage* typedImage = aImage->AsSurfaceTextureImage();
texture = AndroidSurfaceTextureData::CreateTextureClient(
typedImage->GetSurfaceTexture(), size, typedImage->GetOriginPos(),
aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
#endif
} else {
MOZ_ASSERT(false, "Bad ImageFormat.");
}
} else {
RefPtr<gfx::SourceSurface> surface = aImage->GetAsSourceSurface();
MOZ_ASSERT(surface);
texture = TextureClient::CreateForDrawing(aForwarder, surface->GetFormat(), aImage->GetSize(),
BackendSelector::Content, TextureFlags::DEFAULT);
if (!texture) {
return nullptr;
}
MOZ_ASSERT(texture->CanExposeDrawTarget());
if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
return nullptr;
}
{
// We must not keep a reference to the DrawTarget after it has been unlocked.
DrawTarget* dt = texture->BorrowDrawTarget();
if (!dt) {
gfxWarning() << "ImageClientSingle::UpdateImage failed in BorrowDrawTarget";
return nullptr;
}
MOZ_ASSERT(surface.get());
dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
}
texture->Unlock();
}
return texture.forget();
}
bool
ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
{
AutoTArray<ImageContainer::OwningImage,4> images;
uint32_t generationCounter;
aContainer->GetCurrentImages(&images, &generationCounter);
if (mLastUpdateGenerationCounter == generationCounter) {
return true;
}
mLastUpdateGenerationCounter = generationCounter;
for (int32_t i = images.Length() - 1; i >= 0; --i) {
if (!images[i].mImage->IsValid()) {
// Don't try to update to an invalid image.
images.RemoveElementAt(i);
}
}
if (images.IsEmpty()) {
// This can happen if a ClearAllImages raced with SetCurrentImages from
// another thread and ClearImagesFromImageBridge ran after the
// SetCurrentImages call but before UpdateImageClientNow.
// This can also happen if all images in the list are invalid.
// We return true because the caller would attempt to recreate the
// ImageClient otherwise, and that isn't going to help.
return true;
}
nsTArray<Buffer> newBuffers;
AutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
for (auto& img : images) {
Image* image = img.mImage;
RefPtr<TextureClient> texture = image->GetTextureClient(GetForwarder());
const bool hasTextureClient = !!texture;
for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
if (mBuffers[i].mImageSerial == image->GetSerial()) {
if (hasTextureClient) {
MOZ_ASSERT(image->GetTextureClient(GetForwarder()) == mBuffers[i].mTextureClient);
} else {
texture = mBuffers[i].mTextureClient;
}
// Remove this element from mBuffers so mBuffers only contains
// images that aren't present in 'images'
mBuffers.RemoveElementAt(i);
}
}
if (!texture) {
// Slow path, we should not be hitting it very often and if we do it means
// we are using an Image class that is not backed by textureClient and we
// should fix it.
texture = CreateTextureClientForImage(image, GetForwarder());
}
if (!texture || !AddTextureClient(texture)) {
return false;
}
CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
t->mTextureClient = texture;
t->mTimeStamp = img.mTimeStamp;
t->mPictureRect = image->GetPictureRect();
t->mFrameID = img.mFrameID;
t->mProducerID = img.mProducerID;
Buffer* newBuf = newBuffers.AppendElement();
newBuf->mImageSerial = image->GetSerial();
newBuf->mTextureClient = texture;
texture->SyncWithObject(GetForwarder()->GetSyncObject());
}
GetForwarder()->UseTextures(this, textures);
for (auto& b : mBuffers) {
RemoveTexture(b.mTextureClient);
}
mBuffers.SwapElements(newBuffers);
return true;
}
bool
ImageClientSingle::AddTextureClient(TextureClient* aTexture)
{
MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
return CompositableClient::AddTextureClient(aTexture);
}
void
ImageClientSingle::OnDetach()
{
mBuffers.Clear();
}
ImageClient::ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
CompositableType aType)
: CompositableClient(aFwd, aFlags)
, mLayer(nullptr)
, mType(aType)
, mLastUpdateGenerationCounter(0)
{}
ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
TextureFlags aFlags)
: ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
, mAsyncContainerID(0)
{
}
bool
ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
{
if (!GetForwarder() || !mLayer) {
return false;
}
if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
return true;
}
mAsyncContainerID = aContainer->GetAsyncContainerID();
static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
return true;
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,138 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_IMAGECLIENT_H
#define MOZILLA_GFX_IMAGECLIENT_H
#include <stdint.h> // for uint32_t, uint64_t
#include <sys/types.h> // for int32_t
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
#include "mozilla/gfx/Types.h" // for SurfaceFormat
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
#include "mozilla/mozalloc.h" // for operator delete
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsRect.h" // for mozilla::gfx::IntRect
namespace mozilla {
namespace layers {
class ClientLayer;
class CompositableForwarder;
class Image;
class ImageContainer;
class ShadowableLayer;
class ImageClientSingle;
/**
* Image clients are used by basic image layers on the content thread, they
* always match with an ImageHost on the compositor thread. See
* CompositableClient.h for information on connecting clients to hosts.
*/
class ImageClient : public CompositableClient
{
public:
/**
* Creates, configures, and returns a new image client. If necessary, a
* message will be sent to the compositor to create a corresponding image
* host.
*/
static already_AddRefed<ImageClient> CreateImageClient(CompositableType aImageHostType,
CompositableForwarder* aFwd,
TextureFlags aFlags);
virtual ~ImageClient() {}
/**
* Update this ImageClient from aContainer in aLayer
* returns false if this is the wrong kind of ImageClient for aContainer.
* Note that returning true does not necessarily imply success
*/
virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
ClientLayer* GetLayer() const { return mLayer; }
/**
* asynchronously remove all the textures used by the image client.
*
*/
virtual void FlushAllImages() {}
virtual void RemoveTexture(TextureClient* aTexture) override;
virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
protected:
ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
CompositableType aType);
ClientLayer* mLayer;
CompositableType mType;
uint32_t mLastUpdateGenerationCounter;
};
/**
* An image client which uses a single texture client.
*/
class ImageClientSingle : public ImageClient
{
public:
ImageClientSingle(CompositableForwarder* aFwd,
TextureFlags aFlags,
CompositableType aType);
virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
virtual void OnDetach() override;
virtual bool AddTextureClient(TextureClient* aTexture) override;
virtual TextureInfo GetTextureInfo() const override;
virtual void FlushAllImages() override;
ImageClientSingle* AsImageClientSingle() override { return this; }
protected:
struct Buffer {
RefPtr<TextureClient> mTextureClient;
int32_t mImageSerial;
};
nsTArray<Buffer> mBuffers;
};
/**
* Image class to be used for async image uploads using the image bridge
* protocol.
* We store the ImageBridge id in the TextureClientIdentifier.
*/
class ImageClientBridge : public ImageClient
{
public:
ImageClientBridge(CompositableForwarder* aFwd,
TextureFlags aFlags);
virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
virtual TextureInfo GetTextureInfo() const override
{
return TextureInfo(mType);
}
protected:
uint64_t mAsyncContainerID;
};
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,263 @@
/* -*- 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 "mozilla/layers/SingleTiledContentClient.h"
#include "ClientTiledPaintedLayer.h"
namespace mozilla {
namespace layers {
SingleTiledContentClient::SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
ClientLayerManager* aManager)
: TiledContentClient(aManager, "Single")
{
MOZ_COUNT_CTOR(SingleTiledContentClient);
mTiledBuffer = new ClientSingleTiledLayerBuffer(aPaintedLayer, *this, aManager);
}
void
SingleTiledContentClient::ClearCachedResources()
{
CompositableClient::ClearCachedResources();
mTiledBuffer->DiscardBuffers();
}
void
SingleTiledContentClient::UpdatedBuffer(TiledBufferType aType)
{
mForwarder->UseTiledLayerBuffer(this, mTiledBuffer->GetSurfaceDescriptorTiles());
mTiledBuffer->ClearPaintedRegion();
}
/* static */ bool
SingleTiledContentClient::ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager)
{
int32_t maxTextureSize = aManager->GetMaxTextureSize();
return aSize.width <= maxTextureSize && aSize.height <= maxTextureSize;
}
ClientSingleTiledLayerBuffer::ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
CompositableClient& aCompositableClient,
ClientLayerManager* aManager)
: ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
, mWasLastPaintProgressive(false)
, mFormat(gfx::SurfaceFormat::UNKNOWN)
{
}
void
ClientSingleTiledLayerBuffer::ReleaseTiles()
{
if (!mTile.IsPlaceholderTile()) {
mTile.DiscardBuffers();
}
mTile.SetTextureAllocator(nullptr);
}
void
ClientSingleTiledLayerBuffer::DiscardBuffers()
{
if (!mTile.IsPlaceholderTile()) {
mTile.DiscardFrontBuffer();
mTile.DiscardBackBuffer();
}
}
SurfaceDescriptorTiles
ClientSingleTiledLayerBuffer::GetSurfaceDescriptorTiles()
{
InfallibleTArray<TileDescriptor> tiles;
TileDescriptor tileDesc = mTile.GetTileDescriptor();
tiles.AppendElement(tileDesc);
mTile.mUpdateRect = gfx::IntRect();
return SurfaceDescriptorTiles(mValidRegion,
tiles,
mTilingOrigin,
mSize,
0, 0, 1, 1,
1.0,
mFrameResolution.xScale,
mFrameResolution.yScale,
mWasLastPaintProgressive);
}
already_AddRefed<TextureClient>
ClientSingleTiledLayerBuffer::GetTextureClient()
{
MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN);
return mCompositableClient.CreateTextureClientForDrawing(
gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content,
TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD);
}
void
ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData,
bool aIsProgressive)
{
mWasLastPaintProgressive = aIsProgressive;
// Compare layer valid region size to current backbuffer size, discard if not matching.
gfx::IntSize size = aNewValidRegion.GetBounds().Size();
gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
nsIntRegion paintRegion = aPaintRegion;
RefPtr<TextureClient> discardedFrontBuffer = nullptr;
RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
nsIntRegion discardedValidRegion;
if (mSize != size ||
mTilingOrigin != origin) {
discardedFrontBuffer = mTile.mFrontBuffer;
discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
discardedValidRegion = mValidRegion;
TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
;
ResetPaintedAndValidState();
mSize = size;
mTilingOrigin = origin;
paintRegion = aNewValidRegion;
}
SurfaceMode mode;
gfxContentType content = GetContentType(&mode);
mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);
if (mTile.IsPlaceholderTile()) {
mTile.SetTextureAllocator(this);
}
// The dirty region relative to the top-left of the tile.
nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);
nsIntRegion extraPainted;
RefPtr<TextureClient> backBufferOnWhite;
RefPtr<TextureClient> backBuffer =
mTile.GetBackBuffer(mCompositableClient,
tileDirtyRegion,
content, mode,
extraPainted,
&backBufferOnWhite);
mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
extraPainted.MoveBy(mTilingOrigin);
extraPainted.And(extraPainted, aNewValidRegion);
mPaintedRegion.OrWith(paintRegion);
mPaintedRegion.OrWith(extraPainted);
if (!backBuffer) {
return;
}
RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
RefPtr<gfx::DrawTarget> dtOnWhite;
if (backBufferOnWhite) {
dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
}
if (mode != SurfaceMode::SURFACE_OPAQUE) {
for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
const gfx::IntRect& rect = iter.Get();
if (dtOnWhite) {
dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
} else {
dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
}
}
}
// If the old frontbuffer was discarded then attempt to copy what we
// can from it to the new backbuffer.
if (discardedFrontBuffer) {
nsIntRegion copyableRegion;
copyableRegion.And(aNewValidRegion, discardedValidRegion);
copyableRegion.SubOut(aDirtyRegion);
if (!copyableRegion.IsEmpty()) {
TextureClientAutoLock frontLock(discardedFrontBuffer,
OpenMode::OPEN_READ);
if (frontLock.Succeeded()) {
for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
}
}
if (discardedFrontBufferOnWhite && backBufferOnWhite) {
TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
OpenMode::OPEN_READ);
if (frontOnWhiteLock.Succeeded()) {
for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
&rect, &dest);
}
}
}
TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
// We don't need to repaint valid content that was just copied.
paintRegion.SubOut(copyableRegion);
}
}
if (dtOnWhite) {
dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
dtOnWhite = nullptr;
}
{
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
if (!ctx) {
gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
return;
}
ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
}
// Mark the area we just drew into the back buffer as invalid in the front buffer as they're
// now out of sync.
mTile.mInvalidFront.OrWith(tileDirtyRegion);
// The new buffer is now validated, remove the dirty region from it.
mTile.mInvalidBack.SubOut(tileDirtyRegion);
dt = nullptr;
mTile.Flip();
UnlockTile(mTile);
if (backBuffer->HasIntermediateBuffer()) {
// If our new buffer has an internal buffer, we don't want to keep another
// TextureClient around unnecessarily, so discard the back-buffer.
mTile.DiscardBackBuffer();
}
mValidRegion = aNewValidRegion;
mLastPaintSurfaceMode = mode;
mLastPaintContentType = content;
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,135 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
#define MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
#include "TiledContentClient.h"
namespace mozilla {
namespace layers {
class ClientTiledPaintedLayer;
class ClientLayerManager;
/**
* Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
* This buffer provides an implementation of ValidateTile using a
* thebes callback and can support painting using a single paint buffer.
* Whether a single paint buffer is used is controlled by
* gfxPrefs::PerTileDrawing().
*/
class ClientSingleTiledLayerBuffer
: public ClientTiledLayerBuffer
, public TextureClientAllocator
{
virtual ~ClientSingleTiledLayerBuffer() {}
public:
ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
CompositableClient& aCompositableClient,
ClientLayerManager* aManager);
// TextureClientAllocator
already_AddRefed<TextureClient> GetTextureClient() override;
void ReturnTextureClientDeferred(TextureClient* aClient) override {}
void ReportClientLost() override {}
// ClientTiledLayerBuffer
void PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData,
bool aIsProgressive = false) override;
bool SupportsProgressiveUpdate() override { return false; }
bool ProgressiveUpdate(nsIntRegion& aValidRegion,
nsIntRegion& aInvalidRegion,
const nsIntRegion& aOldValidRegion,
BasicTiledLayerPaintData* aPaintData,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData) override
{
MOZ_ASSERT(false, "ProgressiveUpdate not supported!");
return false;
}
void ResetPaintedAndValidState() override {
mPaintedRegion.SetEmpty();
mValidRegion.SetEmpty();
mTile.DiscardBuffers();
}
const nsIntRegion& GetValidRegion() override {
return mValidRegion;
}
bool IsLowPrecision() const override {
return false;
}
void ReleaseTiles();
void DiscardBuffers();
SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
void ClearPaintedRegion() {
mPaintedRegion.SetEmpty();
}
private:
TileClient mTile;
nsIntRegion mPaintedRegion;
nsIntRegion mValidRegion;
bool mWasLastPaintProgressive;
/**
* While we're adding tiles, this is used to keep track of the position of
* the top-left of the top-left-most tile. When we come to wrap the tiles in
* TiledDrawTarget we subtract the value of this member from each tile's
* offset so that all the tiles have a positive offset, then add a
* translation to the TiledDrawTarget to compensate. This is important so
* that the mRect of the TiledDrawTarget is always at a positive x/y
* position, otherwise its GetSize() methods will be broken.
*/
gfx::IntPoint mTilingOrigin;
gfx::IntSize mSize;
gfxImageFormat mFormat;
};
class SingleTiledContentClient : public TiledContentClient
{
public:
SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
ClientLayerManager* aManager);
protected:
~SingleTiledContentClient()
{
MOZ_COUNT_DTOR(SingleTiledContentClient);
mTiledBuffer->ReleaseTiles();
}
public:
static bool ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager);
virtual void ClearCachedResources() override;
virtual void UpdatedBuffer(TiledBufferType aType) override;
virtual ClientTiledLayerBuffer* GetTiledBuffer() override { return mTiledBuffer; }
virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override { return nullptr; }
private:
RefPtr<ClientSingleTiledLayerBuffer> mTiledBuffer;
};
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,826 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TEXTURECLIENT_H
#define MOZILLA_GFX_TEXTURECLIENT_H
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint8_t, uint64_t
#include "GLTextureImage.h" // for TextureImage
#include "ImageTypes.h" // for StereoMode
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/Attributes.h" // for override
#include "mozilla/DebugOnly.h"
#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
#include "mozilla/gfx/2D.h" // for DrawTarget
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Types.h" // for SurfaceFormat
#include "mozilla/ipc/Shmem.h" // for Shmem
#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/mozalloc.h" // for operator delete
#include "mozilla/gfx/CriticalSection.h"
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
#include "GfxTexturesReporter.h"
#include "pratom.h"
#include "nsThreadUtils.h"
class gfxImageSurface;
namespace mozilla {
// When defined, we track which pool the tile came from and test for
// any inconsistencies. This can be defined in release build as well.
#ifdef DEBUG
#define GFX_DEBUG_TRACK_CLIENTS_IN_POOL 1
#endif
namespace layers {
class AsyncTransactionWaiter;
class BufferTextureData;
class CompositableForwarder;
class KnowsCompositor;
class LayersIPCChannel;
class CompositableClient;
struct PlanarYCbCrData;
class Image;
class PTextureChild;
class TextureChild;
class TextureData;
class GPUVideoTextureData;
struct RawTextureBuffer;
class RawYCbCrTextureBuffer;
class TextureClient;
class ITextureClientRecycleAllocator;
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
class TextureClientPool;
#endif
class TextureForwarder;
class KeepAlive;
/**
* TextureClient is the abstraction that allows us to share data between the
* content and the compositor side.
*/
enum TextureAllocationFlags {
ALLOC_DEFAULT = 0,
ALLOC_CLEAR_BUFFER = 1 << 1, // Clear the buffer to whatever is best for the draw target
ALLOC_CLEAR_BUFFER_WHITE = 1 << 2, // explicit all white
ALLOC_CLEAR_BUFFER_BLACK = 1 << 3, // explicit all black
ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
// Allocate the texture for out-of-band content updates. This is mostly for
// TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
// surfaces when used on the main thread.
ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
// Disable any cross-device synchronization. This is also for TextureClientD3D11,
// and creates a texture without KeyedMutex.
ALLOC_MANUAL_SYNCHRONIZATION = 1 << 6,
// The texture is going to be updated using UpdateFromSurface and needs to support
// that call.
ALLOC_UPDATE_FROM_SURFACE = 1 << 7,
};
#ifdef XP_WIN
typedef void* SyncHandle;
#else
typedef uintptr_t SyncHandle;
#endif // XP_WIN
class SyncObject : public RefCounted<SyncObject>
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObject)
virtual ~SyncObject() { }
static already_AddRefed<SyncObject> CreateSyncObject(SyncHandle aHandle);
enum class SyncType {
D3D11,
};
virtual SyncType GetSyncType() = 0;
virtual void FinalizeFrame() = 0;
virtual bool IsSyncObjectValid() = 0;
protected:
SyncObject() { }
};
/**
* This class may be used to asynchronously receive an update when the content
* drawn to this texture client is available for reading in CPU memory. This
* can only be used on texture clients that support draw target creation.
*/
class TextureReadbackSink
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadbackSink)
public:
/**
* Callback function to implement in order to receive a DataSourceSurface
* containing the data read back from the texture client. This will always
* be called on the main thread, and this may not hold on to the
* DataSourceSurface beyond the execution of this function.
*/
virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0;
protected:
virtual ~TextureReadbackSink() {}
};
enum class BackendSelector
{
Content,
Canvas
};
/// Temporary object providing direct access to a Texture's memory.
///
/// see TextureClient::CanExposeMappedData() and TextureClient::BorrowMappedData().
struct MappedTextureData
{
uint8_t* data;
gfx::IntSize size;
int32_t stride;
gfx::SurfaceFormat format;
};
struct MappedYCbCrChannelData
{
uint8_t* data;
gfx::IntSize size;
int32_t stride;
int32_t skip;
bool CopyInto(MappedYCbCrChannelData& aDst);
};
struct MappedYCbCrTextureData {
MappedYCbCrChannelData y;
MappedYCbCrChannelData cb;
MappedYCbCrChannelData cr;
// Sad but because of how SharedPlanarYCbCrData is used we have to expose this for now.
uint8_t* metadata;
StereoMode stereoMode;
bool CopyInto(MappedYCbCrTextureData& aDst)
{
return y.CopyInto(aDst.y)
&& cb.CopyInto(aDst.cb)
&& cr.CopyInto(aDst.cr);
}
};
class ReadLockDescriptor;
// A class to help implement copy-on-write semantics for shared textures.
//
// A TextureClient/Host pair can opt into using a ReadLock by calling
// TextureClient::EnableReadLock. This will equip the TextureClient with a
// ReadLock object that will be automatically ReadLock()'ed by the texture itself
// when it is written into (see TextureClient::Unlock).
// A TextureReadLock's counter starts at 1 and is expected to be equal to 1 when the
// lock is destroyed. See ShmemTextureReadLock for explanations about why we use
// 1 instead of 0 as the initial state.
// TextureReadLock is mostly internally managed by the TextureClient/Host pair,
// and the compositable only has to forward it during updates. If an update message
// contains a null_t lock, it means that the texture was not written into on the
// content side, and there is no synchronization required on the compositor side
// (or it means that the texture pair did not opt into using ReadLocks).
// On the compositor side, the TextureHost can receive a ReadLock during a
// transaction, and will both ReadUnlock() it and drop it as soon as the shared
// data is available again for writing (the texture upload is done, or the compositor
// not reading the texture anymore). The lock is dropped to make sure it is
// ReadUnlock()'ed only once.
class TextureReadLock {
protected:
virtual ~TextureReadLock() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadLock)
virtual int32_t ReadLock() = 0;
virtual int32_t ReadUnlock() = 0;
virtual int32_t GetReadCount() = 0;
virtual bool IsValid() const = 0;
static already_AddRefed<TextureReadLock>
Create(LayersIPCChannel* aAllocator);
static already_AddRefed<TextureReadLock>
Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator);
virtual bool Serialize(ReadLockDescriptor& aOutput) = 0;
enum LockType {
TYPE_MEMORY,
TYPE_SHMEM
};
virtual LockType GetType() = 0;
protected:
NS_DECL_OWNINGTHREAD
};
#ifdef XP_WIN
class D3D11TextureData;
#endif
class TextureData {
public:
struct Info {
gfx::IntSize size;
gfx::SurfaceFormat format;
bool hasIntermediateBuffer;
bool hasSynchronization;
bool supportsMoz2D;
bool canExposeMappedData;
Info()
: format(gfx::SurfaceFormat::UNKNOWN)
, hasIntermediateBuffer(false)
, hasSynchronization(false)
, supportsMoz2D(false)
, canExposeMappedData(false)
{}
};
TextureData() { MOZ_COUNT_CTOR(TextureData); }
virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
virtual void FillInfo(TextureData::Info& aInfo) const = 0;
virtual bool Lock(OpenMode aMode) = 0;
virtual void Unlock() = 0;
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
virtual bool BorrowMappedData(MappedTextureData&) { return false; }
virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
virtual void Deallocate(LayersIPCChannel* aAllocator) = 0;
/// Depending on the texture's flags either Deallocate or Forget is called.
virtual void Forget(LayersIPCChannel* aAllocator) {}
virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
virtual TextureData*
CreateSimilar(LayersIPCChannel* aAllocator,
LayersBackend aLayersBackend,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
virtual bool ReadBack(TextureReadbackSink* aReadbackSink) { return false; }
virtual void SyncWithObject(SyncObject* aFence) {};
virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
#ifdef XP_WIN
virtual D3D11TextureData* AsD3D11TextureData() {
return nullptr;
}
#endif
virtual BufferTextureData* AsBufferTextureData() { return nullptr; }
virtual GPUVideoTextureData* AsGPUVideoTextureData() { return nullptr; }
};
/**
* TextureClient is a thin abstraction over texture data that need to be shared
* between the content process and the compositor process. It is the
* content-side half of a TextureClient/TextureHost pair. A corresponding
* TextureHost lives on the compositor-side.
*
* TextureClient's primary purpose is to present texture data in a way that is
* understood by the IPC system. There are two ways to use it:
* - Use it to serialize image data that is not IPC-friendly (most likely
* involving a copy into shared memory)
* - preallocate it and paint directly into it, which avoids copy but requires
* the painting code to be aware of TextureClient (or at least the underlying
* shared memory).
*
* There is always one and only one TextureClient per TextureHost, and the
* TextureClient/Host pair only owns one buffer of image data through its
* lifetime. This means that the lifetime of the underlying shared data
* matches the lifetime of the TextureClient/Host pair. It also means
* TextureClient/Host do not implement double buffering, which is the
* responsibility of the compositable (which would use two Texture pairs).
* In order to send several different buffers to the compositor side, use
* several TextureClients.
*/
class TextureClient
: public AtomicRefCountedWithFinalize<TextureClient>
{
public:
explicit TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
virtual ~TextureClient();
static already_AddRefed<TextureClient>
CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
// Creates and allocates a TextureClient usable with Moz2D.
static already_AddRefed<TextureClient>
CreateForDrawing(KnowsCompositor* aAllocator,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags flags = ALLOC_DEFAULT);
static already_AddRefed<TextureClient>
CreateFromSurface(KnowsCompositor* aAllocator,
gfx::SourceSurface* aSurface,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags);
// Creates and allocates a TextureClient supporting the YCbCr format.
static already_AddRefed<TextureClient>
CreateForYCbCr(KnowsCompositor* aAllocator,
gfx::IntSize aYSize,
gfx::IntSize aCbCrSize,
StereoMode aStereoMode,
YUVColorSpace aYUVColorSpace,
TextureFlags aTextureFlags);
// Creates and allocates a TextureClient (can be accessed through raw
// pointers).
static already_AddRefed<TextureClient>
CreateForRawBufferAccess(KnowsCompositor* aAllocator,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
gfx::BackendType aMoz2dBackend,
TextureFlags aTextureFlags,
TextureAllocationFlags flags = ALLOC_DEFAULT);
// Creates and allocates a TextureClient (can beaccessed through raw
// pointers) with a certain buffer size. It's unfortunate that we need this.
// providing format and sizes could let us do more optimization.
static already_AddRefed<TextureClient>
CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
size_t aSize,
YUVColorSpace aYUVColorSpace,
TextureFlags aTextureFlags);
// Creates and allocates a TextureClient of the same type.
already_AddRefed<TextureClient>
CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
/**
* Locks the shared data, allowing the caller to get access to it.
*
* Please always lock/unlock when accessing the shared data.
* If Lock() returns false, you should not attempt to access the shared data.
*/
bool Lock(OpenMode aMode);
void Unlock();
bool IsLocked() const { return mIsLocked; }
gfx::IntSize GetSize() const { return mInfo.size; }
gfx::SurfaceFormat GetFormat() const { return mInfo.format; }
/**
* Returns true if this texture has a synchronization mechanism (mutex, fence, etc.).
* Textures that do not implement synchronization should be immutable or should
* use immediate uploads (see TextureFlags in CompositorTypes.h)
* Even if a texture does not implement synchronization, Lock and Unlock need
* to be used appropriately since the latter are also there to map/numap data.
*/
bool HasSynchronization() const { return mInfo.hasSynchronization; }
/**
* Indicates whether the TextureClient implementation is backed by an
* in-memory buffer. The consequence of this is that locking the
* TextureClient does not contend with locking the texture on the host side.
*/
bool HasIntermediateBuffer() const { return mInfo.hasIntermediateBuffer; }
bool CanExposeDrawTarget() const { return mInfo.supportsMoz2D; }
bool CanExposeMappedData() const { return mInfo.canExposeMappedData; }
/**
* Returns a DrawTarget to draw into the TextureClient.
* This function should never be called when not on the main thread!
*
* This must never be called on a TextureClient that is not sucessfully locked.
* When called several times within one Lock/Unlock pair, this method should
* return the same DrawTarget.
* The DrawTarget is automatically flushed by the TextureClient when the latter
* is unlocked, and the DrawTarget that will be returned within the next
* lock/unlock pair may or may not be the same object.
* Do not keep references to the DrawTarget outside of the lock/unlock pair.
*
* This is typically used as follows:
*
* if (!texture->Lock(OpenMode::OPEN_READ_WRITE)) {
* return false;
* }
* {
* // Restrict this code's scope to ensure all references to dt are gone
* // when Unlock is called.
* DrawTarget* dt = texture->BorrowDrawTarget();
* // use the draw target ...
* }
* texture->Unlock();
*
*/
gfx::DrawTarget* BorrowDrawTarget();
/**
* Similar to BorrowDrawTarget but provides direct access to the texture's bits
* instead of a DrawTarget.
*/
bool BorrowMappedData(MappedTextureData&);
bool BorrowMappedYCbCrData(MappedYCbCrTextureData&);
/**
* This function can be used to update the contents of the TextureClient
* off the main thread.
*/
void UpdateFromSurface(gfx::SourceSurface* aSurface);
/**
* This method is strictly for debugging. It causes locking and
* needless copies.
*/
already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
/**
* Copies a rectangle from this texture client to a position in aTarget.
* It is assumed that the necessary locks are in place; so this should at
* least have a read lock and aTarget should at least have a write lock.
*/
bool CopyToTextureClient(TextureClient* aTarget,
const gfx::IntRect* aRect,
const gfx::IntPoint* aPoint);
/**
* Allocate and deallocate a TextureChild actor.
*
* TextureChild is an implementation detail of TextureClient that is not
* exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
* are for use with the managing IPDL protocols only (so that they can
* implement AllocPextureChild and DeallocPTextureChild).
*/
static PTextureChild* CreateIPDLActor();
static bool DestroyIPDLActor(PTextureChild* actor);
/**
* Get the TextureClient corresponding to the actor passed in parameter.
*/
static already_AddRefed<TextureClient> AsTextureClient(PTextureChild* actor);
/**
* TextureFlags contain important information about various aspects
* of the texture, like how its liferime is managed, and how it
* should be displayed.
* See TextureFlags in CompositorTypes.h.
*/
TextureFlags GetFlags() const { return mFlags; }
bool HasFlags(TextureFlags aFlags) const
{
return (mFlags & aFlags) == aFlags;
}
void AddFlags(TextureFlags aFlags);
void RemoveFlags(TextureFlags aFlags);
// Must not be called when TextureClient is in use by CompositableClient.
void RecycleTexture(TextureFlags aFlags);
/**
* After being shared with the compositor side, an immutable texture is never
* modified, it can only be read. It is safe to not Lock/Unlock immutable
* textures.
*/
bool IsImmutable() const { return !!(mFlags & TextureFlags::IMMUTABLE); }
void MarkImmutable() { AddFlags(TextureFlags::IMMUTABLE); }
bool IsSharedWithCompositor() const;
/**
* If this method returns false users of TextureClient are not allowed
* to access the shared data.
*/
bool IsValid() const { return !!mData; }
/**
* Called when TextureClient is added to CompositableClient.
*/
void SetAddedToCompositableClient();
/**
* If this method retuns false, TextureClient is already added to CompositableClient,
* since its creation or recycling.
*/
bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; }
/**
* Create and init the TextureChild/Parent IPDL actor pair
* with a CompositableForwarder.
*
* Should be called only once per TextureClient.
* The TextureClient must not be locked when calling this method.
*/
bool InitIPDLActor(CompositableForwarder* aForwarder);
/**
* Create and init the TextureChild/Parent IPDL actor pair
* with a TextureForwarder.
*
* Should be called only once per TextureClient.
* The TextureClient must not be locked when calling this method.
*/
bool InitIPDLActor(KnowsCompositor* aForwarder);
/**
* Return a pointer to the IPDLActor.
*
* This is to be used with IPDL messages only. Do not store the returned
* pointer.
*/
PTextureChild* GetIPDLActor();
/**
* Triggers the destruction of the shared data and the corresponding TextureHost.
*
* If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction
* will be synchronously coordinated with the compositor side, otherwise it
* will be done asynchronously.
* If sync is true, the destruction will be synchronous regardless of the
* texture's flags (bad for performance, use with care).
*/
void Destroy(bool sync = false);
/**
* Track how much of this texture is wasted.
* For example we might allocate a 256x256 tile but only use 10x10.
*/
void SetWaste(int aWasteArea) {
mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
}
/**
* This sets the readback sink that this texture is to use. This will
* receive the data for this texture as soon as it becomes available after
* texture unlock.
*/
virtual void SetReadbackSink(TextureReadbackSink* aReadbackSink) {
mReadbackSink = aReadbackSink;
}
void SyncWithObject(SyncObject* aFence) { mData->SyncWithObject(aFence); }
LayersIPCChannel* GetAllocator() { return mAllocator; }
ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
/// If you add new code that uses this method, you are probably doing something wrong.
TextureData* GetInternalData() { return mData; }
const TextureData* GetInternalData() const { return mData; }
uint64_t GetSerial() const { return mSerial; }
void CancelWaitForRecycle();
/**
* Set last transaction id of CompositableForwarder.
*
* Called when TextureClient has TextureFlags::RECYCLE flag.
* When CompositableForwarder forwards the TextureClient with
* TextureFlags::RECYCLE, it holds TextureClient's ref until host side
* releases it. The host side sends TextureClient release message.
* The id is used to check if the message is for the last TextureClient
* forwarding.
*/
void SetLastFwdTransactionId(uint64_t aTransactionId)
{
MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
mFwdTransactionId = aTransactionId;
}
uint64_t GetLastFwdTransactionId() { return mFwdTransactionId; }
void EnableReadLock();
TextureReadLock* GetReadLock() { return mReadLock; }
bool IsReadLocked() const;
void SerializeReadLock(ReadLockDescriptor& aDescriptor);
private:
static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
// Internal helpers for creating texture clients using the actual forwarder instead
// of KnowsCompositor. TextureClientPool uses these to let it cache texture clients
// per-process instead of per ShadowLayerForwarder, but everyone else should
// use the public functions instead.
friend class TextureClientPool;
static already_AddRefed<TextureClient>
CreateForDrawing(TextureForwarder* aAllocator,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
LayersBackend aLayersBackend,
int32_t aMaxTextureSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
static already_AddRefed<TextureClient>
CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
gfx::BackendType aMoz2dBackend,
LayersBackend aLayersBackend,
TextureFlags aTextureFlags,
TextureAllocationFlags flags = ALLOC_DEFAULT);
/**
* Called once, during the destruction of the Texture, on the thread in which
* texture's reference count reaches 0 (could be any thread).
*
* Here goes the shut-down code that uses virtual methods.
* Must only be called by Release().
*/
void Finalize() {}
friend class AtomicRefCountedWithFinalize<TextureClient>;
protected:
/**
* Should only be called *once* per texture, in TextureClient::InitIPDLActor.
* Some texture implementations rely on the fact that the descriptor will be
* deserialized.
* Calling ToSurfaceDescriptor again after it has already returned true,
* or never constructing a TextureHost with aDescriptor may result in a memory
* leak (see TextureClientD3D9 for example).
*/
bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
void LockActor() const;
void UnlockActor() const;
TextureData::Info mInfo;
RefPtr<LayersIPCChannel> mAllocator;
RefPtr<TextureChild> mActor;
RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
RefPtr<TextureReadLock> mReadLock;
TextureData* mData;
RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
TextureFlags mFlags;
gl::GfxTextureWasteTracker mWasteTracker;
OpenMode mOpenMode;
#ifdef DEBUG
uint32_t mExpectedDtRefs;
#endif
bool mIsLocked;
// This member tracks that the texture was written into until the update
// is sent to the compositor. We need this remember to lock mReadLock on
// behalf of the compositor just before sending the notification.
bool mUpdated;
// Used when TextureClient is recycled with TextureFlags::RECYCLE flag.
bool mAddedToCompositableClient;
bool mWorkaroundAnnoyingSharedSurfaceLifetimeIssues;
bool mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
RefPtr<TextureReadbackSink> mReadbackSink;
uint64_t mFwdTransactionId;
// Serial id of TextureClient. It is unique in current process.
const uint64_t mSerial;
// Used to assign serial ids of TextureClient.
static mozilla::Atomic<uint64_t> sSerialCounter;
friend class TextureChild;
friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
public:
// Pointer to the pool this tile came from.
TextureClientPool* mPoolTracker;
#endif
};
/**
* Task that releases TextureClient pointer on a specified thread.
*/
class TextureClientReleaseTask : public Runnable
{
public:
explicit TextureClientReleaseTask(TextureClient* aClient)
: mTextureClient(aClient) {
}
NS_IMETHOD Run() override
{
mTextureClient = nullptr;
return NS_OK;
}
private:
RefPtr<TextureClient> mTextureClient;
};
// Automatically lock and unlock a texture. Since texture locking is fallible,
// Succeeded() must be checked on the guard object before proceeding.
class MOZ_RAII TextureClientAutoLock
{
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
public:
TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mTexture(aTexture),
mSucceeded(false)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mSucceeded = mTexture->Lock(aMode);
#ifdef DEBUG
mChecked = false;
#endif
}
~TextureClientAutoLock() {
MOZ_ASSERT(mChecked);
if (mSucceeded) {
mTexture->Unlock();
}
}
bool Succeeded() {
#ifdef DEBUG
mChecked = true;
#endif
return mSucceeded;
}
private:
TextureClient* mTexture;
#ifdef DEBUG
bool mChecked;
#endif
bool mSucceeded;
};
class KeepAlive
{
public:
virtual ~KeepAlive() {}
};
template<typename T>
class TKeepAlive : public KeepAlive
{
public:
explicit TKeepAlive(T* aData) : mData(aData) {}
protected:
RefPtr<T> mData;
};
/// Convenience function to set the content of ycbcr texture.
bool UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData);
} // namespace layers
} // namespace mozilla
#endif

View file

@ -0,0 +1,339 @@
/* -*- 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 "TextureClientPool.h"
#include "CompositableClient.h"
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/TextureForwarder.h"
#include "mozilla/layers/TiledContentClient.h"
#include "gfxPrefs.h"
#include "nsComponentManagerUtils.h"
#define TCP_LOG(...)
//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
namespace mozilla {
namespace layers {
// We want to shrink to our maximum size of N unused tiles
// after a timeout to allow for short-term budget requirements
static void
ShrinkCallback(nsITimer *aTimer, void *aClosure)
{
static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
}
// After a certain amount of inactivity, let's clear the pool so that
// we don't hold onto tiles needlessly. In general, allocations are
// cheap enough that re-allocating isn't an issue unless we're allocating
// at an inopportune time (e.g. mid-animation).
static void
ClearCallback(nsITimer *aTimer, void *aClosure)
{
static_cast<TextureClientPool*>(aClosure)->Clear();
}
TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
int32_t aMaxTextureSize,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
TextureFlags aFlags,
uint32_t aShrinkTimeoutMsec,
uint32_t aClearTimeoutMsec,
uint32_t aInitialPoolSize,
uint32_t aPoolUnusedSize,
TextureForwarder* aAllocator)
: mBackend(aLayersBackend)
, mMaxTextureSize(aMaxTextureSize)
, mFormat(aFormat)
, mSize(aSize)
, mFlags(aFlags)
, mShrinkTimeoutMsec(aShrinkTimeoutMsec)
, mClearTimeoutMsec(aClearTimeoutMsec)
, mInitialPoolSize(aInitialPoolSize)
, mPoolUnusedSize(aPoolUnusedSize)
, mOutstandingClients(0)
, mSurfaceAllocator(aAllocator)
, mDestroyed(false)
{
TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
this, mInitialPoolSize);
mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1");
mClearTimer = do_CreateInstance("@mozilla.org/timer;1");
if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
}
}
TextureClientPool::~TextureClientPool()
{
mShrinkTimer->Cancel();
mClearTimer->Cancel();
}
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
static bool TestClientPool(const char* what,
TextureClient* aClient,
TextureClientPool* aPool)
{
if (!aClient || !aPool) {
return false;
}
TextureClientPool* actual = aClient->mPoolTracker;
bool ok = (actual == aPool);
if (ok) {
ok = (aClient->GetFormat() == aPool->GetFormat());
}
if (!ok) {
if (actual) {
gfxCriticalError() << "Pool error(" << what << "): "
<< aPool << "-" << aPool->GetFormat() << ", "
<< actual << "-" << actual->GetFormat() << ", "
<< aClient->GetFormat();
MOZ_CRASH("GFX: Crashing with actual");
} else {
gfxCriticalError() << "Pool error(" << what << "): "
<< aPool << "-" << aPool->GetFormat() << ", nullptr, "
<< aClient->GetFormat();
MOZ_CRASH("GFX: Crashing without actual");
}
}
return ok;
}
#endif
already_AddRefed<TextureClient>
TextureClientPool::GetTextureClient()
{
// Try to fetch a client from the pool
RefPtr<TextureClient> textureClient;
// We initially allocate mInitialPoolSize for our pool. If we run
// out of TextureClients, we allocate additional TextureClients to try and keep around
// mPoolUnusedSize
if (!mTextureClients.size()) {
AllocateTextureClient();
}
if (!mTextureClients.size()) {
// All our allocations failed, return nullptr
return nullptr;
}
mOutstandingClients++;
textureClient = mTextureClients.top();
mTextureClients.pop();
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
if (textureClient) {
textureClient->mPoolTracker = this;
}
DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
MOZ_ASSERT(ok);
#endif
TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
return textureClient.forget();
}
void
TextureClientPool::AllocateTextureClient()
{
TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
this, mOutstandingClients);
RefPtr<TextureClient> newClient;
if (gfxPrefs::ForceShmemTiles()) {
// gfx::BackendType::NONE means use the content backend
newClient =
TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
mFormat, mSize,
gfx::BackendType::NONE,
mBackend,
mFlags, ALLOC_DEFAULT);
} else {
newClient =
TextureClient::CreateForDrawing(mSurfaceAllocator,
mFormat, mSize,
mBackend,
mMaxTextureSize,
BackendSelector::Content,
mFlags);
}
if (newClient) {
mTextureClients.push(newClient);
}
}
void
TextureClientPool::ResetTimers()
{
// Shrink down if we're beyond our maximum size
if (mShrinkTimeoutMsec &&
mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
nsITimer::TYPE_ONE_SHOT);
}
// Clear pool after a period of inactivity to reduce memory consumption
if (mClearTimeoutMsec) {
TCP_LOG("TexturePool %p scheduling a clear\n", this);
mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec,
nsITimer::TYPE_ONE_SHOT);
}
}
void
TextureClientPool::ReturnTextureClient(TextureClient *aClient)
{
if (!aClient || mDestroyed) {
return;
}
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
DebugOnly<bool> ok = TestClientPool("return", aClient, this);
MOZ_ASSERT(ok);
#endif
// Add the client to the pool:
MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
mOutstandingClients--;
mTextureClients.push(aClient);
TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
this, aClient, mTextureClients.size(), mOutstandingClients);
ResetTimers();
}
void
TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
{
if (!aClient || mDestroyed) {
return;
}
MOZ_ASSERT(aClient->GetReadLock());
#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
MOZ_ASSERT(ok);
#endif
mTextureClientsDeferred.push_back(aClient);
TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
ResetTimers();
}
void
TextureClientPool::ShrinkToMaximumSize()
{
// We're over our desired maximum size, immediately shrink down to the
// maximum.
//
// We cull from the deferred TextureClients first, as we can't reuse those
// until they get returned.
uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
// If we have > mInitialPoolSize outstanding, then we want to keep around
// mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
// outstanding, then keep around the entire initial pool size.
uint32_t targetUnusedClients;
if (mOutstandingClients > mInitialPoolSize) {
targetUnusedClients = mPoolUnusedSize;
} else {
targetUnusedClients = mInitialPoolSize;
}
TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
while (totalUnusedTextureClients > targetUnusedClients) {
if (mTextureClientsDeferred.size()) {
mOutstandingClients--;
TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
this, mTextureClientsDeferred.front().get(),
mTextureClientsDeferred.size() - 1);
mTextureClientsDeferred.pop_front();
} else {
TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
this, mTextureClients.top().get(), mTextureClients.size() - 1);
mTextureClients.pop();
}
totalUnusedTextureClients--;
}
}
void
TextureClientPool::ReturnDeferredClients()
{
if (mTextureClientsDeferred.empty()) {
return;
}
TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
this, mTextureClientsDeferred.size());
ReturnUnlockedClients();
ShrinkToMaximumSize();
}
void
TextureClientPool::ReturnUnlockedClients()
{
for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
// Last count is held by the lock itself.
if (!(*it)->IsReadLocked()) {
mTextureClients.push(*it);
it = mTextureClientsDeferred.erase(it);
MOZ_ASSERT(mOutstandingClients > 0);
mOutstandingClients--;
} else {
it++;
}
}
}
void
TextureClientPool::ReportClientLost()
{
MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
mOutstandingClients--;
TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
this, mOutstandingClients);
}
void
TextureClientPool::Clear()
{
TCP_LOG("TexturePool %p getting cleared\n", this);
while (!mTextureClients.empty()) {
TCP_LOG("TexturePool %p releasing client %p\n",
this, mTextureClients.top().get());
mTextureClients.pop();
}
while (!mTextureClientsDeferred.empty()) {
MOZ_ASSERT(mOutstandingClients > 0);
mOutstandingClients--;
TCP_LOG("TexturePool %p releasing deferred client %p\n",
this, mTextureClientsDeferred.front().get());
mTextureClientsDeferred.pop_front();
}
}
void TextureClientPool::Destroy()
{
Clear();
mDestroyed = true;
mInitialPoolSize = 0;
mPoolUnusedSize = 0;
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,180 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TEXTURECLIENTPOOL_H
#define MOZILLA_GFX_TEXTURECLIENTPOOL_H
#include "mozilla/gfx/Types.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/RefPtr.h"
#include "TextureClient.h"
#include "nsITimer.h"
#include <stack>
#include <list>
namespace mozilla {
namespace layers {
class ISurfaceAllocator;
class TextureForwarder;
class TextureReadLock;
class TextureClientAllocator
{
protected:
virtual ~TextureClientAllocator() {}
public:
NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator)
virtual already_AddRefed<TextureClient> GetTextureClient() = 0;
/**
* Return a TextureClient that is not yet ready to be reused, but will be
* imminently.
*/
virtual void ReturnTextureClientDeferred(TextureClient *aClient) = 0;
virtual void ReportClientLost() = 0;
};
class TextureClientPool final : public TextureClientAllocator
{
~TextureClientPool();
public:
TextureClientPool(LayersBackend aBackend,
int32_t aMaxTextureSize,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
TextureFlags aFlags,
uint32_t aShrinkTimeoutMsec,
uint32_t aClearTimeoutMsec,
uint32_t aInitialPoolSize,
uint32_t aPoolUnusedSize,
TextureForwarder* aAllocator);
/**
* Gets an allocated TextureClient of size and format that are determined
* by the initialisation parameters given to the pool. This will either be
* a cached client that was returned to the pool, or a newly allocated
* client if one isn't available.
*
* All clients retrieved by this method should be returned using the return
* functions, or reported lost so that the pool can manage its size correctly.
*/
already_AddRefed<TextureClient> GetTextureClient() override;
/**
* Return a TextureClient that is no longer being used and is ready for
* immediate re-use or destruction.
*/
void ReturnTextureClient(TextureClient *aClient);
/**
* Return a TextureClient that is not yet ready to be reused, but will be
* imminently.
*/
void ReturnTextureClientDeferred(TextureClient *aClient) override;
/**
* Return any clients to the pool that were previously returned in
* ReturnTextureClientDeferred.
*/
void ReturnDeferredClients();
/**
* Attempt to shrink the pool so that there are no more than
* mInitialPoolSize outstanding.
*/
void ShrinkToMaximumSize();
/**
* Report that a client retrieved via GetTextureClient() has become
* unusable, so that it will no longer be tracked.
*/
virtual void ReportClientLost() override;
/**
* Calling this will cause the pool to attempt to relinquish any unused
* clients.
*/
void Clear();
LayersBackend GetBackend() const { return mBackend; }
int32_t GetMaxTextureSize() const { return mMaxTextureSize; }
gfx::SurfaceFormat GetFormat() { return mFormat; }
TextureFlags GetFlags() const { return mFlags; }
/**
* Clear the pool and put it in a state where it won't recycle any new texture.
*/
void Destroy();
private:
void ReturnUnlockedClients();
/// Allocate a single TextureClient to be returned from the pool.
void AllocateTextureClient();
/// Reset and/or initialise timers for shrinking/clearing the pool.
void ResetTimers();
/// Backend passed to the TextureClient for buffer creation.
LayersBackend mBackend;
// Max texture size passed to the TextureClient for buffer creation.
int32_t mMaxTextureSize;
/// Format is passed to the TextureClient for buffer creation.
gfx::SurfaceFormat mFormat;
/// The width and height of the tiles to be used.
gfx::IntSize mSize;
/// Flags passed to the TextureClient for buffer creation.
const TextureFlags mFlags;
/// How long to wait after a TextureClient is returned before trying
/// to shrink the pool to its maximum size of mPoolUnusedSize.
uint32_t mShrinkTimeoutMsec;
/// How long to wait after a TextureClient is returned before trying
/// to clear the pool.
uint32_t mClearTimeoutMsec;
// The initial number of unused texture clients to seed the pool with
// on construction
uint32_t mInitialPoolSize;
// How many unused texture clients to try and keep around if we go over
// the initial allocation
uint32_t mPoolUnusedSize;
/// This is a total number of clients in the wild and in the stack of
/// deferred clients (see below). So, the total number of clients in
/// existence is always mOutstandingClients + the size of mTextureClients.
uint32_t mOutstandingClients;
// On b2g gonk, std::queue might be a better choice.
// On ICS, fence wait happens implicitly before drawing.
// Since JB, fence wait happens explicitly when fetching a client from the pool.
std::stack<RefPtr<TextureClient> > mTextureClients;
std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
RefPtr<nsITimer> mShrinkTimer;
RefPtr<nsITimer> mClearTimer;
// This mSurfaceAllocator owns us, so no need to hold a ref to it
TextureForwarder* mSurfaceAllocator;
// Keep track of whether this pool has been destroyed or not. If it has,
// we won't accept returns of TextureClients anymore, and the refcounting
// should take care of their destruction.
bool mDestroyed;
};
} // namespace layers
} // namespace mozilla
#endif /* MOZILLA_GFX_TEXTURECLIENTPOOL_H */

View file

@ -0,0 +1,279 @@
/* -*- 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 "gfxPlatform.h"
#include "ImageContainer.h"
#include "mozilla/layers/BufferTexture.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/TextureForwarder.h"
#include "TextureClientRecycleAllocator.h"
namespace mozilla {
namespace layers {
// Used to keep TextureClient's reference count stable as not to disrupt recycling.
class TextureClientHolder
{
~TextureClientHolder() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
explicit TextureClientHolder(TextureClient* aClient)
: mTextureClient(aClient)
, mWillRecycle(true)
{}
TextureClient* GetTextureClient()
{
return mTextureClient;
}
bool WillRecycle()
{
return mWillRecycle;
}
void ClearWillRecycle()
{
mWillRecycle = false;
}
void ClearTextureClient() { mTextureClient = nullptr; }
protected:
RefPtr<TextureClient> mTextureClient;
bool mWillRecycle;
};
class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
{
public:
DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocationFlags)
: ITextureClientAllocationHelper(aFormat,
aSize,
aSelector,
aTextureFlags,
aAllocationFlags)
, mAllocator(aAllocator)
{}
bool IsCompatible(TextureClient* aTextureClient) override
{
if (aTextureClient->GetFormat() != mFormat ||
aTextureClient->GetSize() != mSize) {
return false;
}
return true;
}
already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
{
return mAllocator->Allocate(mFormat,
mSize,
mSelector,
mTextureFlags,
mAllocationFlags);
}
protected:
TextureClientRecycleAllocator* mAllocator;
};
YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
TextureFlags aTextureFlags)
: ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
aData.mYSize,
BackendSelector::Content,
aTextureFlags,
ALLOC_DEFAULT)
, mData(aData)
{
}
bool
YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
{
MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
if (!bufferData ||
aTextureClient->GetSize() != mData.mYSize ||
bufferData->GetCbCrSize().isNothing() ||
bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
bufferData->GetYUVColorSpace().isNothing() ||
bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
bufferData->GetStereoMode().isNothing() ||
bufferData->GetStereoMode().ref() != mData.mStereoMode) {
return false;
}
return true;
}
already_AddRefed<TextureClient>
YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
{
return TextureClient::CreateForYCbCr(aAllocator,
mData.mYSize, mData.mCbCrSize,
mData.mStereoMode,
mData.mYUVColorSpace,
mTextureFlags);
}
TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
: mSurfaceAllocator(aAllocator)
, mMaxPooledSize(kMaxPooledSized)
, mLock("TextureClientRecycleAllocatorImp.mLock")
, mIsDestroyed(false)
{
}
TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
{
MutexAutoLock lock(mLock);
while (!mPooledClients.empty()) {
mPooledClients.pop();
}
MOZ_ASSERT(mInUseClients.empty());
}
void
TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
{
mMaxPooledSize = aMax;
}
already_AddRefed<TextureClient>
TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags)
{
MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
DefaultTextureClientAllocationHelper helper(this,
aFormat,
aSize,
aSelector,
aTextureFlags,
aAllocFlags);
return CreateOrRecycle(helper);
}
already_AddRefed<TextureClient>
TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
{
MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
RefPtr<TextureClientHolder> textureHolder;
{
MutexAutoLock lock(mLock);
if (mIsDestroyed) {
return nullptr;
}
if (!mPooledClients.empty()) {
textureHolder = mPooledClients.top();
mPooledClients.pop();
// If a pooled TextureClient is not compatible, release it.
if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
// Release TextureClient.
RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
textureHolder->ClearTextureClient();
textureHolder = nullptr;
mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
} else {
textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
}
}
}
if (!textureHolder) {
// Allocate new TextureClient
RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
if (!texture) {
return nullptr;
}
textureHolder = new TextureClientHolder(texture);
}
{
MutexAutoLock lock(mLock);
MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
// Register TextureClient
mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
}
RefPtr<TextureClient> client(textureHolder->GetTextureClient());
// Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
// ref count drops to 1.
client->SetRecycleAllocator(this);
return client.forget();
}
already_AddRefed<TextureClient>
TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags)
{
return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
aSelector, aTextureFlags, aAllocFlags);
}
void
TextureClientRecycleAllocator::ShrinkToMinimumSize()
{
MutexAutoLock lock(mLock);
while (!mPooledClients.empty()) {
mPooledClients.pop();
}
// We can not clear using TextureClients safely.
// Just clear WillRecycle here.
std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
RefPtr<TextureClientHolder> holder = it->second;
holder->ClearWillRecycle();
}
}
void
TextureClientRecycleAllocator::Destroy()
{
MutexAutoLock lock(mLock);
while (!mPooledClients.empty()) {
mPooledClients.pop();
}
mIsDestroyed = true;
}
void
TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
{
// Clearing the recycle allocator drops a reference, so make sure we stay alive
// for the duration of this function.
RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
aClient->SetRecycleAllocator(nullptr);
RefPtr<TextureClientHolder> textureHolder;
{
MutexAutoLock lock(mLock);
if (mInUseClients.find(aClient) != mInUseClients.end()) {
textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
if (textureHolder->WillRecycle() &&
!mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
mPooledClients.push(textureHolder);
}
mInUseClients.erase(aClient);
}
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,140 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
#define MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
#include <map>
#include <stack>
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/TextureForwarder.h"
#include "mozilla/RefPtr.h"
#include "TextureClient.h"
#include "mozilla/Mutex.h"
namespace mozilla {
namespace layers {
class TextureClientHolder;
struct PlanarYCbCrData;
class ITextureClientRecycleAllocator
{
protected:
virtual ~ITextureClientRecycleAllocator() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ITextureClientRecycleAllocator)
protected:
friend class TextureClient;
virtual void RecycleTextureClient(TextureClient* aClient) = 0;
};
class ITextureClientAllocationHelper
{
public:
ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocationFlags)
: mFormat(aFormat)
, mSize(aSize)
, mSelector(aSelector)
, mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag
, mAllocationFlags(aAllocationFlags)
{}
virtual already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) = 0;
virtual bool IsCompatible(TextureClient* aTextureClient) = 0;
const gfx::SurfaceFormat mFormat;
const gfx::IntSize mSize;
const BackendSelector mSelector;
const TextureFlags mTextureFlags;
const TextureAllocationFlags mAllocationFlags;
};
class YCbCrTextureClientAllocationHelper : public ITextureClientAllocationHelper
{
public:
YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
TextureFlags aTextureFlags);
bool IsCompatible(TextureClient* aTextureClient) override;
already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override;
protected:
const PlanarYCbCrData& mData;
};
/**
* TextureClientRecycleAllocator provides TextureClients allocation and
* recycling capabilities. It expects allocations of same sizes and
* attributres. If a recycled TextureClient is different from
* requested one, the recycled one is dropped and new TextureClient is allocated.
*
* By default this uses TextureClient::CreateForDrawing to allocate new texture
* clients.
*/
class TextureClientRecycleAllocator : public ITextureClientRecycleAllocator
{
protected:
virtual ~TextureClientRecycleAllocator();
public:
explicit TextureClientRecycleAllocator(KnowsCompositor* aAllocator);
void SetMaxPoolSize(uint32_t aMax);
// Creates and allocates a TextureClient.
already_AddRefed<TextureClient>
CreateOrRecycle(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags flags = ALLOC_DEFAULT);
already_AddRefed<TextureClient>
CreateOrRecycle(ITextureClientAllocationHelper& aHelper);
void ShrinkToMinimumSize();
void Destroy();
protected:
virtual already_AddRefed<TextureClient>
Allocate(gfx::SurfaceFormat aFormat,
gfx::IntSize aSize,
BackendSelector aSelector,
TextureFlags aTextureFlags,
TextureAllocationFlags aAllocFlags);
RefPtr<KnowsCompositor> mSurfaceAllocator;
friend class DefaultTextureClientAllocationHelper;
void RecycleTextureClient(TextureClient* aClient) override;
static const uint32_t kMaxPooledSized = 2;
uint32_t mMaxPooledSize;
std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
// On b2g gonk, std::queue might be a better choice.
// On ICS, fence wait happens implicitly before drawing.
// Since JB, fence wait happens explicitly when fetching a client from the pool.
// stack is good from Graphics cache usage point of view.
std::stack<RefPtr<TextureClientHolder> > mPooledClients;
Mutex mLock;
bool mIsDestroyed;
};
} // namespace layers
} // namespace mozilla
#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */

View file

@ -0,0 +1,96 @@
/* -*- 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 "TextureClientSharedSurface.h"
#include "GLContext.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h" // for gfxDebug
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/Unused.h"
#include "nsThreadUtils.h"
#include "SharedSurface.h"
using namespace mozilla::gl;
namespace mozilla {
namespace layers {
SharedSurfaceTextureData::SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf)
: mSurf(Move(surf))
{}
SharedSurfaceTextureData::~SharedSurfaceTextureData()
{}
void
SharedSurfaceTextureData::Deallocate(LayersIPCChannel*)
{}
void
SharedSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
{
aInfo.size = mSurf->mSize;
aInfo.format = gfx::SurfaceFormat::UNKNOWN;
aInfo.hasIntermediateBuffer = false;
aInfo.hasSynchronization = false;
aInfo.supportsMoz2D = false;
aInfo.canExposeMappedData = false;
}
bool
SharedSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
{
return mSurf->ToSurfaceDescriptor(&aOutDescriptor);
}
SharedSurfaceTextureClient::SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
TextureFlags aFlags,
LayersIPCChannel* aAllocator)
: TextureClient(aData, aFlags, aAllocator)
{
mWorkaroundAnnoyingSharedSurfaceLifetimeIssues = true;
}
already_AddRefed<SharedSurfaceTextureClient>
SharedSurfaceTextureClient::Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
LayersIPCChannel* aAllocator, TextureFlags aFlags)
{
if (!surf) {
return nullptr;
}
TextureFlags flags = aFlags | TextureFlags::RECYCLE | surf->GetTextureFlags();
SharedSurfaceTextureData* data = new SharedSurfaceTextureData(Move(surf));
return MakeAndAddRef<SharedSurfaceTextureClient>(data, flags, aAllocator);
}
SharedSurfaceTextureClient::~SharedSurfaceTextureClient()
{
// XXX - Things break when using the proper destruction handshake with
// SharedSurfaceTextureData because the TextureData outlives its gl
// context. Having a strong reference to the gl context creates a cycle.
// This needs to be fixed in a better way, though, because deleting
// the TextureData here can race with the compositor and cause flashing.
TextureData* data = mData;
mData = nullptr;
Destroy();
if (data) {
// Destroy mData right away without doing the proper deallocation handshake,
// because SharedSurface depends on things that may not outlive the texture's
// destructor so we can't wait until we know the compositor isn't using the
// texture anymore.
// It goes without saying that this is really bad and we should fix the bugs
// that block doing the right thing such as bug 1224199 sooner rather than
// later.
delete data;
}
}
} // namespace layers
} // namespace mozilla

View file

@ -0,0 +1,77 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
#define MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
#include <cstddef> // for size_t
#include <stdint.h> // for uint32_t, uint8_t, uint64_t
#include "GLContextTypes.h" // for GLContext (ptr only), etc
#include "TextureClient.h"
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Types.h" // for SurfaceFormat
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
namespace mozilla {
namespace gl {
class GLContext;
class SharedSurface;
class SurfaceFactory;
} // namespace gl
namespace layers {
class SharedSurfaceTextureClient;
class SharedSurfaceTextureData : public TextureData
{
protected:
const UniquePtr<gl::SharedSurface> mSurf;
friend class SharedSurfaceTextureClient;
explicit SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf);
public:
~SharedSurfaceTextureData();
virtual bool Lock(OpenMode) override { return false; }
virtual void Unlock() override {}
virtual void FillInfo(TextureData::Info& aInfo) const override;
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual void Deallocate(LayersIPCChannel*) override;
gl::SharedSurface* Surf() const { return mSurf.get(); }
};
class SharedSurfaceTextureClient : public TextureClient
{
public:
SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
TextureFlags aFlags,
LayersIPCChannel* aAllocator);
~SharedSurfaceTextureClient();
static already_AddRefed<SharedSurfaceTextureClient>
Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
LayersIPCChannel* aAllocator, TextureFlags aFlags);
gl::SharedSurface* Surf() const {
return static_cast<const SharedSurfaceTextureData*>(GetInternalData())->Surf();
}
};
} // namespace layers
} // namespace mozilla
#endif // MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,545 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TILEDCONTENTCLIENT_H
#define MOZILLA_GFX_TILEDCONTENTCLIENT_H
#include <stddef.h> // for size_t
#include <stdint.h> // for uint16_t
#include <algorithm> // for swap
#include <limits>
#include "Layers.h" // for LayerManager, etc
#include "TiledLayerBuffer.h" // for TiledLayerBuffer
#include "Units.h" // for CSSPoint
#include "gfxTypes.h"
#include "mozilla/Attributes.h" // for override
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/ipc/Shmem.h" // for Shmem
#include "mozilla/ipc/SharedMemory.h" // for SharedMemory
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
#include "mozilla/layers/LayersMessages.h" // for TileDescriptor
#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureClientPool.h"
#include "ClientLayerManager.h"
#include "mozilla/mozalloc.h" // for operator delete
#include "nsISupportsImpl.h" // for MOZ_COUNT_DTOR
#include "nsPoint.h" // for nsIntPoint
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsRegion.h" // for nsIntRegion
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
#include "nsExpirationTracker.h"
#include "mozilla/layers/ISurfaceAllocator.h"
namespace mozilla {
namespace layers {
class ClientTiledPaintedLayer;
class ClientLayerManager;
/**
* Represent a single tile in tiled buffer. The buffer keeps tiles,
* each tile keeps a reference to a texture client and a read-lock. This
* read-lock is used to help implement a copy-on-write mechanism. The tile
* should be locked before being sent to the compositor. The compositor should
* unlock the read-lock as soon as it has finished with the buffer in the
* TextureHost to prevent more textures being created than is necessary.
* Ideal place to store per tile debug information.
*/
struct TileClient
{
// Placeholder
TileClient();
~TileClient();
TileClient(const TileClient& o);
TileClient& operator=(const TileClient& o);
bool operator== (const TileClient& o) const
{
return mFrontBuffer == o.mFrontBuffer;
}
bool operator!= (const TileClient& o) const
{
return mFrontBuffer != o.mFrontBuffer;
}
void SetTextureAllocator(TextureClientAllocator* aAllocator)
{
mAllocator = aAllocator;
}
bool IsPlaceholderTile() const
{
return mBackBuffer == nullptr && mFrontBuffer == nullptr;
}
void DiscardBuffers()
{
DiscardFrontBuffer();
DiscardBackBuffer();
}
nsExpirationState *GetExpirationState() { return &mExpirationState; }
TileDescriptor GetTileDescriptor();
/**
* For debugging.
*/
void Dump(std::stringstream& aStream);
/**
* Swaps the front and back buffers.
*/
void Flip();
void DumpTexture(std::stringstream& aStream, TextureDumpMode aCompress) {
// TODO We should combine the OnWhite/OnBlack here an just output a single image.
CompositableClient::DumpTextureClient(aStream, mFrontBuffer, aCompress);
}
/**
* Returns an unlocked TextureClient that can be used for writing new
* data to the tile. This may flip the front-buffer to the back-buffer if
* the front-buffer is still locked by the host, or does not have an
* internal buffer (and so will always be locked).
*
* If getting the back buffer required copying pixels from the front buffer
* then the copied region is stored in aAddPaintedRegion so the host side
* knows to upload it.
*
* If nullptr is returned, aTextureClientOnWhite is undefined.
*/
TextureClient* GetBackBuffer(CompositableClient&,
const nsIntRegion& aDirtyRegion,
gfxContentType aContent, SurfaceMode aMode,
nsIntRegion& aAddPaintedRegion,
RefPtr<TextureClient>* aTextureClientOnWhite);
void DiscardFrontBuffer();
void DiscardBackBuffer();
/* We wrap the back buffer in a class that disallows assignment
* so that we can track when ever it changes so that we can update
* the expiry tracker for expiring the back buffers */
class PrivateProtector {
public:
void Set(TileClient * container, RefPtr<TextureClient>);
void Set(TileClient * container, TextureClient*);
// Implicitly convert to TextureClient* because we can't chain
// implicit conversion that would happen on RefPtr<TextureClient>
operator TextureClient*() const { return mBuffer; }
RefPtr<TextureClient> operator ->() { return mBuffer; }
private:
PrivateProtector& operator=(const PrivateProtector &);
RefPtr<TextureClient> mBuffer;
} mBackBuffer;
RefPtr<TextureClient> mBackBufferOnWhite;
RefPtr<TextureClient> mFrontBuffer;
RefPtr<TextureClient> mFrontBufferOnWhite;
RefPtr<TextureClientAllocator> mAllocator;
gfx::IntRect mUpdateRect;
bool mWasPlaceholder;
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
TimeStamp mLastUpdate;
#endif
nsIntRegion mInvalidFront;
nsIntRegion mInvalidBack;
nsExpirationState mExpirationState;
private:
// Copies dirty pixels from the front buffer into the back buffer,
// and records the copied region in aAddPaintedRegion.
void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
nsIntRegion& aAddPaintedRegion);
};
/**
* This struct stores all the data necessary to perform a paint so that it
* doesn't need to be recalculated on every repeated transaction.
*/
struct BasicTiledLayerPaintData {
/*
* The scroll offset of the content from the nearest ancestor layer that
* represents scrollable content with a display port set.
*/
ParentLayerPoint mScrollOffset;
/*
* The scroll offset of the content from the nearest ancestor layer that
* represents scrollable content with a display port set, for the last
* layer update transaction.
*/
ParentLayerPoint mLastScrollOffset;
/*
* The transform matrix to go from this layer's Layer units to
* the scroll ancestor's ParentLayer units. The "scroll ancestor" is
* the closest ancestor layer which scrolls, and is used to obtain
* the composition bounds that are relevant for this layer.
*/
LayerToParentLayerMatrix4x4 mTransformToCompBounds;
/*
* The critical displayport of the content from the nearest ancestor layer
* that represents scrollable content with a display port set. isNothing()
* if a critical displayport is not set.
*/
Maybe<LayerIntRect> mCriticalDisplayPort;
/*
* The render resolution of the document that the content this layer
* represents is in.
*/
CSSToParentLayerScale2D mResolution;
/*
* The composition bounds of the layer, in Layer coordinates. This is
* used to make sure that tiled updates to regions that are visible to the
* user are grouped coherently.
*/
LayerRect mCompositionBounds;
/*
* Low precision updates are always executed a tile at a time in repeated
* transactions. This counter is set to 1 on the first transaction of a low
* precision update, and incremented for each subsequent transaction.
*/
uint16_t mLowPrecisionPaintCount;
/*
* Whether this is the first time this layer is painting
*/
bool mFirstPaint : 1;
/*
* Whether there is further work to complete this paint. This is used to
* determine whether or not to repeat the transaction when painting
* progressively.
*/
bool mPaintFinished : 1;
/*
* Whether or not there is an async transform animation active
*/
bool mHasTransformAnimation : 1;
/*
* Initializes/clears data to prepare for paint action.
*/
void ResetPaintData();
};
class SharedFrameMetricsHelper
{
public:
SharedFrameMetricsHelper();
~SharedFrameMetricsHelper();
/**
* This is called by the BasicTileLayer to determine if it is still interested
* in the update of this display-port to continue. We can return true here
* to abort the current update and continue with any subsequent ones. This
* is useful for slow-to-render pages when the display-port starts lagging
* behind enough that continuing to draw it is wasted effort.
*/
bool UpdateFromCompositorFrameMetrics(const LayerMetricsWrapper& aLayer,
bool aHasPendingNewThebesContent,
bool aLowPrecision,
AsyncTransform& aViewTransform);
/**
* Determines if the compositor's upcoming composition bounds has fallen
* outside of the contents display port. If it has then the compositor
* will start to checker board. Checker boarding is when the compositor
* tries to composite a tile and it is not available. Historically
* a tile with a checker board pattern was used. Now a blank tile is used.
*/
bool AboutToCheckerboard(const FrameMetrics& aContentMetrics,
const FrameMetrics& aCompositorMetrics);
private:
bool mLastProgressiveUpdateWasLowPrecision;
bool mProgressiveUpdateWasInDanger;
};
/**
* Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
* This buffer provides an implementation of ValidateTile using a
* thebes callback and can support painting using a single paint buffer.
* Whether a single paint buffer is used is controlled by
* gfxPrefs::PerTileDrawing().
*/
class ClientTiledLayerBuffer
{
public:
ClientTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
CompositableClient& aCompositableClient)
: mPaintedLayer(aPaintedLayer)
, mCompositableClient(aCompositableClient)
, mLastPaintContentType(gfxContentType::COLOR)
, mLastPaintSurfaceMode(SurfaceMode::SURFACE_OPAQUE)
, mWasLastPaintProgressive(false)
{}
virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData,
bool aIsProgressive = false) = 0;
virtual bool SupportsProgressiveUpdate() = 0;
virtual bool ProgressiveUpdate(nsIntRegion& aValidRegion,
nsIntRegion& aInvalidRegion,
const nsIntRegion& aOldValidRegion,
BasicTiledLayerPaintData* aPaintData,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData) = 0;
virtual void ResetPaintedAndValidState() = 0;
virtual const nsIntRegion& GetValidRegion() = 0;
virtual bool IsLowPrecision() const = 0;
virtual void Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml,
TextureDumpMode aCompress) {}
const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
void SetFrameResolution(const CSSToParentLayerScale2D& aResolution) { mFrameResolution = aResolution; }
bool HasFormatChanged() const;
protected:
void UnlockTile(TileClient& aTile);
gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
ClientTiledPaintedLayer& mPaintedLayer;
CompositableClient& mCompositableClient;
gfxContentType mLastPaintContentType;
SurfaceMode mLastPaintSurfaceMode;
CSSToParentLayerScale2D mFrameResolution;
bool mWasLastPaintProgressive;
};
class ClientMultiTiledLayerBuffer
: public TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>
, public ClientTiledLayerBuffer
{
friend class TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>;
public:
ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
CompositableClient& aCompositableClient,
ClientLayerManager* aManager,
SharedFrameMetricsHelper* aHelper);
void PaintThebes(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData,
bool aIsProgressive = false) override;
virtual bool SupportsProgressiveUpdate() override { return true; }
/**
* Performs a progressive update of a given tiled buffer.
* See ComputeProgressiveUpdateRegion below for parameter documentation.
*/
bool ProgressiveUpdate(nsIntRegion& aValidRegion,
nsIntRegion& aInvalidRegion,
const nsIntRegion& aOldValidRegion,
BasicTiledLayerPaintData* aPaintData,
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData) override;
void ResetPaintedAndValidState() override {
mPaintedRegion.SetEmpty();
mValidRegion.SetEmpty();
mTiles.mSize.width = 0;
mTiles.mSize.height = 0;
DiscardBuffers();
mRetainedTiles.Clear();
}
const nsIntRegion& GetValidRegion() override {
return TiledLayerBuffer::GetValidRegion();
}
bool IsLowPrecision() const override {
return TiledLayerBuffer::IsLowPrecision();
}
void Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml,
TextureDumpMode aCompress) override {
TiledLayerBuffer::Dump(aStream, aPrefix, aDumpHtml, aCompress);
}
void ReadLock();
void Release();
void DiscardBuffers();
SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
void SetResolution(float aResolution) {
if (mResolution == aResolution) {
return;
}
Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
mResolution = aResolution;
}
protected:
bool ValidateTile(TileClient& aTile,
const nsIntPoint& aTileRect,
const nsIntRegion& dirtyRect);
void Update(const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion);
TileClient GetPlaceholderTile() const { return TileClient(); }
private:
RefPtr<ClientLayerManager> mManager;
LayerManager::DrawPaintedLayerCallback mCallback;
void* mCallbackData;
// The region that will be made valid during Update(). Once Update() is
// completed then this is identical to mValidRegion.
nsIntRegion mNewValidRegion;
SharedFrameMetricsHelper* mSharedFrameMetricsHelper;
// When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
std::vector<gfx::Tile> mMoz2DTiles;
/**
* While we're adding tiles, this is used to keep track of the position of
* the top-left of the top-left-most tile. When we come to wrap the tiles in
* TiledDrawTarget we subtract the value of this member from each tile's
* offset so that all the tiles have a positive offset, then add a
* translation to the TiledDrawTarget to compensate. This is important so
* that the mRect of the TiledDrawTarget is always at a positive x/y
* position, otherwise its GetSize() methods will be broken.
*/
gfx::IntPoint mTilingOrigin;
/**
* Calculates the region to update in a single progressive update transaction.
* This employs some heuristics to update the most 'sensible' region to
* update at this point in time, and how large an update should be performed
* at once to maintain visual coherency.
*
* aInvalidRegion is the current invalid region.
* aOldValidRegion is the valid region of mTiledBuffer at the beginning of the
* current transaction.
* aRegionToPaint will be filled with the region to update. This may be empty,
* which indicates that there is no more work to do.
* aIsRepeated should be true if this function has already been called during
* this transaction.
*
* Returns true if it should be called again, false otherwise. In the case
* that aRegionToPaint is empty, this will return aIsRepeated for convenience.
*/
bool ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
const nsIntRegion& aOldValidRegion,
nsIntRegion& aRegionToPaint,
BasicTiledLayerPaintData* aPaintData,
bool aIsRepeated);
};
class TiledContentClient : public CompositableClient
{
public:
TiledContentClient(ClientLayerManager* aManager,
const char* aName = "")
: CompositableClient(aManager->AsShadowForwarder())
, mName(aName)
{}
protected:
~TiledContentClient()
{}
public:
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
virtual void Dump(std::stringstream& aStream,
const char* aPrefix="",
bool aDumpHtml=false,
TextureDumpMode aCompress=TextureDumpMode::Compress) override;
virtual TextureInfo GetTextureInfo() const override
{
return TextureInfo(CompositableType::CONTENT_TILED);
}
virtual ClientTiledLayerBuffer* GetTiledBuffer() = 0;
virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() = 0;
enum TiledBufferType {
TILED_BUFFER,
LOW_PRECISION_TILED_BUFFER
};
virtual void UpdatedBuffer(TiledBufferType aType) = 0;
private:
const char* mName;
};
/**
* An implementation of TiledContentClient that supports
* multiple tiles and a low precision buffer.
*/
class MultiTiledContentClient : public TiledContentClient
{
public:
MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
ClientLayerManager* aManager);
protected:
~MultiTiledContentClient()
{
MOZ_COUNT_DTOR(MultiTiledContentClient);
mTiledBuffer.DiscardBuffers();
mLowPrecisionTiledBuffer.DiscardBuffers();
}
public:
void ClearCachedResources() override;
void UpdatedBuffer(TiledBufferType aType) override;
ClientTiledLayerBuffer* GetTiledBuffer() override { return &mTiledBuffer; }
ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override {
if (mHasLowPrecision) {
return &mLowPrecisionTiledBuffer;
}
return nullptr;
}
private:
SharedFrameMetricsHelper mSharedFrameMetricsHelper;
ClientMultiTiledLayerBuffer mTiledBuffer;
ClientMultiTiledLayerBuffer mLowPrecisionTiledBuffer;
bool mHasLowPrecision;
};
} // namespace layers
} // namespace mozilla
#endif