Bug 1471639 - Move edge padding to the paint thread. r=nical
This commit ports over the last remaining operation for tiling that doesn't work
on the paint thread.
The difficult part for edge padding is that it is done outside of ValidateTile
so it doesn't have an associated CapturedTilePaintState to be added to as an
operation. We need it to be in the same paint state so that it's guaranteed
to be run after painting has finished.
This commit changes edge padding to instead be decided inside of ValidateTile
and either sent to the paint thread if there is OMTP or executed right away.
MozReview-Commit-ID: JDD4rH1fVwW
new file mode 100644
--- /dev/null
+++ b/gfx/layers/BufferEdgePad.cpp
@@ -0,0 +1,93 @@
+#include "BufferEdgePad.h"
+
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+void
+PadDrawTargetOutFromRegion(RefPtr<DrawTarget> aDrawTarget, nsIntRegion &aRegion)
+{
+ struct LockedBits {
+ uint8_t *data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ static int clamp(int x, int min, int max)
+ {
+ if (x < min)
+ x = min;
+ if (x > max)
+ x = max;
+ return x;
+ }
+
+ static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
+ {
+ if (src + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long src memcpy");
+ }
+ if (src < bitmap) {
+ MOZ_CRASH("GFX: short src memcpy");
+ }
+ if (dst + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long dst mempcy");
+ }
+ if (dst < bitmap) {
+ MOZ_CRASH("GFX: short dst mempcy");
+ }
+ }
+
+ static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
+ LockedBits *lb = static_cast<LockedBits*>(closure);
+ uint8_t *bitmap = lb->data;
+ const int bpp = gfx::BytesPerPixel(lb->format);
+ const int stride = lb->stride;
+ const int width = lb->size.width;
+ const int height = lb->size.height;
+
+ if (side == VisitSide::TOP) {
+ if (y1 > 0) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ if (y1 < height) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::LEFT) {
+ if (x1 > 0) {
+ while (y1 != y2) {
+ memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ } else if (side == VisitSide::RIGHT) {
+ if (x1 < width) {
+ while (y1 != y2) {
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ }
+
+ }
+ } lb;
+
+ if (aDrawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
+ // we can only pad software targets so if we can't lock the bits don't pad
+ aRegion.VisitEdges(lb.visitor, &lb);
+ aDrawTarget->ReleaseBits(lb.data);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/BufferEdgePad.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_LAYERS_BUFFER_EDGE_PAD_H
+#define MOZILLA_LAYERS_BUFFER_EDGE_PAD_H
+
+#include "mozilla/gfx/2D.h"
+#include "nsRegion.h"
+
+namespace mozilla {
+namespace layers {
+
+void PadDrawTargetOutFromRegion(RefPtr<gfx::DrawTarget> aDrawTarget, nsIntRegion &aRegion);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_BUFFER_EDGE_PAD_H
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -7,16 +7,17 @@
#include "PaintThread.h"
#include <algorithm>
#include "base/task.h"
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "GeckoProfiler.h"
+#include "mozilla/layers/BufferEdgePad.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layers/SyncObject.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Preferences.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/SyncRunnable.h"
#include "nsIPropertyBag2.h"
@@ -96,16 +97,49 @@ CapturedTiledPaintState::Clear::ClearBuf
iter.Get().Width(), iter.Get().Height());
mTarget->ClearRect(drawRect);
}
}
mTarget->SetTransform(oldTransform);
}
+void
+CapturedTiledPaintState::EdgePad::EdgePadBuffer()
+{
+ PadDrawTargetOutFromRegion(mTarget, mValidRegion);
+}
+
+void
+CapturedTiledPaintState::PrePaint()
+{
+ for (auto& copy : mCopies) {
+ copy.CopyBuffer();
+ }
+
+ for (auto& clear : mClears) {
+ clear.ClearBuffer();
+ }
+}
+
+void
+CapturedTiledPaintState::Paint()
+{
+ mTarget->DrawCapturedDT(mCapture, Matrix());
+ mTarget->Flush();
+}
+
+void
+CapturedTiledPaintState::PostPaint()
+{
+ if (mEdgePad) {
+ mEdgePad->EdgePadBuffer();
+ }
+}
+
StaticAutoPtr<PaintThread> PaintThread::sSingleton;
StaticRefPtr<nsIThread> PaintThread::sThread;
PlatformThreadId PaintThread::sThreadId;
PaintThread::PaintThread()
{
}
@@ -414,30 +448,19 @@ void
PaintThread::AsyncPaintTiledContents(CompositorBridgeChild* aBridge,
CapturedTiledPaintState* aState)
{
AUTO_PROFILER_LABEL("PaintThread::AsyncPaintTiledContents", GRAPHICS);
MOZ_ASSERT(IsOnPaintWorkerThread());
MOZ_ASSERT(aState);
- for (auto& copy : aState->mCopies) {
- copy.CopyBuffer();
- }
-
- for (auto& clear : aState->mClears) {
- clear.ClearBuffer();
- }
-
- DrawTarget* target = aState->mTarget;
- DrawTargetCapture* capture = aState->mCapture;
-
- // Draw all the things into the actual dest target.
- target->DrawCapturedDT(capture, Matrix());
- target->Flush();
+ aState->PrePaint();
+ aState->Paint();
+ aState->PostPaint();
if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
// This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
// gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
// DrawTargets do not themselves hold on to UnscaledFonts.
NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
}
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -218,16 +218,29 @@ public:
void ClearBuffer();
RefPtr<gfx::DrawTarget> mTarget;
RefPtr<gfx::DrawTarget> mTargetOnWhite;
nsIntRegion mDirtyRegion;
};
+ struct EdgePad {
+ EdgePad(RefPtr<gfx::DrawTarget> aTarget,
+ nsIntRegion&& aValidRegion)
+ : mTarget(aTarget)
+ , mValidRegion(aValidRegion)
+ {}
+
+ void EdgePadBuffer();
+
+ RefPtr<gfx::DrawTarget> mTarget;
+ nsIntRegion mValidRegion;
+ };
+
CapturedTiledPaintState()
{}
CapturedTiledPaintState(gfx::DrawTarget* aTarget,
gfx::DrawTargetCapture* aCapture)
: mTarget(aTarget)
, mCapture(aCapture)
{}
@@ -239,20 +252,25 @@ public:
}
}
void DropTextureClients()
{
mClients.clear();
}
+ void PrePaint();
+ void Paint();
+ void PostPaint();
+
RefPtr<gfx::DrawTarget> mTarget;
RefPtr<gfx::DrawTargetCapture> mCapture;
std::vector<Copy> mCopies;
std::vector<Clear> mClears;
+ Maybe<EdgePad> mEdgePad;
std::vector<RefPtr<TextureClient>> mClients;
protected:
virtual ~CapturedTiledPaintState() {}
};
class CompositorBridgeChild;
--- a/gfx/layers/SourceSurfaceSharedData.cpp
+++ b/gfx/layers/SourceSurfaceSharedData.cpp
@@ -5,16 +5,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SourceSurfaceSharedData.h"
#include "mozilla/Likely.h"
#include "mozilla/Types.h" // for decltype
#include "mozilla/layers/SharedSurfacesChild.h"
+#include "base/process_util.h"
+
#ifdef DEBUG
/**
* If defined, this makes SourceSurfaceSharedData::Finalize memory protect the
* underlying shared buffer in the producing process (the content or UI
* process). Given flushing the page table is expensive, and its utility is
* predominantly diagnostic (in case of overrun), turn it off by default.
*/
#define SHARED_SURFACE_PROTECT_FINALIZED
--- a/gfx/layers/client/MultiTiledContentClient.cpp
+++ b/gfx/layers/client/MultiTiledContentClient.cpp
@@ -3,16 +3,17 @@
/* 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/MultiTiledContentClient.h"
#include "ClientTiledPaintedLayer.h"
#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/BufferEdgePad.h"
namespace mozilla {
using namespace gfx;
namespace layers {
MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
@@ -134,96 +135,16 @@ ClientMultiTiledLayerBuffer::PaintThebes
}
#endif
mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
mCallback = nullptr;
mCallbackData = nullptr;
}
-void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion ®ion)
-{
- struct LockedBits {
- uint8_t *data;
- IntSize size;
- int32_t stride;
- SurfaceFormat format;
- static int clamp(int x, int min, int max)
- {
- if (x < min)
- x = min;
- if (x > max)
- x = max;
- return x;
- }
-
- static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
- {
- if (src + n > bitmap + stride*height) {
- MOZ_CRASH("GFX: long src memcpy");
- }
- if (src < bitmap) {
- MOZ_CRASH("GFX: short src memcpy");
- }
- if (dst + n > bitmap + stride*height) {
- MOZ_CRASH("GFX: long dst mempcy");
- }
- if (dst < bitmap) {
- MOZ_CRASH("GFX: short dst mempcy");
- }
- }
-
- static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
- LockedBits *lb = static_cast<LockedBits*>(closure);
- uint8_t *bitmap = lb->data;
- const int bpp = gfx::BytesPerPixel(lb->format);
- const int stride = lb->stride;
- const int width = lb->size.width;
- const int height = lb->size.height;
-
- if (side == VisitSide::TOP) {
- if (y1 > 0) {
- x1 = clamp(x1, 0, width - 1);
- x2 = clamp(x2, 0, width - 1);
- ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
- memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
- }
- } else if (side == VisitSide::BOTTOM) {
- if (y1 < height) {
- x1 = clamp(x1, 0, width - 1);
- x2 = clamp(x2, 0, width - 1);
- ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
- memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
- }
- } else if (side == VisitSide::LEFT) {
- if (x1 > 0) {
- while (y1 != y2) {
- memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
- y1++;
- }
- }
- } else if (side == VisitSide::RIGHT) {
- if (x1 < width) {
- while (y1 != y2) {
- memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
- y1++;
- }
- }
- }
-
- }
- } lb;
-
- if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
- // we can only pad software targets so if we can't lock the bits don't pad
- region.VisitEdges(lb.visitor, &lb);
- drawTarget->ReleaseBits(lb.data);
- }
-}
-
void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
const nsIntRegion& aPaintRegion,
const nsIntRegion& aDirtyRegion,
TilePaintFlags aFlags)
{
const IntSize scaledTileSize = GetScaledTileSize();
const gfx::IntRect newBounds = newValidRegion.GetBounds();
@@ -279,16 +200,23 @@ void ClientMultiTiledLayerBuffer::Update
}
// Validating the tile may have required more to be painted.
paintRegion.OrWith(tileDrawRegion);
dirtyRegion.OrWith(tileDrawRegion);
}
if (!mPaintTiles.empty()) {
+ // Perform buffer copies and clears if we don't have the paint thread
+ if (!(aFlags & TilePaintFlags::Async)) {
+ for (const auto& state : mPaintStates) {
+ state->PrePaint();
+ }
+ }
+
// Create a tiled draw target
gfx::TileSet tileset;
for (size_t i = 0; i < mPaintTiles.size(); ++i) {
mPaintTiles[i].mTileOrigin -= mTilingOrigin;
}
tileset.mTiles = &mPaintTiles[0];
tileset.mTileCount = mPaintTiles.size();
RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
@@ -303,65 +231,37 @@ void ClientMultiTiledLayerBuffer::Update
MOZ_ASSERT(ctx); // already checked the draw target above
ctx->SetMatrix(
ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
ctx = nullptr;
+ // Dispatch to the paint thread or do any work left over
if (aFlags & TilePaintFlags::Async) {
for (const auto& state : mPaintStates) {
PaintThread::Get()->PaintTiledContents(state);
}
mManager->SetQueuedAsyncPaints();
- MOZ_ASSERT(mPaintStates.size() > 0);
- mPaintStates.clear();
} else {
- MOZ_ASSERT(mPaintStates.size() == 0);
+ for (const auto& state : mPaintStates) {
+ state->PostPaint();
+ }
}
+ mPaintStates.clear();
// Reset
mPaintTiles.clear();
mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
}
- bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
TileClient& tile = mRetainedTiles[i];
-
- // Only worry about padding when not doing low-res because it simplifies
- // the math and the artifacts won't be noticable
- // Edge padding prevents sampling artifacts when compositing.
- if (edgePaddingEnabled && mResolution == 1 &&
- tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
-
- const TileCoordIntPoint tileCoord = newTiles.TileCoord(i);
- IntPoint tileOffset = GetTileOffset(tileCoord);
- // Strictly speakig we want the unscaled rect here, but it doesn't matter
- // because we only run this code when the resolution is equal to 1.
- IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
- GetTileSize().width, GetTileSize().height);
-
- nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
- tileDrawRegion.AndWith(paintRegion);
-
- nsIntRegion tileValidRegion = mValidRegion;
- tileValidRegion.OrWith(tileDrawRegion);
-
- // We only need to pad out if the tile has area that's not valid
- if (!tileValidRegion.Contains(tileRect)) {
- tileValidRegion = tileValidRegion.Intersect(tileRect);
- // translate the region into tile space and pad
- tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
- RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
- PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
- }
- }
UnlockTile(tile);
}
}
mTiles = newTiles;
mValidRegion = newValidRegion;
}
@@ -456,61 +356,74 @@ ClientMultiTiledLayerBuffer::ValidateTil
RefPtr<DrawTarget> drawTarget;
if (dtOnWhite) {
drawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
} else {
drawTarget = dt;
}
+ // Create the paint operation that will be either dispatched to the paint
+ // thread, or executed synchronously
+ RefPtr<CapturedTiledPaintState> paintState = new CapturedTiledPaintState();
+
+ paintState->mCopies = std::move(asyncPaintCopies);
+
// We need to clear the dirty region of the tile before painting
// if we are painting non-opaque content
- Maybe<CapturedTiledPaintState::Clear> clear = Nothing();
if (mode != SurfaceMode::SURFACE_OPAQUE) {
- clear = Some(CapturedTiledPaintState::Clear{
+ auto clear = CapturedTiledPaintState::Clear{
dt,
dtOnWhite,
tileDirtyRegion
- });
+ };
+ paintState->mClears.push_back(clear);
}
- // Queue or execute the paint operation
+ // Only worry about padding when not doing low-res because it simplifies
+ // the math and the artifacts won't be noticable
+ // Edge padding prevents sampling artifacts when compositing.
+ if (gfxPrefs::TileEdgePaddingEnabled() && mResolution == 1) {
+ IntRect tileRect = IntRect(0, 0,
+ GetScaledTileSize().width,
+ GetScaledTileSize().height);
+
+ // We only need to pad out if the tile has area that's not valid
+ if (!tileVisibleRegion.Contains(tileRect)) {
+ tileVisibleRegion = tileVisibleRegion.Intersect(tileRect);
+
+ paintState->mEdgePad = Some(CapturedTiledPaintState::EdgePad{
+ drawTarget,
+ std::move(tileVisibleRegion),
+ });
+ }
+ }
+
+ paintState->mClients = std::move(asyncPaintClients);
+ paintState->mClients.push_back(backBuffer);
+ if (backBufferOnWhite) {
+ paintState->mClients.push_back(backBufferOnWhite);
+ }
+
gfx::Tile paintTile;
paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
if (aFlags & TilePaintFlags::Async) {
- RefPtr<CapturedTiledPaintState> asyncPaint = new CapturedTiledPaintState();
-
RefPtr<DrawTargetCapture> captureDT =
Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(),
drawTarget->GetSize(),
drawTarget->GetFormat());
paintTile.mDrawTarget = captureDT;
- asyncPaint->mTarget = drawTarget;
- asyncPaint->mCapture = captureDT;
-
- asyncPaint->mCopies = std::move(asyncPaintCopies);
- if (clear) {
- asyncPaint->mClears.push_back(*clear);
- }
-
- asyncPaint->mClients = std::move(asyncPaintClients);
- asyncPaint->mClients.push_back(backBuffer);
- if (backBufferOnWhite) {
- asyncPaint->mClients.push_back(backBufferOnWhite);
- }
-
- mPaintStates.push_back(asyncPaint);
+ paintState->mTarget = drawTarget;
+ paintState->mCapture = captureDT;
} else {
paintTile.mDrawTarget = drawTarget;
- if (clear) {
- clear->ClearBuffer();
- }
}
+ mPaintStates.push_back(paintState);
mPaintTiles.push_back(paintTile);
mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
// The new buffer is now validated, remove the dirty region from it.
aTile.mInvalidBack.SubOut(tileDirtyRegion);
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -120,16 +120,17 @@ EXPORTS.mozilla.layers += [
'AsyncCanvasRenderer.h',
'AtomicRefCountedWithFinalize.h',
'AxisPhysicsModel.h',
'AxisPhysicsMSDModel.h',
'basic/BasicCompositor.h',
'basic/MacIOSurfaceTextureHostBasic.h',
'basic/TextureHostBasic.h',
'BSPTree.h',
+ 'BufferEdgePad.h',
'BufferTexture.h',
'CanvasRenderer.h',
'client/CanvasClient.h',
'client/CompositableClient.h',
'client/ContentClient.h',
'client/GPUVideoTextureClient.h',
'client/ImageClient.h',
'client/MultiTiledContentClient.h',
@@ -345,16 +346,17 @@ UNIFIED_SOURCES += [
'basic/BasicCompositor.cpp',
'basic/BasicContainerLayer.cpp',
'basic/BasicImages.cpp',
'basic/BasicLayerManager.cpp',
'basic/BasicLayersImpl.cpp',
'basic/BasicPaintedLayer.cpp',
'basic/TextureHostBasic.cpp',
'BSPTree.cpp',
+ 'BufferEdgePad.cpp',
'BufferTexture.cpp',
'BufferUnrotate.cpp',
'CanvasRenderer.cpp',
'client/CanvasClient.cpp',
'client/ClientCanvasLayer.cpp',
'client/ClientCanvasRenderer.cpp',
'client/ClientColorLayer.cpp',
'client/ClientContainerLayer.cpp',