summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-25 00:09:14 +0000
committerjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-25 00:09:14 +0000
commit94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679 (patch)
tree530f51d5c75459999e4adf2a6895884ce1c15ce0
parent56235947f2b023fc63cfad692c56df4e92199848 (diff)
downloadchromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.zip
chromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.tar.gz
chromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.tar.bz2
Here are gyp targets and stubs for compiling libcc and the webkit_compositor bindings in chromium. Everything is guarded behind the off-by-default use_libcc_for_compositor gyp variable. I haven't included the actual code here, but there are scripts to sync. I plan to land + manually sync the code into place until we're ready to flip the gyp switch.
Snapshot from r126652 BUG= Review URL: https://chromiumcodereview.appspot.com/10828381 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153354 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--PRESUBMIT.py1
-rw-r--r--build/all.gyp1
-rw-r--r--cc/BitmapCanvasLayerTextureUpdater.cpp89
-rw-r--r--cc/BitmapCanvasLayerTextureUpdater.h57
-rw-r--r--cc/BitmapSkPictureCanvasLayerTextureUpdater.cpp84
-rw-r--r--cc/BitmapSkPictureCanvasLayerTextureUpdater.h45
-rw-r--r--cc/CCActiveAnimation.cpp207
-rw-r--r--cc/CCActiveAnimation.h161
-rw-r--r--cc/CCActiveAnimationTest.cpp208
-rw-r--r--cc/CCActiveGestureAnimation.cpp44
-rw-r--r--cc/CCActiveGestureAnimation.h36
-rw-r--r--cc/CCAnimationCurve.cpp23
-rw-r--r--cc/CCAnimationCurve.h56
-rw-r--r--cc/CCAnimationEvents.h38
-rw-r--r--cc/CCCheckerboardDrawQuad.cpp28
-rw-r--r--cc/CCCheckerboardDrawQuad.h28
-rw-r--r--cc/CCCompletionEvent.h65
-rw-r--r--cc/CCDamageTracker.cpp345
-rw-r--r--cc/CCDamageTracker.h62
-rw-r--r--cc/CCDamageTrackerTest.cpp1138
-rw-r--r--cc/CCDebugBorderDrawQuad.cpp32
-rw-r--r--cc/CCDebugBorderDrawQuad.h35
-rw-r--r--cc/CCDebugRectHistory.cpp116
-rw-r--r--cc/CCDebugRectHistory.h83
-rw-r--r--cc/CCDelayBasedTimeSource.cpp220
-rw-r--r--cc/CCDelayBasedTimeSource.h78
-rw-r--r--cc/CCDelayBasedTimeSourceTest.cpp385
-rw-r--r--cc/CCDirectRenderer.cpp212
-rw-r--r--cc/CCDirectRenderer.h106
-rw-r--r--cc/CCDrawQuad.cpp85
-rw-r--r--cc/CCDrawQuad.h94
-rw-r--r--cc/CCFontAtlas.cpp70
-rw-r--r--cc/CCFontAtlas.h65
-rw-r--r--cc/CCFrameRateController.cpp142
-rw-r--r--cc/CCFrameRateController.h76
-rw-r--r--cc/CCFrameRateControllerTest.cpp169
-rw-r--r--cc/CCFrameRateCounter.cpp130
-rw-r--r--cc/CCFrameRateCounter.h71
-rw-r--r--cc/CCGestureCurve.h31
-rw-r--r--cc/CCGraphicsContext.h21
-rw-r--r--cc/CCHeadsUpDisplayLayerImpl.cpp285
-rw-r--r--cc/CCHeadsUpDisplayLayerImpl.h56
-rw-r--r--cc/CCIOSurfaceDrawQuad.cpp30
-rw-r--r--cc/CCIOSurfaceDrawQuad.h42
-rw-r--r--cc/CCIOSurfaceLayerImpl.cpp112
-rw-r--r--cc/CCIOSurfaceLayerImpl.h43
-rw-r--r--cc/CCInputHandler.h85
-rw-r--r--cc/CCKeyframedAnimationCurve.cpp221
-rw-r--r--cc/CCKeyframedAnimationCurve.h111
-rw-r--r--cc/CCKeyframedAnimationCurveTest.cpp208
-rw-r--r--cc/CCLayerAnimationController.cpp407
-rw-r--r--cc/CCLayerAnimationController.h112
-rw-r--r--cc/CCLayerAnimationControllerTest.cpp562
-rw-r--r--cc/CCLayerImpl.cpp626
-rw-r--r--cc/CCLayerImpl.h390
-rw-r--r--cc/CCLayerImplTest.cpp160
-rw-r--r--cc/CCLayerIterator.cpp150
-rw-r--r--cc/CCLayerIterator.h210
-rw-r--r--cc/CCLayerIteratorTest.cpp255
-rw-r--r--cc/CCLayerQuad.cpp66
-rw-r--r--cc/CCLayerQuad.h112
-rw-r--r--cc/CCLayerQuadTest.cpp45
-rw-r--r--cc/CCLayerSorter.cpp422
-rw-r--r--cc/CCLayerSorter.h88
-rw-r--r--cc/CCLayerSorterTest.cpp267
-rw-r--r--cc/CCLayerTilingData.cpp149
-rw-r--r--cc/CCLayerTilingData.h98
-rw-r--r--cc/CCLayerTreeHost.cpp752
-rw-r--r--cc/CCLayerTreeHost.h318
-rw-r--r--cc/CCLayerTreeHostCommon.cpp888
-rw-r--r--cc/CCLayerTreeHostCommon.h85
-rw-r--r--cc/CCLayerTreeHostCommonTest.cpp3247
-rw-r--r--cc/CCLayerTreeHostImpl.cpp1277
-rw-r--r--cc/CCLayerTreeHostImpl.h295
-rw-r--r--cc/CCLayerTreeHostImplTest.cpp4117
-rw-r--r--cc/CCLayerTreeHostTest.cpp2671
-rw-r--r--cc/CCMathUtil.cpp379
-rw-r--r--cc/CCMathUtil.h109
-rw-r--r--cc/CCMathUtilTest.cpp182
-rw-r--r--cc/CCOcclusionTracker.cpp482
-rw-r--r--cc/CCOcclusionTracker.h103
-rw-r--r--cc/CCOcclusionTrackerTest.cpp3005
-rw-r--r--cc/CCOverdrawMetrics.cpp190
-rw-r--r--cc/CCOverdrawMetrics.h97
-rw-r--r--cc/CCPageScaleAnimation.cpp159
-rw-r--r--cc/CCPageScaleAnimation.h74
-rw-r--r--cc/CCPrioritizedTexture.cpp122
-rw-r--r--cc/CCPrioritizedTexture.h121
-rw-r--r--cc/CCPrioritizedTextureManager.cpp339
-rw-r--r--cc/CCPrioritizedTextureManager.h119
-rw-r--r--cc/CCPrioritizedTextureTest.cpp456
-rw-r--r--cc/CCPriorityCalculator.cpp72
-rw-r--r--cc/CCPriorityCalculator.h33
-rw-r--r--cc/CCProxy.cpp106
-rw-r--r--cc/CCProxy.h129
-rw-r--r--cc/CCQuadCuller.cpp95
-rw-r--r--cc/CCQuadCuller.h40
-rw-r--r--cc/CCQuadCullerTest.cpp470
-rw-r--r--cc/CCQuadSink.h29
-rw-r--r--cc/CCRenderPass.cpp92
-rw-r--r--cc/CCRenderPass.h91
-rw-r--r--cc/CCRenderPassDrawQuad.cpp36
-rw-r--r--cc/CCRenderPassDrawQuad.h48
-rw-r--r--cc/CCRenderSurface.cpp213
-rw-r--r--cc/CCRenderSurface.h124
-rw-r--r--cc/CCRenderSurfaceFilters.cpp443
-rw-r--r--cc/CCRenderSurfaceFilters.h34
-rw-r--r--cc/CCRenderSurfaceFiltersTest.cpp141
-rw-r--r--cc/CCRenderSurfaceTest.cpp116
-rw-r--r--cc/CCRenderer.h90
-rw-r--r--cc/CCRendererGL.cpp1509
-rw-r--r--cc/CCRendererGL.h245
-rw-r--r--cc/CCRenderingStats.h30
-rw-r--r--cc/CCResourceProvider.cpp529
-rw-r--r--cc/CCResourceProvider.h289
-rw-r--r--cc/CCResourceProviderTest.cpp536
-rw-r--r--cc/CCScheduler.cpp191
-rw-r--r--cc/CCScheduler.h107
-rw-r--r--cc/CCSchedulerStateMachine.cpp316
-rw-r--r--cc/CCSchedulerStateMachine.h163
-rw-r--r--cc/CCSchedulerStateMachineTest.cpp1045
-rw-r--r--cc/CCSchedulerTest.cpp472
-rw-r--r--cc/CCScopedTexture.cpp51
-rw-r--r--cc/CCScopedTexture.h44
-rw-r--r--cc/CCScopedTextureTest.cpp108
-rw-r--r--cc/CCScopedThreadProxy.h71
-rw-r--r--cc/CCScrollbarAnimationController.cpp92
-rw-r--r--cc/CCScrollbarAnimationController.h64
-rw-r--r--cc/CCScrollbarAnimationControllerLinearFade.cpp78
-rw-r--r--cc/CCScrollbarAnimationControllerLinearFade.h39
-rw-r--r--cc/CCScrollbarAnimationControllerLinearFadeTest.cpp120
-rw-r--r--cc/CCScrollbarLayerImpl.cpp193
-rw-r--r--cc/CCScrollbarLayerImpl.h109
-rw-r--r--cc/CCSettings.cpp33
-rw-r--r--cc/CCSettings.h31
-rw-r--r--cc/CCSharedQuadState.cpp28
-rw-r--r--cc/CCSharedQuadState.h32
-rw-r--r--cc/CCSingleThreadProxy.cpp340
-rw-r--r--cc/CCSingleThreadProxy.h122
-rw-r--r--cc/CCSolidColorDrawQuad.cpp32
-rw-r--r--cc/CCSolidColorDrawQuad.h33
-rw-r--r--cc/CCSolidColorLayerImpl.cpp49
-rw-r--r--cc/CCSolidColorLayerImpl.h34
-rw-r--r--cc/CCSolidColorLayerImplTest.cpp92
-rw-r--r--cc/CCStreamVideoDrawQuad.cpp29
-rw-r--r--cc/CCStreamVideoDrawQuad.h36
-rw-r--r--cc/CCTexture.cpp36
-rw-r--r--cc/CCTexture.h42
-rw-r--r--cc/CCTextureDrawQuad.cpp36
-rw-r--r--cc/CCTextureDrawQuad.h41
-rw-r--r--cc/CCTextureLayerImpl.cpp79
-rw-r--r--cc/CCTextureLayerImpl.h48
-rw-r--r--cc/CCTextureUpdateController.cpp168
-rw-r--r--cc/CCTextureUpdateController.h59
-rw-r--r--cc/CCTextureUpdateControllerTest.cpp668
-rw-r--r--cc/CCTextureUpdateQueue.cpp64
-rw-r--r--cc/CCTextureUpdateQueue.h45
-rw-r--r--cc/CCThread.h41
-rw-r--r--cc/CCThreadProxy.cpp892
-rw-r--r--cc/CCThreadProxy.h180
-rw-r--r--cc/CCThreadTask.h305
-rw-r--r--cc/CCThreadTaskTest.cpp45
-rw-r--r--cc/CCThreadedTest.cpp622
-rw-r--r--cc/CCThreadedTest.h207
-rw-r--r--cc/CCTileDrawQuad.cpp39
-rw-r--r--cc/CCTileDrawQuad.h54
-rw-r--r--cc/CCTiledLayerImpl.cpp219
-rw-r--r--cc/CCTiledLayerImpl.h61
-rw-r--r--cc/CCTiledLayerImplTest.cpp243
-rw-r--r--cc/CCTimeSource.h39
-rw-r--r--cc/CCTimer.cpp79
-rw-r--r--cc/CCTimer.h42
-rw-r--r--cc/CCTimerTest.cpp63
-rw-r--r--cc/CCTimingFunction.cpp76
-rw-r--r--cc/CCTimingFunction.h64
-rw-r--r--cc/CCVideoLayerImpl.cpp388
-rw-r--r--cc/CCVideoLayerImpl.h89
-rw-r--r--cc/CCYUVVideoDrawQuad.cpp30
-rw-r--r--cc/CCYUVVideoDrawQuad.h34
-rw-r--r--cc/CanvasLayerTextureUpdater.cpp70
-rw-r--r--cc/CanvasLayerTextureUpdater.h39
-rw-r--r--cc/ContentLayerChromium.cpp107
-rw-r--r--cc/ContentLayerChromium.h74
-rw-r--r--cc/DEPS7
-rw-r--r--cc/FrameBufferSkPictureCanvasLayerTextureUpdater.cpp114
-rw-r--r--cc/FrameBufferSkPictureCanvasLayerTextureUpdater.h52
-rw-r--r--cc/GeometryBinding.cpp61
-rw-r--r--cc/GeometryBinding.h48
-rw-r--r--cc/HeadsUpDisplayLayerChromium.cpp70
-rw-r--r--cc/HeadsUpDisplayLayerChromium.h36
-rw-r--r--cc/IOSurfaceLayerChromium.cpp56
-rw-r--r--cc/IOSurfaceLayerChromium.h38
-rw-r--r--cc/ImageLayerChromium.cpp161
-rw-r--r--cc/ImageLayerChromium.h48
-rw-r--r--cc/LayerChromium.cpp717
-rw-r--r--cc/LayerChromium.h381
-rw-r--r--cc/LayerPainterChromium.h27
-rw-r--r--cc/LayerTextureSubImage.cpp109
-rw-r--r--cc/LayerTextureSubImage.h46
-rw-r--r--cc/LayerTextureUpdater.h63
-rw-r--r--cc/PlatformColor.h58
-rw-r--r--cc/ProgramBinding.cpp149
-rw-r--r--cc/ProgramBinding.h84
-rw-r--r--cc/README19
-rw-r--r--cc/RateLimiter.cpp86
-rw-r--r--cc/RateLimiter.h53
-rw-r--r--cc/RenderSurfaceChromium.cpp43
-rw-r--r--cc/RenderSurfaceChromium.h99
-rw-r--r--cc/ScrollbarLayerChromium.cpp261
-rw-r--r--cc/ScrollbarLayerChromium.h66
-rw-r--r--cc/ShaderChromium.cpp884
-rw-r--r--cc/ShaderChromium.h351
-rw-r--r--cc/SkPictureCanvasLayerTextureUpdater.cpp47
-rw-r--r--cc/SkPictureCanvasLayerTextureUpdater.h48
-rw-r--r--cc/SolidColorLayerChromium.cpp36
-rw-r--r--cc/SolidColorLayerChromium.h32
-rw-r--r--cc/TextureCopier.cpp101
-rw-r--r--cc/TextureCopier.h68
-rw-r--r--cc/TextureLayerChromium.cpp133
-rw-r--r--cc/TextureLayerChromium.h90
-rw-r--r--cc/TextureUploader.h30
-rw-r--r--cc/ThrottledTextureUploader.cpp118
-rw-r--r--cc/ThrottledTextureUploader.h68
-rw-r--r--cc/TiledLayerChromium.cpp791
-rw-r--r--cc/TiledLayerChromium.h106
-rw-r--r--cc/TreeSynchronizer.cpp112
-rw-r--r--cc/TreeSynchronizer.h41
-rw-r--r--cc/UnthrottledTextureUploader.h33
-rw-r--r--cc/VideoLayerChromium.cpp37
-rw-r--r--cc/VideoLayerChromium.h40
-rw-r--r--cc/cc.gyp241
-rw-r--r--cc/cc_tests.gyp105
-rw-r--r--cc/copyfiles.py78
-rw-r--r--cc/stubs/Extensions3D.h26
-rw-r--r--cc/stubs/Extensions3DChromium.h55
-rw-r--r--cc/stubs/GraphicsContext3D.h76
-rw-r--r--cc/stubs/GraphicsTypes3D.h18
-rw-r--r--cc/stubs/NotImplemented.h10
-rw-r--r--cc/stubs/Region.h10
-rw-r--r--cc/stubs/SkiaUtils.h17
-rw-r--r--cc/stubs/TextStream.h23
-rw-r--r--cc/stubs/TilingData.h10
-rw-r--r--cc/stubs/TraceEvent.h10
-rw-r--r--cc/stubs/UnitBezier.h6
-rw-r--r--cc/test/CCAnimationTestCommon.cpp156
-rw-r--r--cc/test/CCAnimationTestCommon.h90
-rw-r--r--cc/test/CCLayerTestCommon.cpp36
-rw-r--r--cc/test/CCLayerTestCommon.h19
-rw-r--r--cc/test/CCLayerTreeTestCommon.h40
-rw-r--r--cc/test/CCOcclusionTrackerTestCommon.h37
-rw-r--r--cc/test/CCSchedulerTestCommon.h133
-rw-r--r--cc/test/CCTestCommon.h22
-rw-r--r--cc/test/CCTiledLayerTestCommon.cpp120
-rw-r--r--cc/test/CCTiledLayerTestCommon.h145
-rw-r--r--cc/test/CompositorFakeWebGraphicsContext3D.h36
-rw-r--r--cc/test/FakeCCGraphicsContext.h22
-rw-r--r--cc/test/FakeCCLayerTreeHostClient.h39
-rw-r--r--cc/test/FakeGraphicsContext3DTest.cpp36
-rw-r--r--cc/test/FakeWebCompositorOutputSurface.h57
-rw-r--r--cc/test/FakeWebGraphicsContext3D.h260
-rw-r--r--cc/test/FakeWebScrollbarThemeGeometry.h48
-rw-r--r--cc/test/MockCCQuadCuller.h58
-rw-r--r--cc/test/run_all_unittests.cc18
-rw-r--r--webkit/compositor/CCThreadImpl.cpp98
-rw-r--r--webkit/compositor/CCThreadImpl.h34
-rw-r--r--webkit/compositor/PlatformGestureCurve.h29
-rw-r--r--webkit/compositor/PlatformGestureCurveTarget.h23
-rw-r--r--webkit/compositor/TouchpadFlingPlatformGestureCurve.h45
-rw-r--r--webkit/compositor/WebAnimationCurveCommon.cpp33
-rw-r--r--webkit/compositor/WebAnimationCurveCommon.h19
-rw-r--r--webkit/compositor/WebAnimationImpl.cpp109
-rw-r--r--webkit/compositor/WebAnimationImpl.h43
-rw-r--r--webkit/compositor/WebCompositorImpl.cpp98
-rw-r--r--webkit/compositor/WebCompositorImpl.h41
-rw-r--r--webkit/compositor/WebCompositorInputHandlerImpl.cpp341
-rw-r--r--webkit/compositor/WebCompositorInputHandlerImpl.h85
-rw-r--r--webkit/compositor/WebContentLayerImpl.cpp71
-rw-r--r--webkit/compositor/WebContentLayerImpl.h41
-rw-r--r--webkit/compositor/WebExternalTextureLayerImpl.cpp111
-rw-r--r--webkit/compositor/WebExternalTextureLayerImpl.h43
-rw-r--r--webkit/compositor/WebFloatAnimationCurveImpl.cpp62
-rw-r--r--webkit/compositor/WebFloatAnimationCurveImpl.h42
-rw-r--r--webkit/compositor/WebIOSurfaceLayerImpl.cpp41
-rw-r--r--webkit/compositor/WebIOSurfaceLayerImpl.h33
-rw-r--r--webkit/compositor/WebImageLayerImpl.cpp39
-rw-r--r--webkit/compositor/WebImageLayerImpl.h33
-rw-r--r--webkit/compositor/WebLayerImpl.cpp372
-rw-r--r--webkit/compositor/WebLayerImpl.h89
-rw-r--r--webkit/compositor/WebLayerTreeViewImpl.cpp255
-rw-r--r--webkit/compositor/WebLayerTreeViewImpl.h72
-rw-r--r--webkit/compositor/WebScrollbarLayerImpl.cpp44
-rw-r--r--webkit/compositor/WebScrollbarLayerImpl.h34
-rw-r--r--webkit/compositor/WebSolidColorLayerImpl.cpp41
-rw-r--r--webkit/compositor/WebSolidColorLayerImpl.h35
-rw-r--r--webkit/compositor/WebTransformAnimationCurveImpl.cpp61
-rw-r--r--webkit/compositor/WebTransformAnimationCurveImpl.h42
-rw-r--r--webkit/compositor/WebVideoLayerImpl.cpp37
-rw-r--r--webkit/compositor/WebVideoLayerImpl.h33
-rw-r--r--webkit/compositor/WheelFlingPlatformGestureCurve.h38
-rw-r--r--webkit/compositor/compositor.gyp79
-rw-r--r--webkit/compositor/copyfiles.py69
-rw-r--r--webkit/compositor/stubs/public/WebTransformationMatrix.h13
302 files changed, 55916 insertions, 5 deletions
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 0ae66e7..e99161c 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -22,6 +22,7 @@ _EXCLUDED_PATHS = (
r"^v8[\\\/].*",
r".*MakeFile$",
r".+_autogen\.h$",
+ r"^cc[\\\/].*",
)
diff --git a/build/all.gyp b/build/all.gyp
index 947b43d..2db7070 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -31,6 +31,7 @@
# as gyp files come online.
['OS!="ios"', {
'dependencies': [
+ '../cc/cc_tests.gyp:*',
'../chrome/chrome.gyp:*',
'../content/content.gyp:*',
'../gpu/gpu.gyp:*',
diff --git a/cc/BitmapCanvasLayerTextureUpdater.cpp b/cc/BitmapCanvasLayerTextureUpdater.cpp
new file mode 100644
index 0000000..240543f
--- /dev/null
+++ b/cc/BitmapCanvasLayerTextureUpdater.cpp
@@ -0,0 +1,89 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BitmapCanvasLayerTextureUpdater.h"
+
+#include "LayerPainterChromium.h"
+#include "PlatformColor.h"
+#include "skia/ext/platform_canvas.h"
+
+namespace WebCore {
+
+BitmapCanvasLayerTextureUpdater::Texture::Texture(BitmapCanvasLayerTextureUpdater* textureUpdater, PassOwnPtr<CCPrioritizedTexture> texture)
+ : LayerTextureUpdater::Texture(texture)
+ , m_textureUpdater(textureUpdater)
+{
+}
+
+BitmapCanvasLayerTextureUpdater::Texture::~Texture()
+{
+}
+
+void BitmapCanvasLayerTextureUpdater::Texture::updateRect(CCResourceProvider* resourceProvider, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ textureUpdater()->updateTextureRect(resourceProvider, texture(), sourceRect, destOffset);
+}
+
+PassRefPtr<BitmapCanvasLayerTextureUpdater> BitmapCanvasLayerTextureUpdater::create(PassOwnPtr<LayerPainterChromium> painter)
+{
+ return adoptRef(new BitmapCanvasLayerTextureUpdater(painter));
+}
+
+BitmapCanvasLayerTextureUpdater::BitmapCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium> painter)
+ : CanvasLayerTextureUpdater(painter)
+ , m_opaque(false)
+{
+}
+
+BitmapCanvasLayerTextureUpdater::~BitmapCanvasLayerTextureUpdater()
+{
+}
+
+PassOwnPtr<LayerTextureUpdater::Texture> BitmapCanvasLayerTextureUpdater::createTexture(CCPrioritizedTextureManager* manager)
+{
+ return adoptPtr(new Texture(this, CCPrioritizedTexture::create(manager)));
+}
+
+LayerTextureUpdater::SampledTexelFormat BitmapCanvasLayerTextureUpdater::sampledTexelFormat(GC3Denum textureFormat)
+{
+ // The component order may be bgra if we uploaded bgra pixels to rgba textures.
+ return PlatformColor::sameComponentOrder(textureFormat) ?
+ LayerTextureUpdater::SampledTexelFormatRGBA : LayerTextureUpdater::SampledTexelFormatBGRA;
+}
+
+void BitmapCanvasLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize& tileSize, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats& stats)
+{
+ if (m_canvasSize != contentRect.size()) {
+ m_canvasSize = contentRect.size();
+ m_canvas = adoptPtr(skia::CreateBitmapCanvas(m_canvasSize.width(), m_canvasSize.height(), m_opaque));
+ }
+
+ paintContents(m_canvas.get(), contentRect, contentsWidthScale, contentsHeightScale, resultingOpaqueRect, stats);
+}
+
+void BitmapCanvasLayerTextureUpdater::updateTextureRect(CCResourceProvider* resourceProvider, CCPrioritizedTexture* texture, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false);
+ bitmap.lockPixels();
+
+ texture->upload(resourceProvider, static_cast<const uint8_t*>(bitmap.getPixels()), contentRect(), sourceRect, destOffset);
+ bitmap.unlockPixels();
+}
+
+void BitmapCanvasLayerTextureUpdater::setOpaque(bool opaque)
+{
+ if (opaque != m_opaque) {
+ m_canvas.clear();
+ m_canvasSize = IntSize();
+ }
+ m_opaque = opaque;
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/BitmapCanvasLayerTextureUpdater.h b/cc/BitmapCanvasLayerTextureUpdater.h
new file mode 100644
index 0000000..233c83b
--- /dev/null
+++ b/cc/BitmapCanvasLayerTextureUpdater.h
@@ -0,0 +1,57 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef BitmapCanvasLayerTextureUpdater_h
+#define BitmapCanvasLayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CanvasLayerTextureUpdater.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+class LayerPainterChromium;
+
+// This class rasterizes the contentRect into a skia bitmap canvas. It then updates
+// textures by copying from the canvas into the texture, using MapSubImage if
+// possible.
+class BitmapCanvasLayerTextureUpdater : public CanvasLayerTextureUpdater {
+public:
+ class Texture : public LayerTextureUpdater::Texture {
+ public:
+ Texture(BitmapCanvasLayerTextureUpdater*, PassOwnPtr<CCPrioritizedTexture>);
+ virtual ~Texture();
+
+ virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) OVERRIDE;
+
+ private:
+ BitmapCanvasLayerTextureUpdater* textureUpdater() { return m_textureUpdater; }
+
+ BitmapCanvasLayerTextureUpdater* m_textureUpdater;
+ };
+
+ static PassRefPtr<BitmapCanvasLayerTextureUpdater> create(PassOwnPtr<LayerPainterChromium>);
+ virtual ~BitmapCanvasLayerTextureUpdater();
+
+ virtual PassOwnPtr<LayerTextureUpdater::Texture> createTexture(CCPrioritizedTextureManager*) OVERRIDE;
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum textureFormat) OVERRIDE;
+ virtual void prepareToUpdate(const IntRect& contentRect, const IntSize& tileSize, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats&) OVERRIDE;
+ void updateTextureRect(CCResourceProvider*, CCPrioritizedTexture*, const IntRect& sourceRect, const IntSize& destOffset);
+
+ virtual void setOpaque(bool) OVERRIDE;
+
+private:
+ explicit BitmapCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
+
+ OwnPtr<SkCanvas> m_canvas;
+ IntSize m_canvasSize;
+ bool m_opaque;
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // BitmapCanvasLayerTextureUpdater_h
diff --git a/cc/BitmapSkPictureCanvasLayerTextureUpdater.cpp b/cc/BitmapSkPictureCanvasLayerTextureUpdater.cpp
new file mode 100644
index 0000000..1d0c913
--- /dev/null
+++ b/cc/BitmapSkPictureCanvasLayerTextureUpdater.cpp
@@ -0,0 +1,84 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "BitmapSkPictureCanvasLayerTextureUpdater.h"
+
+#include "CCRenderingStats.h"
+#include "LayerPainterChromium.h"
+#include "PlatformColor.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+BitmapSkPictureCanvasLayerTextureUpdater::Texture::Texture(BitmapSkPictureCanvasLayerTextureUpdater* textureUpdater, PassOwnPtr<CCPrioritizedTexture> texture)
+ : CanvasLayerTextureUpdater::Texture(texture)
+ , m_textureUpdater(textureUpdater)
+{
+}
+
+void BitmapSkPictureCanvasLayerTextureUpdater::Texture::prepareRect(const IntRect& sourceRect, CCRenderingStats& stats)
+{
+ m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, sourceRect.width(), sourceRect.height());
+ m_bitmap.allocPixels();
+ m_bitmap.setIsOpaque(m_textureUpdater->layerIsOpaque());
+ SkDevice device(m_bitmap);
+ SkCanvas canvas(&device);
+ double paintBeginTime = monotonicallyIncreasingTime();
+ textureUpdater()->paintContentsRect(&canvas, sourceRect, stats);
+ stats.totalPaintTimeInSeconds += monotonicallyIncreasingTime() - paintBeginTime;
+}
+
+void BitmapSkPictureCanvasLayerTextureUpdater::Texture::updateRect(CCResourceProvider* resourceProvider, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ m_bitmap.lockPixels();
+ texture()->upload(resourceProvider, static_cast<uint8_t*>(m_bitmap.getPixels()), sourceRect, sourceRect, destOffset);
+ m_bitmap.unlockPixels();
+ m_bitmap.reset();
+}
+
+PassRefPtr<BitmapSkPictureCanvasLayerTextureUpdater> BitmapSkPictureCanvasLayerTextureUpdater::create(PassOwnPtr<LayerPainterChromium> painter)
+{
+ return adoptRef(new BitmapSkPictureCanvasLayerTextureUpdater(painter));
+}
+
+BitmapSkPictureCanvasLayerTextureUpdater::BitmapSkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium> painter)
+ : SkPictureCanvasLayerTextureUpdater(painter)
+{
+}
+
+BitmapSkPictureCanvasLayerTextureUpdater::~BitmapSkPictureCanvasLayerTextureUpdater()
+{
+}
+
+PassOwnPtr<LayerTextureUpdater::Texture> BitmapSkPictureCanvasLayerTextureUpdater::createTexture(CCPrioritizedTextureManager* manager)
+{
+ return adoptPtr(new Texture(this, CCPrioritizedTexture::create(manager)));
+}
+
+LayerTextureUpdater::SampledTexelFormat BitmapSkPictureCanvasLayerTextureUpdater::sampledTexelFormat(GC3Denum textureFormat)
+{
+ // The component order may be bgra if we uploaded bgra pixels to rgba textures.
+ return PlatformColor::sameComponentOrder(textureFormat) ?
+ LayerTextureUpdater::SampledTexelFormatRGBA : LayerTextureUpdater::SampledTexelFormatBGRA;
+}
+
+void BitmapSkPictureCanvasLayerTextureUpdater::paintContentsRect(SkCanvas* canvas, const IntRect& sourceRect, CCRenderingStats& stats)
+{
+ // Translate the origin of contentRect to that of sourceRect.
+ canvas->translate(contentRect().x() - sourceRect.x(),
+ contentRect().y() - sourceRect.y());
+ double rasterizeBeginTime = monotonicallyIncreasingTime();
+ drawPicture(canvas);
+ stats.totalRasterizeTimeInSeconds += monotonicallyIncreasingTime() - rasterizeBeginTime;
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/BitmapSkPictureCanvasLayerTextureUpdater.h b/cc/BitmapSkPictureCanvasLayerTextureUpdater.h
new file mode 100644
index 0000000..4718d85
--- /dev/null
+++ b/cc/BitmapSkPictureCanvasLayerTextureUpdater.h
@@ -0,0 +1,45 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef BitmapSkPictureCanvasLayerTextureUpdater_h
+#define BitmapSkPictureCanvasLayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "SkBitmap.h"
+#include "SkPictureCanvasLayerTextureUpdater.h"
+
+namespace WebCore {
+
+// This class records the contentRect into an SkPicture, then software rasterizes
+// the SkPicture into bitmaps for each tile. This implements CCSettings::perTilePainting.
+class BitmapSkPictureCanvasLayerTextureUpdater : public SkPictureCanvasLayerTextureUpdater {
+public:
+ class Texture : public CanvasLayerTextureUpdater::Texture {
+ public:
+ Texture(BitmapSkPictureCanvasLayerTextureUpdater*, PassOwnPtr<CCPrioritizedTexture>);
+
+ virtual void prepareRect(const IntRect& sourceRect, CCRenderingStats&) OVERRIDE;
+ virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) OVERRIDE;
+
+ private:
+ BitmapSkPictureCanvasLayerTextureUpdater* textureUpdater() { return m_textureUpdater; }
+
+ SkBitmap m_bitmap;
+ BitmapSkPictureCanvasLayerTextureUpdater* m_textureUpdater;
+ };
+
+ static PassRefPtr<BitmapSkPictureCanvasLayerTextureUpdater> create(PassOwnPtr<LayerPainterChromium>);
+ virtual ~BitmapSkPictureCanvasLayerTextureUpdater();
+
+ virtual PassOwnPtr<LayerTextureUpdater::Texture> createTexture(CCPrioritizedTextureManager*) OVERRIDE;
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum textureFormat) OVERRIDE;
+ void paintContentsRect(SkCanvas*, const IntRect& sourceRect, CCRenderingStats&);
+
+private:
+ explicit BitmapSkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
+};
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // BitmapSkPictureCanvasLayerTextureUpdater_h
diff --git a/cc/CCActiveAnimation.cpp b/cc/CCActiveAnimation.cpp
new file mode 100644
index 0000000..87086a3
--- /dev/null
+++ b/cc/CCActiveAnimation.cpp
@@ -0,0 +1,207 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCActiveAnimation.h"
+
+#include "CCAnimationCurve.h"
+#include "TraceEvent.h"
+#include <cmath>
+#include <wtf/Assertions.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
+
+namespace {
+
+// This should match the RunState enum.
+static const char* const s_runStateNames[] = {
+ "WaitingForNextTick",
+ "WaitingForTargetAvailability",
+ "WaitingForStartTime",
+ "WaitingForDeletion",
+ "Running",
+ "Paused",
+ "Finished",
+ "Aborted"
+};
+
+COMPILE_ASSERT(static_cast<int>(WebCore::CCActiveAnimation::RunStateEnumSize) == WTF_ARRAY_LENGTH(s_runStateNames), RunState_names_match_enum);
+
+// This should match the TargetProperty enum.
+static const char* const s_targetPropertyNames[] = {
+ "Transform",
+ "Opacity"
+};
+
+COMPILE_ASSERT(static_cast<int>(WebCore::CCActiveAnimation::TargetPropertyEnumSize) == WTF_ARRAY_LENGTH(s_targetPropertyNames), TargetProperty_names_match_enum);
+
+} // namespace
+
+namespace WebCore {
+
+PassOwnPtr<CCActiveAnimation> CCActiveAnimation::create(PassOwnPtr<CCAnimationCurve> curve, int animationId, int groupId, TargetProperty targetProperty)
+{
+ return adoptPtr(new CCActiveAnimation(curve, animationId, groupId, targetProperty));
+}
+
+CCActiveAnimation::CCActiveAnimation(PassOwnPtr<CCAnimationCurve> curve, int animationId, int groupId, TargetProperty targetProperty)
+ : m_curve(curve)
+ , m_id(animationId)
+ , m_group(groupId)
+ , m_targetProperty(targetProperty)
+ , m_runState(WaitingForTargetAvailability)
+ , m_iterations(1)
+ , m_startTime(0)
+ , m_alternatesDirection(false)
+ , m_timeOffset(0)
+ , m_needsSynchronizedStartTime(false)
+ , m_suspended(false)
+ , m_pauseTime(0)
+ , m_totalPausedTime(0)
+ , m_isControllingInstance(false)
+{
+}
+
+CCActiveAnimation::~CCActiveAnimation()
+{
+ if (m_runState == Running || m_runState == Paused)
+ setRunState(Aborted, 0);
+}
+
+void CCActiveAnimation::setRunState(RunState runState, double monotonicTime)
+{
+ if (m_suspended)
+ return;
+
+ char nameBuffer[256];
+ snprintf(nameBuffer, sizeof(nameBuffer), "%s-%d%s", s_targetPropertyNames[m_targetProperty], m_group, m_isControllingInstance ? "(impl)" : "");
+
+ bool isWaitingToStart = m_runState == WaitingForNextTick
+ || m_runState == WaitingForTargetAvailability
+ || m_runState == WaitingForStartTime;
+
+ if (isWaitingToStart && runState == Running)
+ TRACE_EVENT_ASYNC_BEGIN1("cc", "CCActiveAnimation", this, "Name", TRACE_STR_COPY(nameBuffer));
+
+ bool wasFinished = isFinished();
+
+ const char* oldRunStateName = s_runStateNames[m_runState];
+
+ if (runState == Running && m_runState == Paused)
+ m_totalPausedTime += monotonicTime - m_pauseTime;
+ else if (runState == Paused)
+ m_pauseTime = monotonicTime;
+ m_runState = runState;
+
+ const char* newRunStateName = s_runStateNames[runState];
+
+ if (!wasFinished && isFinished())
+ TRACE_EVENT_ASYNC_END0("cc", "CCActiveAnimation", this);
+
+ char stateBuffer[256];
+ snprintf(stateBuffer, sizeof(stateBuffer), "%s->%s", oldRunStateName, newRunStateName);
+
+ TRACE_EVENT_INSTANT2("cc", "CCLayerAnimationController::setRunState", "Name", TRACE_STR_COPY(nameBuffer), "State", TRACE_STR_COPY(stateBuffer));
+}
+
+void CCActiveAnimation::suspend(double monotonicTime)
+{
+ setRunState(Paused, monotonicTime);
+ m_suspended = true;
+}
+
+void CCActiveAnimation::resume(double monotonicTime)
+{
+ m_suspended = false;
+ setRunState(Running, monotonicTime);
+}
+
+bool CCActiveAnimation::isFinishedAt(double monotonicTime) const
+{
+ if (isFinished())
+ return true;
+
+ if (m_needsSynchronizedStartTime)
+ return false;
+
+ return m_runState == Running
+ && m_iterations >= 0
+ && m_iterations * m_curve->duration() <= monotonicTime - startTime() - m_totalPausedTime;
+}
+
+double CCActiveAnimation::trimTimeToCurrentIteration(double monotonicTime) const
+{
+ double trimmed = monotonicTime + m_timeOffset;
+
+ // If we're paused, time is 'stuck' at the pause time.
+ if (m_runState == Paused)
+ trimmed = m_pauseTime;
+
+ // Returned time should always be relative to the start time and should subtract
+ // all time spent paused.
+ trimmed -= m_startTime + m_totalPausedTime;
+
+ // Zero is always the start of the animation.
+ if (trimmed <= 0)
+ return 0;
+
+ // Always return zero if we have no iterations.
+ if (!m_iterations)
+ return 0;
+
+ // If less than an iteration duration, just return trimmed.
+ if (trimmed < m_curve->duration())
+ return trimmed;
+
+ // If greater than or equal to the total duration, return iteration duration.
+ if (m_iterations >= 0 && trimmed >= m_curve->duration() * m_iterations) {
+ if (m_alternatesDirection && !(m_iterations % 2))
+ return 0;
+ return m_curve->duration();
+ }
+
+ // We need to know the current iteration if we're alternating.
+ int iteration = static_cast<int>(trimmed / m_curve->duration());
+
+ // Calculate x where trimmed = x + n * m_curve->duration() for some positive integer n.
+ trimmed = fmod(trimmed, m_curve->duration());
+
+ // If we're alternating and on an odd iteration, reverse the direction.
+ if (m_alternatesDirection && iteration % 2 == 1)
+ return m_curve->duration() - trimmed;
+
+ return trimmed;
+}
+
+PassOwnPtr<CCActiveAnimation> CCActiveAnimation::clone(InstanceType instanceType) const
+{
+ return cloneAndInitialize(instanceType, m_runState, m_startTime);
+}
+
+PassOwnPtr<CCActiveAnimation> CCActiveAnimation::cloneAndInitialize(InstanceType instanceType, RunState initialRunState, double startTime) const
+{
+ OwnPtr<CCActiveAnimation> toReturn(adoptPtr(new CCActiveAnimation(m_curve->clone(), m_id, m_group, m_targetProperty)));
+ toReturn->m_runState = initialRunState;
+ toReturn->m_iterations = m_iterations;
+ toReturn->m_startTime = startTime;
+ toReturn->m_pauseTime = m_pauseTime;
+ toReturn->m_totalPausedTime = m_totalPausedTime;
+ toReturn->m_timeOffset = m_timeOffset;
+ toReturn->m_alternatesDirection = m_alternatesDirection;
+ toReturn->m_isControllingInstance = instanceType == ControllingInstance;
+ return toReturn.release();
+}
+
+void CCActiveAnimation::pushPropertiesTo(CCActiveAnimation* other) const
+{
+ // Currently, we only push changes due to pausing and resuming animations on the main thread.
+ if (m_runState == CCActiveAnimation::Paused || other->m_runState == CCActiveAnimation::Paused) {
+ other->m_runState = m_runState;
+ other->m_pauseTime = m_pauseTime;
+ other->m_totalPausedTime = m_totalPausedTime;
+ }
+}
+
+} // namespace WebCore
diff --git a/cc/CCActiveAnimation.h b/cc/CCActiveAnimation.h
new file mode 100644
index 0000000..66c2d6d
--- /dev/null
+++ b/cc/CCActiveAnimation.h
@@ -0,0 +1,161 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCActiveAnimation_h
+#define CCActiveAnimation_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCAnimationCurve;
+
+// A CCActiveAnimation, contains all the state required to play a CCAnimationCurve.
+// Specifically, the affected property, the run state (paused, finished, etc.),
+// loop count, last pause time, and the total time spent paused.
+class CCActiveAnimation {
+ WTF_MAKE_NONCOPYABLE(CCActiveAnimation);
+public:
+ // Animations begin in one of the 'waiting' states. Animations waiting for the next tick
+ // will start the next time the controller animates. Animations waiting for target
+ // availibility will run as soon as their target property is free (and all the animations
+ // animating with it are also able to run). Animations waiting for their start time to
+ // come have be scheduled to run at a particular point in time. When this time arrives,
+ // the controller will move the animations into the Running state. Running animations
+ // may toggle between Running and Paused, and may be stopped by moving into either the
+ // Aborted or Finished states. A Finished animation was allowed to run to completion, but
+ // an Aborted animation was not.
+ enum RunState {
+ WaitingForNextTick = 0,
+ WaitingForTargetAvailability,
+ WaitingForStartTime,
+ WaitingForDeletion,
+ Running,
+ Paused,
+ Finished,
+ Aborted,
+ // This sentinel must be last.
+ RunStateEnumSize
+ };
+
+ enum TargetProperty {
+ Transform = 0,
+ Opacity,
+ // This sentinel must be last.
+ TargetPropertyEnumSize
+ };
+
+ static PassOwnPtr<CCActiveAnimation> create(PassOwnPtr<CCAnimationCurve>, int animationId, int groupId, TargetProperty);
+
+ virtual ~CCActiveAnimation();
+
+ int id() const { return m_id; }
+ int group() const { return m_group; }
+ TargetProperty targetProperty() const { return m_targetProperty; }
+
+ RunState runState() const { return m_runState; }
+ void setRunState(RunState, double monotonicTime);
+
+ // This is the number of times that the animation will play. If this
+ // value is zero the animation will not play. If it is negative, then
+ // the animation will loop indefinitely.
+ int iterations() const { return m_iterations; }
+ void setIterations(int n) { m_iterations = n; }
+
+ double startTime() const { return m_startTime; }
+ void setStartTime(double monotonicTime) { m_startTime = monotonicTime; }
+ bool hasSetStartTime() const { return m_startTime; }
+
+ double timeOffset() const { return m_timeOffset; }
+ void setTimeOffset(double monotonicTime) { m_timeOffset = monotonicTime; }
+
+ void suspend(double monotonicTime);
+ void resume(double monotonicTime);
+
+ // If alternatesDirection is true, on odd numbered iterations we reverse the curve.
+ bool alternatesDirection() const { return m_alternatesDirection; }
+ void setAlternatesDirection(bool alternates) { m_alternatesDirection = alternates; }
+
+ bool isFinishedAt(double monotonicTime) const;
+ bool isFinished() const { return m_runState == Finished
+ || m_runState == Aborted
+ || m_runState == WaitingForDeletion; }
+
+ CCAnimationCurve* curve() { return m_curve.get(); }
+ const CCAnimationCurve* curve() const { return m_curve.get(); }
+
+ // If this is true, even if the animation is running, it will not be tickable until
+ // it is given a start time. This is true for animations running on the main thread.
+ bool needsSynchronizedStartTime() const { return m_needsSynchronizedStartTime; }
+ void setNeedsSynchronizedStartTime(bool needsSynchronizedStartTime) { m_needsSynchronizedStartTime = needsSynchronizedStartTime; }
+
+ // Takes the given absolute time, and using the start time and the number
+ // of iterations, returns the relative time in the current iteration.
+ double trimTimeToCurrentIteration(double monotonicTime) const;
+
+ enum InstanceType {
+ ControllingInstance = 0,
+ NonControllingInstance
+ };
+
+ PassOwnPtr<CCActiveAnimation> clone(InstanceType) const;
+ PassOwnPtr<CCActiveAnimation> cloneAndInitialize(InstanceType, RunState initialRunState, double startTime) const;
+ bool isControllingInstance() const { return m_isControllingInstance; }
+
+ void pushPropertiesTo(CCActiveAnimation*) const;
+
+private:
+ CCActiveAnimation(PassOwnPtr<CCAnimationCurve>, int animationId, int groupId, TargetProperty);
+
+ OwnPtr<CCAnimationCurve> m_curve;
+
+ // IDs are not necessarily unique.
+ int m_id;
+
+ // Animations that must be run together are called 'grouped' and have the same group id
+ // Grouped animations are guaranteed to start at the same time and no other animations
+ // may animate any of the group's target properties until all animations in the
+ // group have finished animating. Note: an active animation's group id and target
+ // property uniquely identify that animation.
+ int m_group;
+
+ TargetProperty m_targetProperty;
+ RunState m_runState;
+ int m_iterations;
+ double m_startTime;
+ bool m_alternatesDirection;
+
+ // The time offset effectively pushes the start of the animation back in time. This is
+ // used for resuming paused animations -- an animation is added with a non-zero time
+ // offset, causing the animation to skip ahead to the desired point in time.
+ double m_timeOffset;
+
+ bool m_needsSynchronizedStartTime;
+
+ // When an animation is suspended, it behaves as if it is paused and it also ignores
+ // all run state changes until it is resumed. This is used for testing purposes.
+ bool m_suspended;
+
+ // These are used in trimTimeToCurrentIteration to account for time
+ // spent while paused. This is not included in AnimationState since it
+ // there is absolutely no need for clients of this controller to know
+ // about these values.
+ double m_pauseTime;
+ double m_totalPausedTime;
+
+ // Animations lead dual lives. An active animation will be conceptually owned by
+ // two controllers, one on the impl thread and one on the main. In reality, there
+ // will be two separate CCActiveAnimation instances for the same animation. They
+ // will have the same group id and the same target property (these two values
+ // uniquely identify an animation). The instance on the impl thread is the instance
+ // that ultimately controls the values of the animating layer and so we will refer
+ // to it as the 'controlling instance'.
+ bool m_isControllingInstance;
+};
+
+} // namespace WebCore
+
+#endif // CCActiveAnimation_h
diff --git a/cc/CCActiveAnimationTest.cpp b/cc/CCActiveAnimationTest.cpp
new file mode 100644
index 0000000..86d4004
--- /dev/null
+++ b/cc/CCActiveAnimationTest.cpp
@@ -0,0 +1,208 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCActiveAnimation.h"
+
+#include "CCAnimationTestCommon.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wtf/Vector.h>
+
+using namespace WebKitTests;
+using namespace WebCore;
+
+namespace {
+
+PassOwnPtr<CCActiveAnimation> createActiveAnimation(int iterations)
+{
+ OwnPtr<CCActiveAnimation> toReturn(CCActiveAnimation::create(adoptPtr(new FakeFloatAnimationCurve), 0, 1, CCActiveAnimation::Opacity));
+ toReturn->setIterations(iterations);
+ return toReturn.release();
+}
+
+TEST(CCActiveAnimationTest, TrimTimeZeroIterations)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(-1));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(1));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeOneIteration)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(-1));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(2));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeInfiniteIterations)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(-1));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(1));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1.5));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeAlternating)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(-1));
+ anim->setAlternatesDirection(true);
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1));
+ EXPECT_EQ(0.75, anim->trimTimeToCurrentIteration(1.25));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeStartTime)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setStartTime(4);
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(4));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(4.5));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(5));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(6));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeTimeOffset)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setTimeOffset(4);
+ anim->setStartTime(4);
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1));
+}
+
+TEST(CCActiveAnimationTest, TrimTimePauseResume)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+ anim->setRunState(CCActiveAnimation::Paused, 0.5);
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+ anim->setRunState(CCActiveAnimation::Running, 1024);
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1024.5));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeSuspendResume)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+ anim->suspend(0.5);
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+ anim->resume(1024);
+ EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+ EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1024.5));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtZeroIterations)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_FALSE(anim->isFinishedAt(-1));
+ EXPECT_TRUE(anim->isFinishedAt(0));
+ EXPECT_TRUE(anim->isFinishedAt(1));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtOneIteration)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_FALSE(anim->isFinishedAt(-1));
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ EXPECT_TRUE(anim->isFinishedAt(1));
+ EXPECT_TRUE(anim->isFinishedAt(2));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtInfiniteIterations)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(-1));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ EXPECT_FALSE(anim->isFinishedAt(0.5));
+ EXPECT_FALSE(anim->isFinishedAt(1));
+ EXPECT_FALSE(anim->isFinishedAt(1.5));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtNotRunning)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_TRUE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::Paused, 0);
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 0);
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+ EXPECT_FALSE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::Finished, 0);
+ EXPECT_TRUE(anim->isFinishedAt(0));
+ anim->setRunState(CCActiveAnimation::Aborted, 0);
+ EXPECT_TRUE(anim->isFinishedAt(0));
+}
+
+TEST(CCActiveAnimationTest, IsFinished)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Paused, 0);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 0);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Finished, 0);
+ EXPECT_TRUE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Aborted, 0);
+ EXPECT_TRUE(anim->isFinished());
+}
+
+TEST(CCActiveAnimationTest, IsFinishedNeedsSynchronizedStartTime)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->setRunState(CCActiveAnimation::Running, 2);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Paused, 2);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForNextTick, 2);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 2);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::WaitingForStartTime, 2);
+ EXPECT_FALSE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Finished, 0);
+ EXPECT_TRUE(anim->isFinished());
+ anim->setRunState(CCActiveAnimation::Aborted, 0);
+ EXPECT_TRUE(anim->isFinished());
+}
+
+TEST(CCActiveAnimationTest, RunStateChangesIgnoredWhileSuspended)
+{
+ OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+ anim->suspend(0);
+ EXPECT_EQ(CCActiveAnimation::Paused, anim->runState());
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_EQ(CCActiveAnimation::Paused, anim->runState());
+ anim->resume(0);
+ anim->setRunState(CCActiveAnimation::Running, 0);
+ EXPECT_EQ(CCActiveAnimation::Running, anim->runState());
+}
+
+} // namespace
diff --git a/cc/CCActiveGestureAnimation.cpp b/cc/CCActiveGestureAnimation.cpp
new file mode 100644
index 0000000..b8542e7
--- /dev/null
+++ b/cc/CCActiveGestureAnimation.cpp
@@ -0,0 +1,44 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCActiveGestureAnimation.h"
+
+#include "CCGestureCurve.h"
+#include "TraceEvent.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCActiveGestureAnimation> CCActiveGestureAnimation::create(PassOwnPtr<CCGestureCurve> curve, CCGestureCurveTarget* target)
+{
+ return adoptPtr(new CCActiveGestureAnimation(curve, target));
+}
+
+CCActiveGestureAnimation::CCActiveGestureAnimation(PassOwnPtr<CCGestureCurve> curve, CCGestureCurveTarget* target)
+ : m_startTime(0)
+ , m_waitingForFirstTick(true)
+ , m_gestureCurve(curve)
+ , m_gestureCurveTarget(target)
+{
+ TRACE_EVENT_ASYNC_BEGIN1("input", "GestureAnimation", this, "curve", m_gestureCurve->debugName());
+}
+
+CCActiveGestureAnimation::~CCActiveGestureAnimation()
+{
+ TRACE_EVENT_ASYNC_END0("input", "GestureAnimation", this);
+}
+
+bool CCActiveGestureAnimation::animate(double monotonicTime)
+{
+ if (m_waitingForFirstTick) {
+ m_startTime = monotonicTime;
+ m_waitingForFirstTick = false;
+ }
+
+ // CCGestureCurves used zero-based time, so subtract start-time.
+ return m_gestureCurve->apply(monotonicTime - m_startTime, m_gestureCurveTarget);
+}
+
+} // namespace WebCore
diff --git a/cc/CCActiveGestureAnimation.h b/cc/CCActiveGestureAnimation.h
new file mode 100644
index 0000000..57d70a2
--- /dev/null
+++ b/cc/CCActiveGestureAnimation.h
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCActiveGestureAnimation_h
+#define CCActiveGestureAnimation_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCGestureCurve;
+class CCGestureCurveTarget;
+
+class CCActiveGestureAnimation {
+ WTF_MAKE_NONCOPYABLE(CCActiveGestureAnimation);
+public:
+ static PassOwnPtr<CCActiveGestureAnimation> create(PassOwnPtr<CCGestureCurve>, CCGestureCurveTarget*);
+ ~CCActiveGestureAnimation();
+
+ bool animate(double monotonicTime);
+
+private:
+ CCActiveGestureAnimation(PassOwnPtr<CCGestureCurve>, CCGestureCurveTarget*);
+
+ double m_startTime;
+ double m_waitingForFirstTick;
+ OwnPtr<CCGestureCurve> m_gestureCurve;
+ CCGestureCurveTarget* m_gestureCurveTarget;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCAnimationCurve.cpp b/cc/CCAnimationCurve.cpp
new file mode 100644
index 0000000..b9e80b2
--- /dev/null
+++ b/cc/CCAnimationCurve.cpp
@@ -0,0 +1,23 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCAnimationCurve.h"
+
+namespace WebCore {
+
+const CCFloatAnimationCurve* CCAnimationCurve::toFloatAnimationCurve() const
+{
+ ASSERT(type() == CCAnimationCurve::Float);
+ return static_cast<const CCFloatAnimationCurve*>(this);
+}
+
+const CCTransformAnimationCurve* CCAnimationCurve::toTransformAnimationCurve() const
+{
+ ASSERT(type() == CCAnimationCurve::Transform);
+ return static_cast<const CCTransformAnimationCurve*>(this);
+}
+
+} // namespace WebCore
diff --git a/cc/CCAnimationCurve.h b/cc/CCAnimationCurve.h
new file mode 100644
index 0000000..4fb6141
--- /dev/null
+++ b/cc/CCAnimationCurve.h
@@ -0,0 +1,56 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCAnimationCurve_h
+#define CCAnimationCurve_h
+
+#include <public/WebTransformationMatrix.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCFloatAnimationCurve;
+class CCTransformAnimationCurve;
+class IntSize;
+class TransformOperations;
+
+// An animation curve is a function that returns a value given a time.
+// There are currently only two types of curve, float and transform.
+class CCAnimationCurve {
+public:
+ enum Type { Float, Transform };
+
+ virtual ~CCAnimationCurve() { }
+
+ virtual double duration() const = 0;
+ virtual Type type() const = 0;
+ virtual PassOwnPtr<CCAnimationCurve> clone() const = 0;
+
+ const CCFloatAnimationCurve* toFloatAnimationCurve() const;
+ const CCTransformAnimationCurve* toTransformAnimationCurve() const;
+};
+
+class CCFloatAnimationCurve : public CCAnimationCurve {
+public:
+ virtual ~CCFloatAnimationCurve() { }
+
+ virtual float getValue(double t) const = 0;
+
+ // Partial CCAnimation implementation.
+ virtual Type type() const OVERRIDE { return Float; }
+};
+
+class CCTransformAnimationCurve : public CCAnimationCurve {
+public:
+ virtual ~CCTransformAnimationCurve() { }
+
+ virtual WebKit::WebTransformationMatrix getValue(double t) const = 0;
+
+ // Partial CCAnimation implementation.
+ virtual Type type() const OVERRIDE { return Transform; }
+};
+
+} // namespace WebCore
+
+#endif // CCAnimation_h
diff --git a/cc/CCAnimationEvents.h b/cc/CCAnimationEvents.h
new file mode 100644
index 0000000..b1f5d2a
--- /dev/null
+++ b/cc/CCAnimationEvents.h
@@ -0,0 +1,38 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCAnimationEvents_h
+#define CCAnimationEvents_h
+
+#include "CCActiveAnimation.h"
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+struct CCAnimationEvent {
+ enum Type { Started, Finished };
+
+ CCAnimationEvent(Type type, int layerId, int groupId, CCActiveAnimation::TargetProperty targetProperty, double monotonicTime)
+ : type(type)
+ , layerId(layerId)
+ , groupId(groupId)
+ , targetProperty(targetProperty)
+ , monotonicTime(monotonicTime)
+ {
+ }
+
+ Type type;
+ int layerId;
+ int groupId;
+ CCActiveAnimation::TargetProperty targetProperty;
+ double monotonicTime;
+};
+
+typedef Vector<CCAnimationEvent> CCAnimationEventsVector;
+
+} // namespace WebCore
+
+#endif // CCAnimationEvents_h
diff --git a/cc/CCCheckerboardDrawQuad.cpp b/cc/CCCheckerboardDrawQuad.cpp
new file mode 100644
index 0000000..0f8b499
--- /dev/null
+++ b/cc/CCCheckerboardDrawQuad.cpp
@@ -0,0 +1,28 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCCheckerboardDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCCheckerboardDrawQuad> CCCheckerboardDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect)
+{
+ return adoptPtr(new CCCheckerboardDrawQuad(sharedQuadState, quadRect));
+}
+
+CCCheckerboardDrawQuad::CCCheckerboardDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::Checkerboard, quadRect)
+{
+}
+
+const CCCheckerboardDrawQuad* CCCheckerboardDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::Checkerboard);
+ return static_cast<const CCCheckerboardDrawQuad*>(quad);
+}
+
+
+}
diff --git a/cc/CCCheckerboardDrawQuad.h b/cc/CCCheckerboardDrawQuad.h
new file mode 100644
index 0000000..c1bfe61
--- /dev/null
+++ b/cc/CCCheckerboardDrawQuad.h
@@ -0,0 +1,28 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCCheckerboardDrawQuad_h
+#define CCCheckerboardDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCCheckerboardDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCCheckerboardDrawQuad> create(const CCSharedQuadState*, const IntRect&);
+
+ static const CCCheckerboardDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCCheckerboardDrawQuad(const CCSharedQuadState*, const IntRect&);
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCCompletionEvent.h b/cc/CCCompletionEvent.h
new file mode 100644
index 0000000..7445733
--- /dev/null
+++ b/cc/CCCompletionEvent.h
@@ -0,0 +1,65 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCCompletionEvent_h
+#define CCCompletionEvent_h
+
+#include <wtf/ThreadingPrimitives.h>
+
+namespace WebCore {
+
+// Used for making blocking calls from one thread to another. Use only when
+// absolutely certain that doing-so will not lead to a deadlock.
+//
+// It is safe to destroy this object as soon as wait() returns.
+class CCCompletionEvent {
+public:
+ CCCompletionEvent()
+ {
+#ifndef NDEBUG
+ m_waited = false;
+ m_signaled = false;
+#endif
+ m_mutex.lock();
+ }
+
+ ~CCCompletionEvent()
+ {
+ m_mutex.unlock();
+ ASSERT(m_waited);
+ ASSERT(m_signaled);
+ }
+
+ void wait()
+ {
+ ASSERT(!m_waited);
+#ifndef NDEBUG
+ m_waited = true;
+#endif
+ m_condition.wait(m_mutex);
+ }
+
+ void signal()
+ {
+ MutexLocker lock(m_mutex);
+ ASSERT(!m_signaled);
+#ifndef NDEBUG
+ m_signaled = true;
+#endif
+ m_condition.signal();
+ }
+
+private:
+ Mutex m_mutex;
+ ThreadCondition m_condition;
+#ifndef NDEBUG
+ // Used to assert that wait() and signal() are each called exactly once.
+ bool m_waited;
+ bool m_signaled;
+#endif
+};
+
+}
+
+#endif
diff --git a/cc/CCDamageTracker.cpp b/cc/CCDamageTracker.cpp
new file mode 100644
index 0000000..90d6e29
--- /dev/null
+++ b/cc/CCDamageTracker.cpp
@@ -0,0 +1,345 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCDamageTracker.h"
+
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCMathUtil.h"
+#include "CCRenderSurface.h"
+#include <public/WebFilterOperations.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+PassOwnPtr<CCDamageTracker> CCDamageTracker::create()
+{
+ return adoptPtr(new CCDamageTracker());
+}
+
+CCDamageTracker::CCDamageTracker()
+ : m_forceFullDamageNextUpdate(false)
+{
+ m_currentRectHistory = adoptPtr(new RectMap);
+ m_nextRectHistory = adoptPtr(new RectMap);
+}
+
+CCDamageTracker::~CCDamageTracker()
+{
+}
+
+static inline void expandRectWithFilters(FloatRect& rect, const WebKit::WebFilterOperations& filters)
+{
+ int top, right, bottom, left;
+ filters.getOutsets(top, right, bottom, left);
+ rect.move(-left, -top);
+ rect.expand(left + right, top + bottom);
+}
+
+static inline void expandDamageRectInsideRectWithFilters(FloatRect& damageRect, const FloatRect& preFilterRect, const WebKit::WebFilterOperations& filters)
+{
+ FloatRect expandedDamageRect = damageRect;
+ expandRectWithFilters(expandedDamageRect, filters);
+ FloatRect filterRect = preFilterRect;
+ expandRectWithFilters(filterRect, filters);
+
+ expandedDamageRect.intersect(filterRect);
+ damageRect.unite(expandedDamageRect);
+}
+
+void CCDamageTracker::updateDamageTrackingState(const Vector<CCLayerImpl*>& layerList, int targetSurfaceLayerID, bool targetSurfacePropertyChangedOnlyFromDescendant, const IntRect& targetSurfaceContentRect, CCLayerImpl* targetSurfaceMaskLayer, const WebKit::WebFilterOperations& filters)
+{
+ //
+ // This function computes the "damage rect" of a target surface, and updates the state
+ // that is used to correctly track damage across frames. The damage rect is the region
+ // of the surface that may have changed and needs to be redrawn. This can be used to
+ // scissor what is actually drawn, to save GPU computation and bandwidth.
+ //
+ // The surface's damage rect is computed as the union of all possible changes that
+ // have happened to the surface since the last frame was drawn. This includes:
+ // - any changes for existing layers/surfaces that contribute to the target surface
+ // - layers/surfaces that existed in the previous frame, but no longer exist.
+ //
+ // The basic algorithm for computing the damage region is as follows:
+ //
+ // 1. compute damage caused by changes in active/new layers
+ // for each layer in the layerList:
+ // if the layer is actually a renderSurface:
+ // add the surface's damage to our target surface.
+ // else
+ // add the layer's damage to the target surface.
+ //
+ // 2. compute damage caused by the target surface's mask, if it exists.
+ //
+ // 3. compute damage caused by old layers/surfaces that no longer exist
+ // for each leftover layer:
+ // add the old layer/surface bounds to the target surface damage.
+ //
+ // 4. combine all partial damage rects to get the full damage rect.
+ //
+ // Additional important points:
+ //
+ // - This algorithm is implicitly recursive; it assumes that descendant surfaces have
+ // already computed their damage.
+ //
+ // - Changes to layers/surfaces indicate "damage" to the target surface; If a layer is
+ // not changed, it does NOT mean that the layer can skip drawing. All layers that
+ // overlap the damaged region still need to be drawn. For example, if a layer
+ // changed its opacity, then layers underneath must be re-drawn as well, even if
+ // they did not change.
+ //
+ // - If a layer/surface property changed, the old bounds and new bounds may overlap...
+ // i.e. some of the exposed region may not actually be exposing anything. But this
+ // does not artificially inflate the damage rect. If the layer changed, its entire
+ // old bounds would always need to be redrawn, regardless of how much it overlaps
+ // with the layer's new bounds, which also need to be entirely redrawn.
+ //
+ // - See comments in the rest of the code to see what exactly is considered a "change"
+ // in a layer/surface.
+ //
+ // - To correctly manage exposed rects, two RectMaps are maintained:
+ //
+ // 1. The "current" map contains all the layer bounds that contributed to the
+ // previous frame (even outside the previous damaged area). If a layer changes
+ // or does not exist anymore, those regions are then exposed and damage the
+ // target surface. As the algorithm progresses, entries are removed from the
+ // map until it has only leftover layers that no longer exist.
+ //
+ // 2. The "next" map starts out empty, and as the algorithm progresses, every
+ // layer/surface that contributes to the surface is added to the map.
+ //
+ // 3. After the damage rect is computed, the two maps are swapped, so that the
+ // damage tracker is ready for the next frame.
+ //
+
+ // These functions cannot be bypassed with early-exits, even if we know what the
+ // damage will be for this frame, because we need to update the damage tracker state
+ // to correctly track the next frame.
+ FloatRect damageFromActiveLayers = trackDamageFromActiveLayers(layerList, targetSurfaceLayerID);
+ FloatRect damageFromSurfaceMask = trackDamageFromSurfaceMask(targetSurfaceMaskLayer);
+ FloatRect damageFromLeftoverRects = trackDamageFromLeftoverRects();
+
+ FloatRect damageRectForThisUpdate;
+
+ if (m_forceFullDamageNextUpdate || targetSurfacePropertyChangedOnlyFromDescendant) {
+ damageRectForThisUpdate = targetSurfaceContentRect;
+ m_forceFullDamageNextUpdate = false;
+ } else {
+ // FIXME: can we clamp this damage to the surface's content rect? (affects performance, but not correctness)
+ damageRectForThisUpdate = damageFromActiveLayers;
+ damageRectForThisUpdate.uniteIfNonZero(damageFromSurfaceMask);
+ damageRectForThisUpdate.uniteIfNonZero(damageFromLeftoverRects);
+
+ if (filters.hasFilterThatMovesPixels())
+ expandRectWithFilters(damageRectForThisUpdate, filters);
+ }
+
+ // Damage accumulates until we are notified that we actually did draw on that frame.
+ m_currentDamageRect.uniteIfNonZero(damageRectForThisUpdate);
+
+ // The next history map becomes the current map for the next frame. Note this must
+ // happen every frame to correctly track changes, even if damage accumulates over
+ // multiple frames before actually being drawn.
+ swap(m_currentRectHistory, m_nextRectHistory);
+}
+
+FloatRect CCDamageTracker::removeRectFromCurrentFrame(int layerID, bool& layerIsNew)
+{
+ layerIsNew = !m_currentRectHistory->contains(layerID);
+
+ // take() will remove the entry from the map, or if not found, return a default (empty) rect.
+ return m_currentRectHistory->take(layerID);
+}
+
+void CCDamageTracker::saveRectForNextFrame(int layerID, const FloatRect& targetSpaceRect)
+{
+ // This layer should not yet exist in next frame's history.
+ ASSERT(layerID > 0);
+ ASSERT(m_nextRectHistory->find(layerID) == m_nextRectHistory->end());
+ m_nextRectHistory->set(layerID, targetSpaceRect);
+}
+
+FloatRect CCDamageTracker::trackDamageFromActiveLayers(const Vector<CCLayerImpl*>& layerList, int targetSurfaceLayerID)
+{
+ FloatRect damageRect = FloatRect();
+
+ for (unsigned layerIndex = 0; layerIndex < layerList.size(); ++layerIndex) {
+ // Visit layers in back-to-front order.
+ CCLayerImpl* layer = layerList[layerIndex];
+
+ if (CCLayerTreeHostCommon::renderSurfaceContributesToTarget<CCLayerImpl>(layer, targetSurfaceLayerID))
+ extendDamageForRenderSurface(layer, damageRect);
+ else
+ extendDamageForLayer(layer, damageRect);
+ }
+
+ return damageRect;
+}
+
+FloatRect CCDamageTracker::trackDamageFromSurfaceMask(CCLayerImpl* targetSurfaceMaskLayer)
+{
+ FloatRect damageRect = FloatRect();
+
+ if (!targetSurfaceMaskLayer)
+ return damageRect;
+
+ // Currently, if there is any change to the mask, we choose to damage the entire
+ // surface. This could potentially be optimized later, but it is not expected to be a
+ // common case.
+ if (targetSurfaceMaskLayer->layerPropertyChanged() || !targetSurfaceMaskLayer->updateRect().isEmpty())
+ damageRect = FloatRect(FloatPoint::zero(), FloatSize(targetSurfaceMaskLayer->bounds()));
+
+ return damageRect;
+}
+
+FloatRect CCDamageTracker::trackDamageFromLeftoverRects()
+{
+ // After computing damage for all active layers, any leftover items in the current
+ // rect history correspond to layers/surfaces that no longer exist. So, these regions
+ // are now exposed on the target surface.
+
+ FloatRect damageRect = FloatRect();
+
+ for (RectMap::iterator it = m_currentRectHistory->begin(); it != m_currentRectHistory->end(); ++it)
+ damageRect.unite(it->second);
+
+ m_currentRectHistory->clear();
+
+ return damageRect;
+}
+
+static bool layerNeedsToRedrawOntoItsTargetSurface(CCLayerImpl* layer)
+{
+ // If the layer does NOT own a surface but has SurfacePropertyChanged,
+ // this means that its target surface is affected and needs to be redrawn.
+ // However, if the layer DOES own a surface, then the SurfacePropertyChanged
+ // flag should not be used here, because that flag represents whether the
+ // layer's surface has changed.
+ if (layer->renderSurface())
+ return layer->layerPropertyChanged();
+ return layer->layerPropertyChanged() || layer->layerSurfacePropertyChanged();
+}
+
+void CCDamageTracker::extendDamageForLayer(CCLayerImpl* layer, FloatRect& targetDamageRect)
+{
+ // There are two ways that a layer can damage a region of the target surface:
+ // 1. Property change (e.g. opacity, position, transforms):
+ // - the entire region of the layer itself damages the surface.
+ // - the old layer region also damages the surface, because this region is now exposed.
+ // - note that in many cases the old and new layer rects may overlap, which is fine.
+ //
+ // 2. Repaint/update: If a region of the layer that was repainted/updated, that
+ // region damages the surface.
+ //
+ // Property changes take priority over update rects.
+ //
+ // This method is called when we want to consider how a layer contributes to its
+ // targetRenderSurface, even if that layer owns the targetRenderSurface itself.
+ // To consider how a layer's targetSurface contributes to the ancestorSurface,
+ // extendDamageForRenderSurface() must be called instead.
+
+ bool layerIsNew = false;
+ FloatRect oldRectInTargetSpace = removeRectFromCurrentFrame(layer->id(), layerIsNew);
+
+ FloatRect rectInTargetSpace = CCMathUtil::mapClippedRect(layer->drawTransform(), FloatRect(FloatPoint::zero(), layer->contentBounds()));
+ saveRectForNextFrame(layer->id(), rectInTargetSpace);
+
+ if (layerIsNew || layerNeedsToRedrawOntoItsTargetSurface(layer)) {
+ // If a layer is new or has changed, then its entire layer rect affects the target surface.
+ targetDamageRect.uniteIfNonZero(rectInTargetSpace);
+
+ // The layer's old region is now exposed on the target surface, too.
+ // Note oldRectInTargetSpace is already in target space.
+ targetDamageRect.uniteIfNonZero(oldRectInTargetSpace);
+ } else if (!layer->updateRect().isEmpty()) {
+ // If the layer properties havent changed, then the the target surface is only
+ // affected by the layer's update area, which could be empty.
+ FloatRect updateContentRect = layer->updateRect();
+ float widthScale = layer->contentBounds().width() / static_cast<float>(layer->bounds().width());
+ float heightScale = layer->contentBounds().height() / static_cast<float>(layer->bounds().height());
+ updateContentRect.scale(widthScale, heightScale);
+
+ FloatRect updateRectInTargetSpace = CCMathUtil::mapClippedRect(layer->drawTransform(), updateContentRect);
+ targetDamageRect.uniteIfNonZero(updateRectInTargetSpace);
+ }
+}
+
+void CCDamageTracker::extendDamageForRenderSurface(CCLayerImpl* layer, FloatRect& targetDamageRect)
+{
+ // There are two ways a "descendant surface" can damage regions of the "target surface":
+ // 1. Property change:
+ // - a surface's geometry can change because of
+ // - changes to descendants (i.e. the subtree) that affect the surface's content rect
+ // - changes to ancestor layers that propagate their property changes to their entire subtree.
+ // - just like layers, both the old surface rect and new surface rect will
+ // damage the target surface in this case.
+ //
+ // 2. Damage rect: This surface may have been damaged by its own layerList as well, and that damage
+ // should propagate to the target surface.
+ //
+
+ CCRenderSurface* renderSurface = layer->renderSurface();
+
+ bool surfaceIsNew = false;
+ FloatRect oldSurfaceRect = removeRectFromCurrentFrame(layer->id(), surfaceIsNew);
+
+ FloatRect surfaceRectInTargetSpace = renderSurface->drawableContentRect(); // already includes replica if it exists.
+ saveRectForNextFrame(layer->id(), surfaceRectInTargetSpace);
+
+ FloatRect damageRectInLocalSpace;
+ if (surfaceIsNew || renderSurface->surfacePropertyChanged() || layer->layerSurfacePropertyChanged()) {
+ // The entire surface contributes damage.
+ damageRectInLocalSpace = renderSurface->contentRect();
+
+ // The surface's old region is now exposed on the target surface, too.
+ targetDamageRect.uniteIfNonZero(oldSurfaceRect);
+ } else {
+ // Only the surface's damageRect will damage the target surface.
+ damageRectInLocalSpace = renderSurface->damageTracker()->currentDamageRect();
+ }
+
+ // If there was damage, transform it to target space, and possibly contribute its reflection if needed.
+ if (!damageRectInLocalSpace.isEmpty()) {
+ const WebTransformationMatrix& drawTransform = renderSurface->drawTransform();
+ FloatRect damageRectInTargetSpace = CCMathUtil::mapClippedRect(drawTransform, damageRectInLocalSpace);
+ targetDamageRect.uniteIfNonZero(damageRectInTargetSpace);
+
+ if (layer->replicaLayer()) {
+ const WebTransformationMatrix& replicaDrawTransform = renderSurface->replicaDrawTransform();
+ targetDamageRect.uniteIfNonZero(CCMathUtil::mapClippedRect(replicaDrawTransform, damageRectInLocalSpace));
+ }
+ }
+
+ // If there was damage on the replica's mask, then the target surface receives that damage as well.
+ if (layer->replicaLayer() && layer->replicaLayer()->maskLayer()) {
+ CCLayerImpl* replicaMaskLayer = layer->replicaLayer()->maskLayer();
+
+ bool replicaIsNew = false;
+ removeRectFromCurrentFrame(replicaMaskLayer->id(), replicaIsNew);
+
+ const WebTransformationMatrix& replicaDrawTransform = renderSurface->replicaDrawTransform();
+ FloatRect replicaMaskLayerRect = CCMathUtil::mapClippedRect(replicaDrawTransform, FloatRect(FloatPoint::zero(), FloatSize(replicaMaskLayer->bounds().width(), replicaMaskLayer->bounds().height())));
+ saveRectForNextFrame(replicaMaskLayer->id(), replicaMaskLayerRect);
+
+ // In the current implementation, a change in the replica mask damages the entire replica region.
+ if (replicaIsNew || replicaMaskLayer->layerPropertyChanged() || !replicaMaskLayer->updateRect().isEmpty())
+ targetDamageRect.uniteIfNonZero(replicaMaskLayerRect);
+ }
+
+ // If the layer has a background filter, this may cause pixels in our surface to be expanded, so we will need to expand any damage
+ // at or below this layer. We expand the damage from this layer too, as we need to readback those pixels from the surface with only
+ // the contents of layers below this one in them. This means we need to redraw any pixels in the surface being used for the blur in
+ // this layer this frame.
+ if (layer->backgroundFilters().hasFilterThatMovesPixels())
+ expandDamageRectInsideRectWithFilters(targetDamageRect, surfaceRectInTargetSpace, layer->backgroundFilters());
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCDamageTracker.h b/cc/CCDamageTracker.h
new file mode 100644
index 0000000..eb2a644
--- /dev/null
+++ b/cc/CCDamageTracker.h
@@ -0,0 +1,62 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDamageTracker_h
+#define CCDamageTracker_h
+
+#include "FloatRect.h"
+#include <wtf/HashMap.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebKit {
+class WebFilterOperations;
+}
+
+namespace WebCore {
+
+class CCLayerImpl;
+class CCRenderSurface;
+
+// Computes the region where pixels have actually changed on a RenderSurface. This region is used
+// to scissor what is actually drawn to the screen to save GPU computation and bandwidth.
+class CCDamageTracker {
+public:
+ static PassOwnPtr<CCDamageTracker> create();
+ ~CCDamageTracker();
+
+ void didDrawDamagedArea() { m_currentDamageRect = FloatRect(); }
+ void forceFullDamageNextUpdate() { m_forceFullDamageNextUpdate = true; }
+ void updateDamageTrackingState(const Vector<CCLayerImpl*>& layerList, int targetSurfaceLayerID, bool targetSurfacePropertyChangedOnlyFromDescendant, const IntRect& targetSurfaceContentRect, CCLayerImpl* targetSurfaceMaskLayer, const WebKit::WebFilterOperations&);
+
+ const FloatRect& currentDamageRect() { return m_currentDamageRect; }
+
+private:
+ CCDamageTracker();
+
+ FloatRect trackDamageFromActiveLayers(const Vector<CCLayerImpl*>& layerList, int targetSurfaceLayerID);
+ FloatRect trackDamageFromSurfaceMask(CCLayerImpl* targetSurfaceMaskLayer);
+ FloatRect trackDamageFromLeftoverRects();
+
+ FloatRect removeRectFromCurrentFrame(int layerID, bool& layerIsNew);
+ void saveRectForNextFrame(int layerID, const FloatRect& targetSpaceRect);
+
+ // These helper functions are used only in trackDamageFromActiveLayers().
+ void extendDamageForLayer(CCLayerImpl*, FloatRect& targetDamageRect);
+ void extendDamageForRenderSurface(CCLayerImpl*, FloatRect& targetDamageRect);
+
+ // To correctly track exposed regions, two hashtables of rects are maintained.
+ // The "current" map is used to compute exposed regions of the current frame, while
+ // the "next" map is used to collect layer rects that are used in the next frame.
+ typedef HashMap<int, FloatRect> RectMap;
+ OwnPtr<RectMap> m_currentRectHistory;
+ OwnPtr<RectMap> m_nextRectHistory;
+
+ FloatRect m_currentDamageRect;
+ bool m_forceFullDamageNextUpdate;
+};
+
+} // namespace WebCore
+
+#endif // CCDamageTracker_h
diff --git a/cc/CCDamageTrackerTest.cpp b/cc/CCDamageTrackerTest.cpp
new file mode 100644
index 0000000..cc1d0ba
--- /dev/null
+++ b/cc/CCDamageTrackerTest.cpp
@@ -0,0 +1,1138 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCDamageTracker.h"
+
+#include "CCLayerImpl.h"
+#include "CCLayerSorter.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCLayerTreeTestCommon.h"
+#include "CCMathUtil.h"
+#include "CCSingleThreadProxy.h"
+#include <gtest/gtest.h>
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WTF;
+using namespace WebKitTests;
+
+namespace {
+
+void executeCalculateDrawTransformsAndVisibility(CCLayerImpl* root, Vector<CCLayerImpl*>& renderSurfaceLayerList)
+{
+ CCLayerSorter layerSorter;
+ int dummyMaxTextureSize = 512;
+
+ // Sanity check: The test itself should create the root layer's render surface, so
+ // that the surface (and its damage tracker) can persist across multiple
+ // calls to this function.
+ ASSERT_TRUE(root->renderSurface());
+ ASSERT_FALSE(renderSurfaceLayerList.size());
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(root, root->bounds(), 1, &layerSorter, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+}
+
+void clearDamageForAllSurfaces(CCLayerImpl* layer)
+{
+ if (layer->renderSurface())
+ layer->renderSurface()->damageTracker()->didDrawDamagedArea();
+
+ // Recursively clear damage for any existing surface.
+ for (size_t i = 0; i < layer->children().size(); ++i)
+ clearDamageForAllSurfaces(layer->children()[i].get());
+}
+
+void emulateDrawingOneFrame(CCLayerImpl* root)
+{
+ // This emulates only the steps that are relevant to testing the damage tracker:
+ // 1. computing the render passes and layerlists
+ // 2. updating all damage trackers in the correct order
+ // 3. resetting all updateRects and propertyChanged flags for all layers and surfaces.
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ executeCalculateDrawTransformsAndVisibility(root, renderSurfaceLayerList);
+
+ // Iterate back-to-front, so that damage correctly propagates from descendant surfaces to ancestors.
+ for (int i = renderSurfaceLayerList.size() - 1; i >= 0; --i) {
+ CCRenderSurface* targetSurface = renderSurfaceLayerList[i]->renderSurface();
+ targetSurface->damageTracker()->updateDamageTrackingState(targetSurface->layerList(), targetSurface->owningLayerId(), targetSurface->surfacePropertyChangedOnlyFromDescendant(), targetSurface->contentRect(), renderSurfaceLayerList[i]->maskLayer(), renderSurfaceLayerList[i]->filters());
+ }
+
+ root->resetAllChangeTrackingForSubtree();
+}
+
+PassOwnPtr<CCLayerImpl> createTestTreeWithOneSurface()
+{
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2);
+
+ root->setPosition(FloatPoint::zero());
+ root->setAnchorPoint(FloatPoint::zero());
+ root->setBounds(IntSize(500, 500));
+ root->setContentBounds(IntSize(500, 500));
+ root->setDrawsContent(true);
+ root->createRenderSurface();
+ root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(500, 500)));
+
+ child->setPosition(FloatPoint(100, 100));
+ child->setAnchorPoint(FloatPoint::zero());
+ child->setBounds(IntSize(30, 30));
+ child->setContentBounds(IntSize(30, 30));
+ child->setDrawsContent(true);
+ root->addChild(child.release());
+
+ return root.release();
+}
+
+PassOwnPtr<CCLayerImpl> createTestTreeWithTwoSurfaces()
+{
+ // This test tree has two render surfaces: one for the root, and one for
+ // child1. Additionally, the root has a second child layer, and child1 has two
+ // children of its own.
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> child1 = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ OwnPtr<CCLayerImpl> grandChild1 = CCLayerImpl::create(4);
+ OwnPtr<CCLayerImpl> grandChild2 = CCLayerImpl::create(5);
+
+ root->setPosition(FloatPoint::zero());
+ root->setAnchorPoint(FloatPoint::zero());
+ root->setBounds(IntSize(500, 500));
+ root->setContentBounds(IntSize(500, 500));
+ root->setDrawsContent(true);
+ root->createRenderSurface();
+ root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(500, 500)));
+
+ child1->setPosition(FloatPoint(100, 100));
+ child1->setAnchorPoint(FloatPoint::zero());
+ child1->setBounds(IntSize(30, 30));
+ child1->setContentBounds(IntSize(30, 30));
+ child1->setOpacity(0.5); // with a child that drawsContent, this will cause the layer to create its own renderSurface.
+ child1->setDrawsContent(false); // this layer does not draw, but is intended to create its own renderSurface.
+
+ child2->setPosition(FloatPoint(11, 11));
+ child2->setAnchorPoint(FloatPoint::zero());
+ child2->setBounds(IntSize(18, 18));
+ child2->setContentBounds(IntSize(18, 18));
+ child2->setDrawsContent(true);
+
+ grandChild1->setPosition(FloatPoint(200, 200));
+ grandChild1->setAnchorPoint(FloatPoint::zero());
+ grandChild1->setBounds(IntSize(6, 8));
+ grandChild1->setContentBounds(IntSize(6, 8));
+ grandChild1->setDrawsContent(true);
+
+ grandChild2->setPosition(FloatPoint(190, 190));
+ grandChild2->setAnchorPoint(FloatPoint::zero());
+ grandChild2->setBounds(IntSize(6, 8));
+ grandChild2->setContentBounds(IntSize(6, 8));
+ grandChild2->setDrawsContent(true);
+
+ child1->addChild(grandChild1.release());
+ child1->addChild(grandChild2.release());
+ root->addChild(child1.release());
+ root->addChild(child2.release());
+
+ return root.release();
+}
+
+PassOwnPtr<CCLayerImpl> createAndSetUpTestTreeWithOneSurface()
+{
+ OwnPtr<CCLayerImpl> root = createTestTreeWithOneSurface();
+
+ // Setup includes going past the first frame which always damages everything, so
+ // that we can actually perform specific tests.
+ emulateDrawingOneFrame(root.get());
+
+ return root.release();
+}
+
+PassOwnPtr<CCLayerImpl> createAndSetUpTestTreeWithTwoSurfaces()
+{
+ OwnPtr<CCLayerImpl> root = createTestTreeWithTwoSurfaces();
+
+ // Setup includes going past the first frame which always damages everything, so
+ // that we can actually perform specific tests.
+ emulateDrawingOneFrame(root.get());
+
+ return root.release();
+}
+
+class CCDamageTrackerTest : public testing::Test {
+private:
+ // For testing purposes, fake that we are on the impl thread.
+ DebugScopedSetImplThread setImplThread;
+};
+
+TEST_F(CCDamageTrackerTest, sanityCheckTestTreeWithOneSurface)
+{
+ // Sanity check that the simple test tree will actually produce the expected render
+ // surfaces and layer lists.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+
+ EXPECT_EQ(2u, root->renderSurface()->layerList().size());
+ EXPECT_EQ(1, root->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(2, root->renderSurface()->layerList()[1]->id());
+
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 500, 500), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, sanityCheckTestTreeWithTwoSurfaces)
+{
+ // Sanity check that the complex test tree will actually produce the expected render
+ // surfaces and layer lists.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* child2 = root->children()[1].get();
+ FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ ASSERT_TRUE(child1->renderSurface());
+ EXPECT_FALSE(child2->renderSurface());
+ EXPECT_EQ(3u, root->renderSurface()->layerList().size());
+ EXPECT_EQ(2u, child1->renderSurface()->layerList().size());
+
+ // The render surface for child1 only has a contentRect that encloses grandChild1 and grandChild2, because child1 does not draw content.
+ EXPECT_FLOAT_RECT_EQ(FloatRect(190, 190, 16, 18), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 500, 500), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForUpdateRects)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ // CASE 1: Setting the update rect should cause the corresponding damage to the surface.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 12, 13));
+ emulateDrawingOneFrame(root.get());
+
+ // Damage position on the surface should be: position of updateRect (10, 11) relative to the child (100, 100).
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 12, 13), rootDamageRect);
+
+ // CASE 2: The same update rect twice in a row still produces the same damage.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 12, 13));
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 12, 13), rootDamageRect);
+
+ // CASE 3: Setting a different update rect should cause damage on the new update region, but no additional exposed old region.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(20, 25, 1, 2));
+ emulateDrawingOneFrame(root.get());
+
+ // Damage position on the surface should be: position of updateRect (20, 25) relative to the child (100, 100).
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(120, 125, 1, 2), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForPropertyChanges)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ // CASE 1: The layer's property changed flag takes priority over update rect.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 12, 13));
+ child->setOpacity(0.5);
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check - we should not have accidentally created a separate render surface for the translucent layer.
+ ASSERT_FALSE(child->renderSurface());
+ ASSERT_EQ(2u, root->renderSurface()->layerList().size());
+
+ // Damage should be the entire child layer in targetSurface space.
+ FloatRect expectedRect = FloatRect(100, 100, 30, 30);
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(expectedRect, rootDamageRect);
+
+ // CASE 2: If a layer moves due to property change, it damages both the new location
+ // and the old (exposed) location. The old location is the entire old layer,
+ // not just the updateRect.
+
+ // Cycle one frame of no change, just to sanity check that the next rect is not because of the old damage state.
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ EXPECT_TRUE(root->renderSurface()->damageTracker()->currentDamageRect().isEmpty());
+
+ // Then, test the actual layer movement.
+ clearDamageForAllSurfaces(root.get());
+ child->setPosition(FloatPoint(200, 230));
+ emulateDrawingOneFrame(root.get());
+
+ // Expect damage to be the combination of the previous one and the new one.
+ expectedRect.uniteIfNonZero(FloatRect(200, 230, 30, 30));
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(expectedRect, rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForTransformedLayer)
+{
+ // If a layer is transformed, the damage rect should still enclose the entire
+ // transformed layer.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ WebTransformationMatrix rotation;
+ rotation.rotate(45);
+
+ clearDamageForAllSurfaces(root.get());
+ child->setAnchorPoint(FloatPoint(0.5, 0.5));
+ child->setPosition(FloatPoint(85, 85));
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that the layer actually moved to (85, 85), damaging its old location and new location.
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(85, 85, 45, 45), rootDamageRect);
+
+ // With the anchor on the layer's center, now we can test the rotation more
+ // intuitively, since it applies about the layer's anchor.
+ clearDamageForAllSurfaces(root.get());
+ child->setTransform(rotation);
+ emulateDrawingOneFrame(root.get());
+
+ // Since the child layer is square, rotation by 45 degrees about the center should
+ // increase the size of the expected rect by sqrt(2), centered around (100, 100). The
+ // old exposed region should be fully contained in the new region.
+ double expectedWidth = 30 * sqrt(2.0);
+ double expectedPosition = 100 - 0.5 * expectedWidth;
+ FloatRect expectedRect(expectedPosition, expectedPosition, expectedWidth, expectedWidth);
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(expectedRect, rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForPerspectiveClippedLayer)
+{
+ // If a layer has a perspective transform that causes w < 0, then not clipping the
+ // layer can cause an invalid damage rect. This test checks that the w < 0 case is
+ // tracked properly.
+ //
+ // The transform is constructed so that if w < 0 clipping is not performed, the
+ // incorrect rect will be very small, specifically: position (500.972504, 498.544617) and size 0.056610 x 2.910767.
+ // Instead, the correctly transformed rect should actually be very huge (i.e. in theory, -infinity on the left),
+ // and positioned so that the right-most bound rect will be approximately 501 units in root surface space.
+ //
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ WebTransformationMatrix transform;
+ transform.translate3d(500, 500, 0);
+ transform.applyPerspective(1);
+ transform.rotate3d(0, 45, 0);
+ transform.translate3d(-50, -50, 0);
+
+ // Set up the child
+ child->setPosition(FloatPoint(0, 0));
+ child->setBounds(IntSize(100, 100));
+ child->setContentBounds(IntSize(100, 100));
+ child->setTransform(transform);
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that the child layer's bounds would actually get clipped by w < 0,
+ // otherwise this test is not actually testing the intended scenario.
+ FloatQuad testQuad(FloatRect(FloatPoint::zero(), FloatSize(100, 100)));
+ bool clipped = false;
+ CCMathUtil::mapQuad(transform, testQuad, clipped);
+ EXPECT_TRUE(clipped);
+
+ // Damage the child without moving it.
+ clearDamageForAllSurfaces(root.get());
+ child->setOpacity(0.5);
+ emulateDrawingOneFrame(root.get());
+
+ // The expected damage should cover the entire root surface (500x500), but we don't
+ // care whether the damage rect was clamped or is larger than the surface for this test.
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect damageWeCareAbout = FloatRect(FloatPoint::zero(), FloatSize(500, 500));
+ EXPECT_TRUE(rootDamageRect.contains(damageWeCareAbout));
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForBlurredSurface)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(5));
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ // Setting the filter will damage the whole surface.
+ clearDamageForAllSurfaces(root.get());
+ root->setFilters(filters);
+ emulateDrawingOneFrame(root.get());
+
+ // Setting the update rect should cause the corresponding damage to the surface, blurred based on the size of the blur filter.
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 12, 13));
+ emulateDrawingOneFrame(root.get());
+
+ // Damage position on the surface should be: position of updateRect (10, 11) relative to the child (100, 100), but expanded by the blur outsets.
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect expectedDamageRect = FloatRect(110, 111, 12, 13);
+ expectedDamageRect.move(-outsetLeft, -outsetTop);
+ expectedDamageRect.expand(outsetLeft + outsetRight, outsetTop + outsetBottom);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* child2 = root->children()[1].get();
+
+ // Allow us to set damage on child1 too.
+ child1->setDrawsContent(true);
+
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(2));
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ // Setting the filter will damage the whole surface.
+ clearDamageForAllSurfaces(root.get());
+ child1->setBackgroundFilters(filters);
+ emulateDrawingOneFrame(root.get());
+
+ // CASE 1: Setting the update rect should cause the corresponding damage to
+ // the surface, blurred based on the size of the child's background blur
+ // filter.
+ clearDamageForAllSurfaces(root.get());
+ root->setUpdateRect(FloatRect(297, 297, 2, 2));
+ emulateDrawingOneFrame(root.get());
+
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage position on the surface should be a composition of the damage on the root and on child2.
+ // Damage on the root should be: position of updateRect (297, 297), but expanded by the blur outsets.
+ FloatRect expectedDamageRect = FloatRect(297, 297, 2, 2);
+ expectedDamageRect.move(-outsetLeft, -outsetTop);
+ expectedDamageRect.expand(outsetLeft + outsetRight, outsetTop + outsetBottom);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+
+ // CASE 2: Setting the update rect should cause the corresponding damage to
+ // the surface, blurred based on the size of the child's background blur
+ // filter. Since the damage extends to the right/bottom outside of the
+ // blurred layer, only the left/top should end up expanded.
+ clearDamageForAllSurfaces(root.get());
+ root->setUpdateRect(FloatRect(297, 297, 30, 30));
+ emulateDrawingOneFrame(root.get());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage position on the surface should be a composition of the damage on the root and on child2.
+ // Damage on the root should be: position of updateRect (297, 297), but expanded on the left/top
+ // by the blur outsets.
+ expectedDamageRect = FloatRect(297, 297, 30, 30);
+ expectedDamageRect.move(-outsetLeft, -outsetTop);
+ expectedDamageRect.expand(outsetLeft, outsetTop);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+
+ // CASE 3: Setting this update rect outside the blurred contentBounds of the blurred
+ // child1 will not cause it to be expanded.
+ clearDamageForAllSurfaces(root.get());
+ root->setUpdateRect(FloatRect(30, 30, 2, 2));
+ emulateDrawingOneFrame(root.get());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage on the root should be: position of updateRect (30, 30), not
+ // expanded.
+ expectedDamageRect = FloatRect(30, 30, 2, 2);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+
+ // CASE 4: Setting this update rect inside the blurred contentBounds but outside the
+ // original contentBounds of the blurred child1 will cause it to be expanded.
+ clearDamageForAllSurfaces(root.get());
+ root->setUpdateRect(FloatRect(99, 99, 1, 1));
+ emulateDrawingOneFrame(root.get());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage on the root should be: position of updateRect (99, 99), expanded
+ // by the blurring on child1, but since it is 1 pixel outside the layer, the
+ // expanding should be reduced by 1.
+ expectedDamageRect = FloatRect(99, 99, 1, 1);
+ expectedDamageRect.move(-outsetLeft + 1, -outsetTop + 1);
+ expectedDamageRect.expand(outsetLeft + outsetRight - 1, outsetTop + outsetBottom - 1);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+
+ // CASE 5: Setting the update rect on child2, which is above child1, will
+ // not get blurred by child1, so it does not need to get expanded.
+ clearDamageForAllSurfaces(root.get());
+ child2->setUpdateRect(FloatRect(0, 0, 1, 1));
+ emulateDrawingOneFrame(root.get());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage on child2 should be: position of updateRect offset by the child's position (11, 11), and not expanded by anything.
+ expectedDamageRect = FloatRect(11, 11, 1, 1);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+
+ // CASE 6: Setting the update rect on child1 will also blur the damage, so
+ // that any pixels needed for the blur are redrawn in the current frame.
+ clearDamageForAllSurfaces(root.get());
+ child1->setUpdateRect(FloatRect(0, 0, 1, 1));
+ emulateDrawingOneFrame(root.get());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ // Damage on child1 should be: position of updateRect offset by the child's position (100, 100), and expanded by the damage.
+ expectedDamageRect = FloatRect(100, 100, 1, 1);
+ expectedDamageRect.move(-outsetLeft, -outsetTop);
+ expectedDamageRect.expand(outsetLeft + outsetRight, outsetTop + outsetBottom);
+ EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child1 = root->children()[0].get();
+
+ // CASE 1: Adding a new layer should cause the appropriate damage.
+ //
+ clearDamageForAllSurfaces(root.get());
+ {
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ child2->setPosition(FloatPoint(400, 380));
+ child2->setAnchorPoint(FloatPoint::zero());
+ child2->setBounds(IntSize(6, 8));
+ child2->setContentBounds(IntSize(6, 8));
+ child2->setDrawsContent(true);
+ root->addChild(child2.release());
+ }
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere.
+ ASSERT_EQ(3u, root->renderSurface()->layerList().size());
+
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(400, 380, 6, 8), rootDamageRect);
+
+ // CASE 2: If the layer is removed, its entire old layer becomes exposed, not just the
+ // last update rect.
+
+ // Advance one frame without damage so that we know the damage rect is not leftover from the previous case.
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ EXPECT_TRUE(root->renderSurface()->damageTracker()->currentDamageRect().isEmpty());
+
+ // Then, test removing child1.
+ child1->removeFromParent();
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(100, 100, 30, 30), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForNewUnchangedLayer)
+{
+ // If child2 is added to the layer tree, but it doesn't have any explicit damage of
+ // its own, it should still indeed damage the target surface.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+
+ clearDamageForAllSurfaces(root.get());
+ {
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ child2->setPosition(FloatPoint(400, 380));
+ child2->setAnchorPoint(FloatPoint::zero());
+ child2->setBounds(IntSize(6, 8));
+ child2->setContentBounds(IntSize(6, 8));
+ child2->setDrawsContent(true);
+ child2->resetAllChangeTrackingForSubtree();
+ // Sanity check the initial conditions of the test, if these asserts trigger, it
+ // means the test no longer actually covers the intended scenario.
+ ASSERT_FALSE(child2->layerPropertyChanged());
+ ASSERT_TRUE(child2->updateRect().isEmpty());
+ root->addChild(child2.release());
+ }
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere.
+ ASSERT_EQ(3u, root->renderSurface()->layerList().size());
+
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(400, 380, 6, 8), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForMultipleLayers)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child1 = root->children()[0].get();
+
+ // In this test we don't want the above tree manipulation to be considered part of the same frame.
+ clearDamageForAllSurfaces(root.get());
+ {
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ child2->setPosition(FloatPoint(400, 380));
+ child2->setAnchorPoint(FloatPoint::zero());
+ child2->setBounds(IntSize(6, 8));
+ child2->setContentBounds(IntSize(6, 8));
+ child2->setDrawsContent(true);
+ root->addChild(child2.release());
+ }
+ CCLayerImpl* child2 = root->children()[1].get();
+ emulateDrawingOneFrame(root.get());
+
+ // Damaging two layers simultaneously should cause combined damage.
+ // - child1 update rect in surface space: FloatRect(100, 100, 1, 2);
+ // - child2 update rect in surface space: FloatRect(400, 380, 3, 4);
+ clearDamageForAllSurfaces(root.get());
+ child1->setUpdateRect(FloatRect(0, 0, 1, 2));
+ child2->setUpdateRect(FloatRect(0, 0, 3, 4));
+ emulateDrawingOneFrame(root.get());
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(100, 100, 303, 284), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForNestedSurfaces)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* child2 = root->children()[1].get();
+ CCLayerImpl* grandChild1 = root->children()[0]->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ // CASE 1: Damage to a descendant surface should propagate properly to ancestor surface.
+ //
+ clearDamageForAllSurfaces(root.get());
+ grandChild1->setOpacity(0.5);
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(200, 200, 6, 8), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(300, 300, 6, 8), rootDamageRect);
+
+ // CASE 2: Same as previous case, but with additional damage elsewhere that should be properly unioned.
+ // - child1 surface damage in root surface space: FloatRect(300, 300, 6, 8);
+ // - child2 damage in root surface space: FloatRect(11, 11, 18, 18);
+ clearDamageForAllSurfaces(root.get());
+ grandChild1->setOpacity(0.7f);
+ child2->setOpacity(0.7f);
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(200, 200, 6, 8), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(11, 11, 295, 297), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForSurfaceChangeFromDescendantLayer)
+{
+ // If descendant layer changes and affects the content bounds of the render surface,
+ // then the entire descendant surface should be damaged, and it should damage its
+ // ancestor surface with the old and new surface regions.
+
+ // This is a tricky case, since only the first grandChild changes, but the entire
+ // surface should be marked dirty.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* grandChild1 = root->children()[0]->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ clearDamageForAllSurfaces(root.get());
+ grandChild1->setPosition(FloatPoint(195, 205));
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ // The new surface bounds should be damaged entirely, even though only one of the layers changed.
+ EXPECT_FLOAT_RECT_EQ(FloatRect(190, 190, 11, 23), childDamageRect);
+
+ // Damage to the root surface should be the union of child1's *entire* render surface
+ // (in target space), and its old exposed area (also in target space).
+ EXPECT_FLOAT_RECT_EQ(FloatRect(290, 290, 16, 23), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForSurfaceChangeFromAncestorLayer)
+{
+ // An ancestor/owning layer changes that affects the position/transform of the render
+ // surface. Note that in this case, the layerPropertyChanged flag already propagates
+ // to the subtree (tested in CCLayerImpltest), which damages the entire child1
+ // surface, but the damage tracker still needs the correct logic to compute the
+ // exposed region on the root surface.
+
+ // FIXME: the expectations of this test case should change when we add support for a
+ // unique scissorRect per renderSurface. In that case, the child1 surface
+ // should be completely unchanged, since we are only transforming it, while the
+ // root surface would be damaged appropriately.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ clearDamageForAllSurfaces(root.get());
+ child1->setPosition(FloatPoint(50, 50));
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ // The new surface bounds should be damaged entirely.
+ EXPECT_FLOAT_RECT_EQ(FloatRect(190, 190, 16, 18), childDamageRect);
+
+ // The entire child1 surface and the old exposed child1 surface should damage the root surface.
+ // - old child1 surface in target space: FloatRect(290, 290, 16, 18)
+ // - new child1 surface in target space: FloatRect(240, 240, 16, 18)
+ EXPECT_FLOAT_RECT_EQ(FloatRect(240, 240, 66, 68), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingRenderSurfaces)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ // CASE 1: If a descendant surface disappears, its entire old area becomes exposed.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child1->setOpacity(1);
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that there is only one surface now.
+ ASSERT_FALSE(child1->renderSurface());
+ ASSERT_EQ(4u, root->renderSurface()->layerList().size());
+
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(290, 290, 16, 18), rootDamageRect);
+
+ // CASE 2: If a descendant surface appears, its entire old area becomes exposed.
+
+ // Cycle one frame of no change, just to sanity check that the next rect is not because of the old damage state.
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+
+ // Then change the tree so that the render surface is added back.
+ clearDamageForAllSurfaces(root.get());
+ child1->setOpacity(0.5);
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that there is a new surface now.
+ ASSERT_TRUE(child1->renderSurface());
+ EXPECT_EQ(3u, root->renderSurface()->layerList().size());
+ EXPECT_EQ(2u, child1->renderSurface()->layerList().size());
+
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(190, 190, 16, 18), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(290, 290, 16, 18), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyNoDamageWhenNothingChanged)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ // CASE 1: If nothing changes, the damage rect should be empty.
+ //
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(childDamageRect.isEmpty());
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+
+ // CASE 2: If nothing changes twice in a row, the damage rect should still be empty.
+ //
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(childDamageRect.isEmpty());
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+}
+
+TEST_F(CCDamageTrackerTest, verifyNoDamageForUpdateRectThatDoesNotDrawContent)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ FloatRect childDamageRect;
+ FloatRect rootDamageRect;
+
+ // In our specific tree, the update rect of child1 should not cause any damage to any
+ // surface because it does not actually draw content.
+ clearDamageForAllSurfaces(root.get());
+ child1->setUpdateRect(FloatRect(0, 0, 1, 2));
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(childDamageRect.isEmpty());
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForReplica)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* grandChild1 = child1->children()[0].get();
+ CCLayerImpl* grandChild2 = child1->children()[1].get();
+
+ // Damage on a surface that has a reflection should cause the target surface to
+ // receive the surface's damage and the surface's reflected damage.
+
+ // For this test case, we modify grandChild2, and add grandChild3 to extend the bounds
+ // of child1's surface. This way, we can test reflection changes without changing
+ // contentBounds of the surface.
+ grandChild2->setPosition(FloatPoint(180, 180));
+ {
+ OwnPtr<CCLayerImpl> grandChild3 = CCLayerImpl::create(6);
+ grandChild3->setPosition(FloatPoint(240, 240));
+ grandChild3->setAnchorPoint(FloatPoint::zero());
+ grandChild3->setBounds(IntSize(10, 10));
+ grandChild3->setContentBounds(IntSize(10, 10));
+ grandChild3->setDrawsContent(true);
+ child1->addChild(grandChild3.release());
+ }
+ child1->setOpacity(0.5);
+ emulateDrawingOneFrame(root.get());
+
+ // CASE 1: adding a reflection about the left edge of grandChild1.
+ //
+ clearDamageForAllSurfaces(root.get());
+ {
+ OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(7);
+ grandChild1Replica->setPosition(FloatPoint::zero());
+ grandChild1Replica->setAnchorPoint(FloatPoint::zero());
+ WebTransformationMatrix reflection;
+ reflection.scale3d(-1, 1, 1);
+ grandChild1Replica->setTransform(reflection);
+ grandChild1->setReplicaLayer(grandChild1Replica.release());
+ }
+ emulateDrawingOneFrame(root.get());
+
+ FloatRect grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ // The grandChild surface damage should not include its own replica. The child
+ // surface damage should include the normal and replica surfaces.
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 6, 8), grandChildDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(194, 200, 12, 8), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(294, 300, 12, 8), rootDamageRect);
+
+ // CASE 2: moving the descendant surface should cause both the original and reflected
+ // areas to be damaged on the target.
+ clearDamageForAllSurfaces(root.get());
+ IntRect oldContentRect = child1->renderSurface()->contentRect();
+ grandChild1->setPosition(FloatPoint(195, 205));
+ emulateDrawingOneFrame(root.get());
+ ASSERT_EQ(oldContentRect.width(), child1->renderSurface()->contentRect().width());
+ ASSERT_EQ(oldContentRect.height(), child1->renderSurface()->contentRect().height());
+
+ grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect();
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ // The child surface damage should include normal and replica surfaces for both old and new locations.
+ // - old location in target space: FloatRect(194, 200, 12, 8)
+ // - new location in target space: FloatRect(189, 205, 12, 8)
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 6, 8), grandChildDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(189, 200, 17, 13), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(289, 300, 17, 13), rootDamageRect);
+
+ // CASE 3: removing the reflection should cause the entire region including reflection
+ // to damage the target surface.
+ clearDamageForAllSurfaces(root.get());
+ grandChild1->setReplicaLayer(nullptr);
+ emulateDrawingOneFrame(root.get());
+ ASSERT_EQ(oldContentRect.width(), child1->renderSurface()->contentRect().width());
+ ASSERT_EQ(oldContentRect.height(), child1->renderSurface()->contentRect().height());
+
+ EXPECT_FALSE(grandChild1->renderSurface());
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+
+ EXPECT_FLOAT_RECT_EQ(FloatRect(189, 205, 12, 8), childDamageRect);
+ EXPECT_FLOAT_RECT_EQ(FloatRect(289, 305, 12, 8), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForMask)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ // In the current implementation of the damage tracker, changes to mask layers should
+ // damage the entire corresponding surface.
+
+ clearDamageForAllSurfaces(root.get());
+
+ // Set up the mask layer.
+ {
+ OwnPtr<CCLayerImpl> maskLayer = CCLayerImpl::create(3);
+ maskLayer->setPosition(child->position());
+ maskLayer->setAnchorPoint(FloatPoint::zero());
+ maskLayer->setBounds(child->bounds());
+ maskLayer->setContentBounds(child->bounds());
+ child->setMaskLayer(maskLayer.release());
+ }
+ CCLayerImpl* maskLayer = child->maskLayer();
+
+ // Add opacity and a grandChild so that the render surface persists even after we remove the mask.
+ child->setOpacity(0.5);
+ {
+ OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(4);
+ grandChild->setPosition(FloatPoint(2, 2));
+ grandChild->setAnchorPoint(FloatPoint::zero());
+ grandChild->setBounds(IntSize(2, 2));
+ grandChild->setContentBounds(IntSize(2, 2));
+ grandChild->setDrawsContent(true);
+ child->addChild(grandChild.release());
+ }
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that a new surface was created for the child.
+ ASSERT_TRUE(child->renderSurface());
+
+ // CASE 1: the updateRect on a mask layer should damage the entire target surface.
+ //
+ clearDamageForAllSurfaces(root.get());
+ maskLayer->setUpdateRect(FloatRect(1, 2, 3, 4));
+ emulateDrawingOneFrame(root.get());
+ FloatRect childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 30, 30), childDamageRect);
+
+ // CASE 2: a property change on the mask layer should damage the entire target surface.
+ //
+
+ // Advance one frame without damage so that we know the damage rect is not leftover from the previous case.
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(childDamageRect.isEmpty());
+
+ // Then test the property change.
+ clearDamageForAllSurfaces(root.get());
+ maskLayer->setStackingOrderChanged(true);
+
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 30, 30), childDamageRect);
+
+ // CASE 3: removing the mask also damages the entire target surface.
+ //
+
+ // Advance one frame without damage so that we know the damage rect is not leftover from the previous case.
+ clearDamageForAllSurfaces(root.get());
+ emulateDrawingOneFrame(root.get());
+ childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(childDamageRect.isEmpty());
+
+ // Then test mask removal.
+ clearDamageForAllSurfaces(root.get());
+ child->setMaskLayer(nullptr);
+ ASSERT_TRUE(child->layerPropertyChanged());
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that a render surface still exists.
+ ASSERT_TRUE(child->renderSurface());
+
+ childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 30, 30), childDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* grandChild1 = child1->children()[0].get();
+
+ // Changes to a replica's mask should not damage the original surface, because it is
+ // not masked. But it does damage the ancestor target surface.
+
+ clearDamageForAllSurfaces(root.get());
+
+ // Create a reflection about the left edge of grandChild1.
+ {
+ OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(6);
+ grandChild1Replica->setPosition(FloatPoint::zero());
+ grandChild1Replica->setAnchorPoint(FloatPoint::zero());
+ WebTransformationMatrix reflection;
+ reflection.scale3d(-1, 1, 1);
+ grandChild1Replica->setTransform(reflection);
+ grandChild1->setReplicaLayer(grandChild1Replica.release());
+ }
+ CCLayerImpl* grandChild1Replica = grandChild1->replicaLayer();
+
+ // Set up the mask layer on the replica layer
+ {
+ OwnPtr<CCLayerImpl> replicaMaskLayer = CCLayerImpl::create(7);
+ replicaMaskLayer->setPosition(FloatPoint::zero());
+ replicaMaskLayer->setAnchorPoint(FloatPoint::zero());
+ replicaMaskLayer->setBounds(grandChild1->bounds());
+ replicaMaskLayer->setContentBounds(grandChild1->bounds());
+ grandChild1Replica->setMaskLayer(replicaMaskLayer.release());
+ }
+ CCLayerImpl* replicaMaskLayer = grandChild1Replica->maskLayer();
+
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that the appropriate render surfaces were created
+ ASSERT_TRUE(grandChild1->renderSurface());
+
+ // CASE 1: a property change on the mask should damage only the reflected region on the target surface.
+ clearDamageForAllSurfaces(root.get());
+ replicaMaskLayer->setStackingOrderChanged(true);
+ emulateDrawingOneFrame(root.get());
+
+ FloatRect grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect();
+ FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+
+ EXPECT_TRUE(grandChildDamageRect.isEmpty());
+ EXPECT_FLOAT_RECT_EQ(FloatRect(194, 200, 6, 8), childDamageRect);
+
+ // CASE 2: removing the replica mask damages only the reflected region on the target surface.
+ //
+ clearDamageForAllSurfaces(root.get());
+ grandChild1Replica->setMaskLayer(nullptr);
+ emulateDrawingOneFrame(root.get());
+
+ grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect();
+ childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+
+ EXPECT_TRUE(grandChildDamageRect.isEmpty());
+ EXPECT_FLOAT_RECT_EQ(FloatRect(194, 200, 6, 8), childDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMaskWithAnchor)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces();
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* grandChild1 = child1->children()[0].get();
+
+ // Verify that the correct replicaOriginTransform is used for the replicaMask;
+ clearDamageForAllSurfaces(root.get());
+
+ grandChild1->setAnchorPoint(FloatPoint(1, 0)); // This is not exactly the anchor being tested, but by convention its expected to be the same as the replica's anchor point.
+
+ {
+ OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(6);
+ grandChild1Replica->setPosition(FloatPoint::zero());
+ grandChild1Replica->setAnchorPoint(FloatPoint(1, 0)); // This is the anchor being tested.
+ WebTransformationMatrix reflection;
+ reflection.scale3d(-1, 1, 1);
+ grandChild1Replica->setTransform(reflection);
+ grandChild1->setReplicaLayer(grandChild1Replica.release());
+ }
+ CCLayerImpl* grandChild1Replica = grandChild1->replicaLayer();
+
+ // Set up the mask layer on the replica layer
+ {
+ OwnPtr<CCLayerImpl> replicaMaskLayer = CCLayerImpl::create(7);
+ replicaMaskLayer->setPosition(FloatPoint::zero());
+ replicaMaskLayer->setAnchorPoint(FloatPoint::zero()); // note, this is not the anchor being tested.
+ replicaMaskLayer->setBounds(grandChild1->bounds());
+ replicaMaskLayer->setContentBounds(grandChild1->bounds());
+ grandChild1Replica->setMaskLayer(replicaMaskLayer.release());
+ }
+ CCLayerImpl* replicaMaskLayer = grandChild1Replica->maskLayer();
+
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check that the appropriate render surfaces were created
+ ASSERT_TRUE(grandChild1->renderSurface());
+
+ // A property change on the replicaMask should damage the reflected region on the target surface.
+ clearDamageForAllSurfaces(root.get());
+ replicaMaskLayer->setStackingOrderChanged(true);
+
+ emulateDrawingOneFrame(root.get());
+
+ FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(206, 200, 6, 8), childDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageWhenForcedFullDamage)
+{
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ // Case 1: This test ensures that when the tracker is forced to have full damage, that
+ // it takes priority over any other partial damage.
+ //
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 12, 13));
+ root->renderSurface()->damageTracker()->forceFullDamageNextUpdate();
+ emulateDrawingOneFrame(root.get());
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 500, 500), rootDamageRect);
+
+ // Case 2: An additional sanity check that forcing full damage works even when nothing
+ // on the layer tree changed.
+ //
+ clearDamageForAllSurfaces(root.get());
+ root->renderSurface()->damageTracker()->forceFullDamageNextUpdate();
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 500, 500), rootDamageRect);
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageForEmptyLayerList)
+{
+ // Though it should never happen, its a good idea to verify that the damage tracker
+ // does not crash when it receives an empty layerList.
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->createRenderSurface();
+
+ ASSERT_TRUE(root == root->renderTarget());
+ CCRenderSurface* targetSurface = root->renderSurface();
+ targetSurface->clearLayerList();
+ targetSurface->damageTracker()->updateDamageTrackingState(targetSurface->layerList(), targetSurface->owningLayerId(), false, IntRect(), 0, WebFilterOperations());
+
+ FloatRect damageRect = targetSurface->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(damageRect.isEmpty());
+}
+
+TEST_F(CCDamageTrackerTest, verifyDamageAccumulatesUntilReset)
+{
+ // If damage is not cleared, it should accumulate.
+
+ OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface();
+ CCLayerImpl* child = root->children()[0].get();
+
+ clearDamageForAllSurfaces(root.get());
+ child->setUpdateRect(FloatRect(10, 11, 1, 2));
+ emulateDrawingOneFrame(root.get());
+
+ // Sanity check damage after the first frame; this isnt the actual test yet.
+ FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 1, 2), rootDamageRect);
+
+ // New damage, without having cleared the previous damage, should be unioned to the previous one.
+ child->setUpdateRect(FloatRect(20, 25, 1, 2));
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 11, 16), rootDamageRect);
+
+ // If we notify the damage tracker that we drew the damaged area, then damage should be emptied.
+ root->renderSurface()->damageTracker()->didDrawDamagedArea();
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+
+ // Damage should remain empty even after one frame, since there's yet no new damage
+ emulateDrawingOneFrame(root.get());
+ rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect();
+ EXPECT_TRUE(rootDamageRect.isEmpty());
+}
+
+} // namespace
diff --git a/cc/CCDebugBorderDrawQuad.cpp b/cc/CCDebugBorderDrawQuad.cpp
new file mode 100644
index 0000000..d819824
--- /dev/null
+++ b/cc/CCDebugBorderDrawQuad.cpp
@@ -0,0 +1,32 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCDebugBorderDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCDebugBorderDrawQuad> CCDebugBorderDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, SkColor color, int width)
+{
+ return adoptPtr(new CCDebugBorderDrawQuad(sharedQuadState, quadRect, color, width));
+}
+
+CCDebugBorderDrawQuad::CCDebugBorderDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, SkColor color, int width)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::DebugBorder, quadRect)
+ , m_color(color)
+ , m_width(width)
+{
+ m_quadOpaque = false;
+ if (SkColorGetA(m_color) < 255)
+ m_needsBlending = true;
+}
+
+const CCDebugBorderDrawQuad* CCDebugBorderDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::DebugBorder);
+ return static_cast<const CCDebugBorderDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCDebugBorderDrawQuad.h b/cc/CCDebugBorderDrawQuad.h
new file mode 100644
index 0000000..77f8877
--- /dev/null
+++ b/cc/CCDebugBorderDrawQuad.h
@@ -0,0 +1,35 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDebugBorderDrawQuad_h
+#define CCDebugBorderDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "SkColor.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCDebugBorderDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCDebugBorderDrawQuad> create(const CCSharedQuadState*, const IntRect&, SkColor, int width);
+
+ SkColor color() const { return m_color; };
+ int width() const { return m_width; }
+
+ static const CCDebugBorderDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCDebugBorderDrawQuad(const CCSharedQuadState*, const IntRect&, SkColor, int width);
+
+ SkColor m_color;
+ int m_width;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCDebugRectHistory.cpp b/cc/CCDebugRectHistory.cpp
new file mode 100644
index 0000000..6cd7c99
--- /dev/null
+++ b/cc/CCDebugRectHistory.cpp
@@ -0,0 +1,116 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "CCDebugRectHistory.h"
+
+#include "CCDamageTracker.h"
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHost.h"
+#include "CCMathUtil.h"
+
+namespace WebCore {
+
+CCDebugRectHistory::CCDebugRectHistory()
+{
+}
+
+void CCDebugRectHistory::saveDebugRectsForCurrentFrame(CCLayerImpl* rootLayer, const Vector<CCLayerImpl*>& renderSurfaceLayerList, const Vector<IntRect>& occludingScreenSpaceRects, const CCLayerTreeSettings& settings)
+{
+ // For now, clear all rects from previous frames. In the future we may want to store
+ // all debug rects for a history of many frames.
+ m_debugRects.clear();
+
+ if (settings.showPaintRects)
+ savePaintRects(rootLayer);
+
+ if (settings.showPropertyChangedRects)
+ savePropertyChangedRects(renderSurfaceLayerList);
+
+ if (settings.showSurfaceDamageRects)
+ saveSurfaceDamageRects(renderSurfaceLayerList);
+
+ if (settings.showScreenSpaceRects)
+ saveScreenSpaceRects(renderSurfaceLayerList);
+
+ if (settings.showOccludingRects)
+ saveOccludingRects(occludingScreenSpaceRects);
+}
+
+
+void CCDebugRectHistory::savePaintRects(CCLayerImpl* layer)
+{
+ // We would like to visualize where any layer's paint rect (update rect) has changed,
+ // regardless of whether this layer is skipped for actual drawing or not. Therefore
+ // we traverse recursively over all layers, not just the render surface list.
+
+ if (!layer->updateRect().isEmpty() && layer->drawsContent()) {
+ FloatRect updateContentRect = layer->updateRect();
+ updateContentRect.scale(layer->contentBounds().width() / static_cast<float>(layer->bounds().width()), layer->contentBounds().height() / static_cast<float>(layer->bounds().height()));
+ m_debugRects.append(CCDebugRect(PaintRectType, CCMathUtil::mapClippedRect(layer->screenSpaceTransform(), updateContentRect)));
+ }
+
+ for (unsigned i = 0; i < layer->children().size(); ++i)
+ savePaintRects(layer->children()[i].get());
+}
+
+void CCDebugRectHistory::savePropertyChangedRects(const Vector<CCLayerImpl*>& renderSurfaceLayerList)
+{
+ for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
+ CCLayerImpl* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex];
+ CCRenderSurface* renderSurface = renderSurfaceLayer->renderSurface();
+ ASSERT(renderSurface);
+
+ const Vector<CCLayerImpl*>& layerList = renderSurface->layerList();
+ for (unsigned layerIndex = 0; layerIndex < layerList.size(); ++layerIndex) {
+ CCLayerImpl* layer = layerList[layerIndex];
+
+ if (CCLayerTreeHostCommon::renderSurfaceContributesToTarget<CCLayerImpl>(layer, renderSurfaceLayer->id()))
+ continue;
+
+ if (layer->layerIsAlwaysDamaged())
+ continue;
+
+ if (layer->layerPropertyChanged() || layer->layerSurfacePropertyChanged())
+ m_debugRects.append(CCDebugRect(PropertyChangedRectType, CCMathUtil::mapClippedRect(layer->screenSpaceTransform(), FloatRect(FloatPoint::zero(), layer->contentBounds()))));
+ }
+ }
+}
+
+void CCDebugRectHistory::saveSurfaceDamageRects(const Vector<CCLayerImpl* >& renderSurfaceLayerList)
+{
+ for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
+ CCLayerImpl* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex];
+ CCRenderSurface* renderSurface = renderSurfaceLayer->renderSurface();
+ ASSERT(renderSurface);
+
+ m_debugRects.append(CCDebugRect(SurfaceDamageRectType, CCMathUtil::mapClippedRect(renderSurface->screenSpaceTransform(), renderSurface->damageTracker()->currentDamageRect())));
+ }
+}
+
+void CCDebugRectHistory::saveScreenSpaceRects(const Vector<CCLayerImpl* >& renderSurfaceLayerList)
+{
+ for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
+ CCLayerImpl* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex];
+ CCRenderSurface* renderSurface = renderSurfaceLayer->renderSurface();
+ ASSERT(renderSurface);
+
+ m_debugRects.append(CCDebugRect(ScreenSpaceRectType, CCMathUtil::mapClippedRect(renderSurface->screenSpaceTransform(), renderSurface->contentRect())));
+
+ if (renderSurfaceLayer->replicaLayer())
+ m_debugRects.append(CCDebugRect(ReplicaScreenSpaceRectType, CCMathUtil::mapClippedRect(renderSurface->replicaScreenSpaceTransform(), renderSurface->contentRect())));
+ }
+}
+
+void CCDebugRectHistory::saveOccludingRects(const Vector<IntRect>& occludingRects)
+{
+ for (size_t i = 0; i < occludingRects.size(); ++i)
+ m_debugRects.append(CCDebugRect(OccludingRectType, occludingRects[i]));
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCDebugRectHistory.h b/cc/CCDebugRectHistory.h
new file mode 100644
index 0000000..af542ad
--- /dev/null
+++ b/cc/CCDebugRectHistory.h
@@ -0,0 +1,83 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDebugRectHistory_h
+#define CCDebugRectHistory_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCLayerImpl;
+struct CCLayerTreeSettings;
+
+// There are currently six types of debug rects:
+//
+// - Paint rects (update rects): regions of a layer that needed to be re-uploaded to the
+// texture resource; in most cases implying that WebCore had to repaint them, too.
+//
+// - Property-changed rects: enclosing bounds of layers that cause changes to the screen
+// even if the layer did not change internally. (For example, if the layer's opacity or
+// position changes.)
+//
+// - Surface damage rects: the aggregate damage on a target surface that is caused by all
+// layers and surfaces that contribute to it. This includes (1) paint rects, (2) property-
+// changed rects, and (3) newly exposed areas.
+//
+// - Screen space rects: this is the region the contents occupy in screen space.
+//
+// - Replica screen space rects: this is the region the replica's contents occupy in screen space.
+//
+// - Occluding rects: these are the regions that contribute to the occluded region.
+//
+enum DebugRectType { PaintRectType, PropertyChangedRectType, SurfaceDamageRectType, ScreenSpaceRectType, ReplicaScreenSpaceRectType, OccludingRectType };
+
+struct CCDebugRect {
+ CCDebugRect(DebugRectType newType, FloatRect newRect)
+ : type(newType)
+ , rect(newRect) { }
+
+ DebugRectType type;
+ FloatRect rect;
+};
+
+// This class maintains a history of rects of various types that can be used
+// for debugging purposes. The overhead of collecting rects is performed only if
+// the appropriate CCLayerTreeSettings are enabled.
+class CCDebugRectHistory {
+ WTF_MAKE_NONCOPYABLE(CCDebugRectHistory);
+public:
+ static PassOwnPtr<CCDebugRectHistory> create()
+ {
+ return adoptPtr(new CCDebugRectHistory());
+ }
+
+ // Note: Saving debug rects must happen before layers' change tracking is reset.
+ void saveDebugRectsForCurrentFrame(CCLayerImpl* rootLayer, const Vector<CCLayerImpl*>& renderSurfaceLayerList, const Vector<IntRect>& occludingScreenSpaceRects, const CCLayerTreeSettings&);
+
+ const Vector<CCDebugRect>& debugRects() { return m_debugRects; }
+
+private:
+ CCDebugRectHistory();
+
+ void savePaintRects(CCLayerImpl*);
+ void savePropertyChangedRects(const Vector<CCLayerImpl*>& renderSurfaceLayerList);
+ void saveSurfaceDamageRects(const Vector<CCLayerImpl* >& renderSurfaceLayerList);
+ void saveScreenSpaceRects(const Vector<CCLayerImpl* >& renderSurfaceLayerList);
+ void saveOccludingRects(const Vector<IntRect>& occludingScreenSpaceRects);
+
+ Vector<CCDebugRect> m_debugRects;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCDelayBasedTimeSource.cpp b/cc/CCDelayBasedTimeSource.cpp
new file mode 100644
index 0000000..344e52c
--- /dev/null
+++ b/cc/CCDelayBasedTimeSource.cpp
@@ -0,0 +1,220 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCDelayBasedTimeSource.h"
+
+#include "TraceEvent.h"
+#include <algorithm>
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+namespace {
+
+// doubleTickThreshold prevents ticks from running within the specified fraction of an interval.
+// This helps account for jitter in the timebase as well as quick timer reactivation.
+const double doubleTickThreshold = 0.25;
+
+// intervalChangeThreshold is the fraction of the interval that will trigger an immediate interval change.
+// phaseChangeThreshold is the fraction of the interval that will trigger an immediate phase change.
+// If the changes are within the thresholds, the change will take place on the next tick.
+// If either change is outside the thresholds, the next tick will be canceled and reissued immediately.
+const double intervalChangeThreshold = 0.25;
+const double phaseChangeThreshold = 0.25;
+
+}
+
+
+PassRefPtr<CCDelayBasedTimeSource> CCDelayBasedTimeSource::create(double interval, CCThread* thread)
+{
+ return adoptRef(new CCDelayBasedTimeSource(interval, thread));
+}
+
+CCDelayBasedTimeSource::CCDelayBasedTimeSource(double intervalSeconds, CCThread* thread)
+ : m_client(0)
+ , m_hasTickTarget(false)
+ , m_lastTickTime(0)
+ , m_currentParameters(intervalSeconds, 0)
+ , m_nextParameters(intervalSeconds, 0)
+ , m_state(STATE_INACTIVE)
+ , m_timer(thread, this)
+{
+}
+
+void CCDelayBasedTimeSource::setActive(bool active)
+{
+ TRACE_EVENT1("cc", "CCDelayBasedTimeSource::setActive", "active", active);
+ if (!active) {
+ m_state = STATE_INACTIVE;
+ m_timer.stop();
+ return;
+ }
+
+ if (m_state == STATE_STARTING || m_state == STATE_ACTIVE)
+ return;
+
+ if (!m_hasTickTarget) {
+ // Becoming active the first time is deferred: we post a 0-delay task. When
+ // it runs, we use that to establish the timebase, become truly active, and
+ // fire the first tick.
+ m_state = STATE_STARTING;
+ m_timer.startOneShot(0);
+ return;
+ }
+
+ m_state = STATE_ACTIVE;
+
+ double now = monotonicTimeNow();
+ postNextTickTask(now);
+}
+
+double CCDelayBasedTimeSource::lastTickTime()
+{
+ return m_lastTickTime;
+}
+
+double CCDelayBasedTimeSource::nextTickTimeIfActivated()
+{
+ return active() ? m_currentParameters.tickTarget : nextTickTarget(monotonicTimeNow());
+}
+
+void CCDelayBasedTimeSource::onTimerFired()
+{
+ ASSERT(m_state != STATE_INACTIVE);
+
+ double now = monotonicTimeNow();
+ m_lastTickTime = now;
+
+ if (m_state == STATE_STARTING) {
+ setTimebaseAndInterval(now, m_currentParameters.interval);
+ m_state = STATE_ACTIVE;
+ }
+
+ postNextTickTask(now);
+
+ // Fire the tick
+ if (m_client)
+ m_client->onTimerTick();
+}
+
+void CCDelayBasedTimeSource::setTimebaseAndInterval(double timebase, double intervalSeconds)
+{
+ m_nextParameters.interval = intervalSeconds;
+ m_nextParameters.tickTarget = timebase;
+ m_hasTickTarget = true;
+
+ if (m_state != STATE_ACTIVE) {
+ // If we aren't active, there's no need to reset the timer.
+ return;
+ }
+
+ // If the change in interval is larger than the change threshold,
+ // request an immediate reset.
+ double intervalDelta = std::abs(intervalSeconds - m_currentParameters.interval);
+ double intervalChange = intervalDelta / intervalSeconds;
+ if (intervalChange > intervalChangeThreshold) {
+ setActive(false);
+ setActive(true);
+ return;
+ }
+
+ // If the change in phase is greater than the change threshold in either
+ // direction, request an immediate reset. This logic might result in a false
+ // negative if there is a simultaneous small change in the interval and the
+ // fmod just happens to return something near zero. Assuming the timebase
+ // is very recent though, which it should be, we'll still be ok because the
+ // old clock and new clock just happen to line up.
+ double targetDelta = std::abs(timebase - m_currentParameters.tickTarget);
+ double phaseChange = fmod(targetDelta, intervalSeconds) / intervalSeconds;
+ if (phaseChange > phaseChangeThreshold && phaseChange < (1.0 - phaseChangeThreshold)) {
+ setActive(false);
+ setActive(true);
+ return;
+ }
+}
+
+double CCDelayBasedTimeSource::monotonicTimeNow() const
+{
+ return monotonicallyIncreasingTime();
+}
+
+// This code tries to achieve an average tick rate as close to m_intervalMs as possible.
+// To do this, it has to deal with a few basic issues:
+// 1. postDelayedTask can delay only at a millisecond granularity. So, 16.666 has to
+// posted as 16 or 17.
+// 2. A delayed task may come back a bit late (a few ms), or really late (frames later)
+//
+// The basic idea with this scheduler here is to keep track of where we *want* to run in
+// m_tickTarget. We update this with the exact interval.
+//
+// Then, when we post our task, we take the floor of (m_tickTarget and now()). If we
+// started at now=0, and 60FPs:
+// now=0 target=16.667 postDelayedTask(16)
+//
+// When our callback runs, we figure out how far off we were from that goal. Because of the flooring
+// operation, and assuming our timer runs exactly when it should, this yields:
+// now=16 target=16.667
+//
+// Since we can't post a 0.667 ms task to get to now=16, we just treat this as a tick. Then,
+// we update target to be 33.333. We now post another task based on the difference between our target
+// and now:
+// now=16 tickTarget=16.667 newTarget=33.333 --> postDelayedTask(floor(33.333 - 16)) --> postDelayedTask(17)
+//
+// Over time, with no late tasks, this leads to us posting tasks like this:
+// now=0 tickTarget=0 newTarget=16.667 --> tick(), postDelayedTask(16)
+// now=16 tickTarget=16.667 newTarget=33.333 --> tick(), postDelayedTask(17)
+// now=33 tickTarget=33.333 newTarget=50.000 --> tick(), postDelayedTask(17)
+// now=50 tickTarget=50.000 newTarget=66.667 --> tick(), postDelayedTask(16)
+//
+// We treat delays in tasks differently depending on the amount of delay we encounter. Suppose we
+// posted a task with a target=16.667:
+// Case 1: late but not unrecoverably-so
+// now=18 tickTarget=16.667
+//
+// Case 2: so late we obviously missed the tick
+// now=25.0 tickTarget=16.667
+//
+// We treat the first case as a tick anyway, and assume the delay was
+// unusual. Thus, we compute the newTarget based on the old timebase:
+// now=18 tickTarget=16.667 newTarget=33.333 --> tick(), postDelayedTask(floor(33.333-18)) --> postDelayedTask(15)
+// This brings us back to 18+15 = 33, which was where we would have been if the task hadn't been late.
+//
+// For the really late delay, we we move to the next logical tick. The timebase is not reset.
+// now=37 tickTarget=16.667 newTarget=50.000 --> tick(), postDelayedTask(floor(50.000-37)) --> postDelayedTask(13)
+//
+// Note, that in the above discussion, times are expressed in milliseconds, but in the code, seconds are used.
+double CCDelayBasedTimeSource::nextTickTarget(double now)
+{
+ double newInterval = m_nextParameters.interval;
+ double intervalsElapsed = floor((now - m_nextParameters.tickTarget) / newInterval);
+ double lastEffectiveTick = m_nextParameters.tickTarget + newInterval * intervalsElapsed;
+ double newTickTarget = lastEffectiveTick + newInterval;
+ ASSERT(newTickTarget > now);
+
+ // Avoid double ticks when:
+ // 1) Turning off the timer and turning it right back on.
+ // 2) Jittery data is passed to setTimebaseAndInterval().
+ if (newTickTarget - m_lastTickTime <= newInterval * doubleTickThreshold)
+ newTickTarget += newInterval;
+
+ return newTickTarget;
+}
+
+void CCDelayBasedTimeSource::postNextTickTask(double now)
+{
+ double newTickTarget = nextTickTarget(now);
+
+ // Post another task *before* the tick and update state
+ double delay = newTickTarget - now;
+ ASSERT(delay <= m_nextParameters.interval * (1.0 + doubleTickThreshold));
+ m_timer.startOneShot(delay);
+
+ m_nextParameters.tickTarget = newTickTarget;
+ m_currentParameters = m_nextParameters;
+}
+
+}
diff --git a/cc/CCDelayBasedTimeSource.h b/cc/CCDelayBasedTimeSource.h
new file mode 100644
index 0000000..9565983
--- /dev/null
+++ b/cc/CCDelayBasedTimeSource.h
@@ -0,0 +1,78 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDelayBasedTimeSource_h
+#define CCDelayBasedTimeSource_h
+
+#include "CCTimeSource.h"
+#include "CCTimer.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class CCThread;
+
+// This timer implements a time source that achieves the specified interval
+// in face of millisecond-precision delayed callbacks and random queueing delays.
+class CCDelayBasedTimeSource : public CCTimeSource, CCTimerClient {
+public:
+ static PassRefPtr<CCDelayBasedTimeSource> create(double intervalSeconds, CCThread*);
+
+ virtual ~CCDelayBasedTimeSource() { }
+
+ virtual void setClient(CCTimeSourceClient* client) OVERRIDE { m_client = client; }
+
+ // CCTimeSource implementation
+ virtual void setTimebaseAndInterval(double timebase, double intervalSeconds) OVERRIDE;
+
+ virtual void setActive(bool) OVERRIDE;
+ virtual bool active() const OVERRIDE { return m_state != STATE_INACTIVE; }
+
+ // Get the last and next tick times.
+ virtual double lastTickTime() OVERRIDE;
+ virtual double nextTickTimeIfActivated() OVERRIDE;
+
+ // CCTimerClient implementation.
+ virtual void onTimerFired() OVERRIDE;
+
+ // Virtual for testing.
+ virtual double monotonicTimeNow() const;
+
+protected:
+ CCDelayBasedTimeSource(double interval, CCThread*);
+ double nextTickTarget(double now);
+ void postNextTickTask(double now);
+
+ enum State {
+ STATE_INACTIVE,
+ STATE_STARTING,
+ STATE_ACTIVE,
+ };
+
+ struct Parameters {
+ Parameters(double interval, double tickTarget)
+ : interval(interval), tickTarget(tickTarget)
+ { }
+ double interval;
+ double tickTarget;
+ };
+
+ CCTimeSourceClient* m_client;
+ bool m_hasTickTarget;
+ double m_lastTickTime;
+
+ // m_currentParameters should only be written by postNextTickTask.
+ // m_nextParameters will take effect on the next call to postNextTickTask.
+ // Maintaining a pending set of parameters allows nextTickTime() to always
+ // reflect the actual time we expect onTimerFired to be called.
+ Parameters m_currentParameters;
+ Parameters m_nextParameters;
+
+ State m_state;
+ CCThread* m_thread;
+ CCTimer m_timer;
+};
+
+}
+#endif // CCDelayBasedTimeSource_h
diff --git a/cc/CCDelayBasedTimeSourceTest.cpp b/cc/CCDelayBasedTimeSourceTest.cpp
new file mode 100644
index 0000000..5e9f49b
--- /dev/null
+++ b/cc/CCDelayBasedTimeSourceTest.cpp
@@ -0,0 +1,385 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCDelayBasedTimeSource.h"
+
+#include "CCSchedulerTestCommon.h"
+#include "CCThread.h"
+#include <gtest/gtest.h>
+#include <wtf/RefPtr.h>
+
+using namespace WTF;
+using namespace WebCore;
+using namespace WebKitTests;
+
+namespace {
+
+TEST(CCDelayBasedTimeSourceTest, TaskPostedAndTickCalled)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+
+ timer->setMonotonicTimeNow(0);
+ timer->setActive(true);
+ EXPECT_TRUE(timer->active());
+ EXPECT_TRUE(thread.hasPendingTask());
+
+ timer->setMonotonicTimeNow(0.016);
+ thread.runPendingTask();
+ EXPECT_TRUE(timer->active());
+ EXPECT_TRUE(client.tickCalled());
+}
+
+TEST(CCDelayBasedTimeSource, TickNotCalledWithTaskPosted)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ EXPECT_TRUE(thread.hasPendingTask());
+ timer->setActive(false);
+ thread.runPendingTask();
+ EXPECT_FALSE(client.tickCalled());
+}
+
+TEST(CCDelayBasedTimeSource, StartTwiceEnqueuesOneTask)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ EXPECT_TRUE(thread.hasPendingTask());
+ thread.reset();
+ timer->setActive(true);
+ EXPECT_FALSE(thread.hasPendingTask());
+}
+
+TEST(CCDelayBasedTimeSource, StartWhenRunningDoesntTick)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ thread.runPendingTask();
+ thread.reset();
+ timer->setActive(true);
+ EXPECT_FALSE(thread.hasPendingTask());
+}
+
+// At 60Hz, when the tick returns at exactly the requested next time, make sure
+// a 16ms next delay is posted.
+TEST(CCDelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ timer->setMonotonicTimeNow(interval);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+}
+
+// At 60Hz, when the tick returns at slightly after the requested next time, make sure
+// a 16ms next delay is posted.
+TEST(CCDelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ timer->setMonotonicTimeNow(interval + 0.0000001);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+}
+
+// At 60Hz, when the tick returns at exactly 2*interval after the requested next time, make sure
+// a 16ms next delay is posted.
+TEST(CCDelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ timer->setMonotonicTimeNow(2*interval);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+}
+
+// At 60Hz, when the tick returns at 2*interval and a bit after the requested next time, make sure
+// a 16ms next delay is posted.
+TEST(CCDelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ timer->setMonotonicTimeNow(2*interval + 0.0000001);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+}
+
+// At 60Hz, when the tick returns halfway to the next frame time, make sure
+// a correct next delay value is posted.
+TEST(CCDelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ timer->setMonotonicTimeNow(interval + interval * 0.5);
+ thread.runPendingTask();
+
+ EXPECT_EQ(8, thread.pendingDelayMs());
+}
+
+// If the timebase and interval are updated with a jittery source, we want to
+// make sure we do not double tick.
+TEST(CCDelayBasedTimeSource, SaneHandlingOfJitteryTimebase)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ // Jitter timebase ~1ms late
+ timer->setTimebaseAndInterval(interval + 0.001, interval);
+ timer->setMonotonicTimeNow(interval);
+ thread.runPendingTask();
+
+ // Without double tick prevention, pendingDelayMs would be 1.
+ EXPECT_EQ(17, thread.pendingDelayMs());
+
+ // Jitter timebase ~1ms early
+ timer->setTimebaseAndInterval(interval * 2 - 0.001, interval);
+ timer->setMonotonicTimeNow(interval * 2);
+ thread.runPendingTask();
+
+ EXPECT_EQ(15, thread.pendingDelayMs());
+}
+
+TEST(CCDelayBasedTimeSource, HanldlesSignificantTimebaseChangesImmediately)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ // Tick, then shift timebase by +7ms.
+ timer->setMonotonicTimeNow(interval);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ client.reset();
+ thread.runPendingTaskOnOverwrite(true);
+ timer->setTimebaseAndInterval(interval + 0.0070001, interval);
+ thread.runPendingTaskOnOverwrite(false);
+
+ EXPECT_FALSE(client.tickCalled()); // Make sure pending tasks were canceled.
+ EXPECT_EQ(7, thread.pendingDelayMs());
+
+ // Tick, then shift timebase by -7ms.
+ timer->setMonotonicTimeNow(interval + 0.0070001);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ client.reset();
+ thread.runPendingTaskOnOverwrite(true);
+ timer->setTimebaseAndInterval(interval, interval);
+ thread.runPendingTaskOnOverwrite(false);
+
+ EXPECT_FALSE(client.tickCalled()); // Make sure pending tasks were canceled.
+ EXPECT_EQ(16-7, thread.pendingDelayMs());
+}
+
+TEST(CCDelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ double interval = 1.0 / 60.0;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+ // Run the first task, as that activates the timer and picks up a timebase.
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ // Tick, then double the interval.
+ timer->setMonotonicTimeNow(interval);
+ thread.runPendingTask();
+
+ EXPECT_EQ(16, thread.pendingDelayMs());
+
+ client.reset();
+ thread.runPendingTaskOnOverwrite(true);
+ timer->setTimebaseAndInterval(interval, interval * 2);
+ thread.runPendingTaskOnOverwrite(false);
+
+ EXPECT_FALSE(client.tickCalled()); // Make sure pending tasks were canceled.
+ EXPECT_EQ(33, thread.pendingDelayMs());
+
+ // Tick, then halve the interval.
+ timer->setMonotonicTimeNow(interval * 3);
+ thread.runPendingTask();
+
+ EXPECT_EQ(33, thread.pendingDelayMs());
+
+ client.reset();
+ thread.runPendingTaskOnOverwrite(true);
+ timer->setTimebaseAndInterval(interval * 3, interval);
+ thread.runPendingTaskOnOverwrite(false);
+
+ EXPECT_FALSE(client.tickCalled()); // Make sure pending tasks were canceled.
+ EXPECT_EQ(16, thread.pendingDelayMs());
+}
+
+TEST(CCDelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise)
+{
+ int numIterations = 1000;
+
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+ timer->setActive(true);
+
+ double totalFrameTime = 0;
+ for (int i = 0; i < numIterations; ++i) {
+ long long delayMs = thread.pendingDelayMs();
+
+ // accumulate the "delay"
+ totalFrameTime += delayMs / 1000.0;
+
+ // Run the callback exactly when asked
+ double now = timer->monotonicTimeNow() + delayMs / 1000.0;
+ timer->setMonotonicTimeNow(now);
+ thread.runPendingTask();
+ }
+ double averageInterval = totalFrameTime / static_cast<double>(numIterations);
+ EXPECT_NEAR(1.0 / 60.0, averageInterval, 0.1);
+}
+
+TEST(CCDelayBasedTimeSource, TestDeactivateWhilePending)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+ timer->setActive(true); // Should post a task.
+ timer->setActive(false);
+ timer.clear();
+ thread.runPendingTask(); // Should run the posted task without crashing.
+}
+
+TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+
+ // Should run the activate task, and pick up a new timebase.
+ timer->setActive(true);
+ timer->setMonotonicTimeNow(0);
+ thread.runPendingTask();
+
+ // Stop the timer
+ timer->setActive(false);
+
+ // Task will be pending anyway, run it
+ thread.runPendingTask();
+
+ // Start the timer again, but before the next tick time the timer previously
+ // planned on using. That same tick time should still be targeted.
+ timer->setMonotonicTimeNow(0.004);
+ timer->setActive(true);
+ EXPECT_EQ(12, thread.pendingDelayMs());
+}
+
+TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime)
+{
+ FakeCCThread thread;
+ FakeCCTimeSourceClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ timer->setClient(&client);
+
+ // Should run the activate task, and pick up a new timebase.
+ timer->setActive(true);
+ timer->setMonotonicTimeNow(0);
+ thread.runPendingTask();
+
+ // Stop the timer
+ timer->setActive(false);
+
+ // Task will be pending anyway, run it
+ thread.runPendingTask();
+
+ // Start the timer again, but before the next tick time the timer previously
+ // planned on using. That same tick time should still be targeted.
+ timer->setMonotonicTimeNow(0.02);
+ timer->setActive(true);
+ EXPECT_EQ(13, thread.pendingDelayMs());
+}
+
+}
diff --git a/cc/CCDirectRenderer.cpp b/cc/CCDirectRenderer.cpp
new file mode 100644
index 0000000..f4b9731
--- /dev/null
+++ b/cc/CCDirectRenderer.cpp
@@ -0,0 +1,212 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCDirectRenderer.h"
+
+#include "CCMathUtil.h"
+#include <public/WebTransformationMatrix.h>
+
+using WebKit::WebTransformationMatrix;
+
+static WebTransformationMatrix orthoProjectionMatrix(float left, float right, float bottom, float top)
+{
+ // Use the standard formula to map the clipping frustum to the cube from
+ // [-1, -1, -1] to [1, 1, 1].
+ float deltaX = right - left;
+ float deltaY = top - bottom;
+ WebTransformationMatrix proj;
+ if (!deltaX || !deltaY)
+ return proj;
+ proj.setM11(2.0f / deltaX);
+ proj.setM41(-(right + left) / deltaX);
+ proj.setM22(2.0f / deltaY);
+ proj.setM42(-(top + bottom) / deltaY);
+
+ // Z component of vertices is always set to zero as we don't use the depth buffer
+ // while drawing.
+ proj.setM33(0);
+
+ return proj;
+}
+
+static WebTransformationMatrix windowMatrix(int x, int y, int width, int height)
+{
+ WebTransformationMatrix canvas;
+
+ // Map to window position and scale up to pixel coordinates.
+ canvas.translate3d(x, y, 0);
+ canvas.scale3d(width, height, 0);
+
+ // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1])
+ canvas.translate3d(0.5, 0.5, 0.5);
+ canvas.scale3d(0.5, 0.5, 0.5);
+
+ return canvas;
+}
+
+namespace WebCore {
+//
+// static
+FloatRect CCDirectRenderer::quadVertexRect()
+{
+ return FloatRect(-0.5, -0.5, 1, 1);
+}
+
+// static
+void CCDirectRenderer::quadRectTransform(WebKit::WebTransformationMatrix* quadRectTransform, const WebKit::WebTransformationMatrix& quadTransform, const FloatRect& quadRect)
+{
+ *quadRectTransform = quadTransform;
+ quadRectTransform->translate(0.5 * quadRect.width() + quadRect.x(), 0.5 * quadRect.height() + quadRect.y());
+ quadRectTransform->scaleNonUniform(quadRect.width(), quadRect.height());
+}
+
+// static
+void CCDirectRenderer::initializeMatrices(DrawingFrame& frame, const IntRect& drawRect, bool flipY)
+{
+ if (flipY)
+ frame.projectionMatrix = orthoProjectionMatrix(drawRect.x(), drawRect.maxX(), drawRect.maxY(), drawRect.y());
+ else
+ frame.projectionMatrix = orthoProjectionMatrix(drawRect.x(), drawRect.maxX(), drawRect.y(), drawRect.maxY());
+ frame.windowMatrix = windowMatrix(0, 0, drawRect.width(), drawRect.height());
+ frame.flippedY = flipY;
+}
+
+// static
+IntRect CCDirectRenderer::moveScissorToWindowSpace(const DrawingFrame& frame, FloatRect scissorRect)
+{
+ IntRect scissorRectInCanvasSpace = enclosingIntRect(scissorRect);
+ // The scissor coordinates must be supplied in viewport space so we need to offset
+ // by the relative position of the top left corner of the current render pass.
+ IntRect framebufferOutputRect = frame.currentRenderPass->outputRect();
+ scissorRectInCanvasSpace.setX(scissorRectInCanvasSpace.x() - framebufferOutputRect.x());
+ if (frame.flippedY && !frame.currentTexture)
+ scissorRectInCanvasSpace.setY(framebufferOutputRect.height() - (scissorRectInCanvasSpace.maxY() - framebufferOutputRect.y()));
+ else
+ scissorRectInCanvasSpace.setY(scissorRectInCanvasSpace.y() - framebufferOutputRect.y());
+ return scissorRectInCanvasSpace;
+}
+
+void CCDirectRenderer::decideRenderPassAllocationsForFrame(const CCRenderPassList& renderPassesInDrawOrder)
+{
+ HashMap<int, const CCRenderPass*> renderPassesInFrame;
+ for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i)
+ renderPassesInFrame.set(renderPassesInDrawOrder[i]->id(), renderPassesInDrawOrder[i]);
+
+ Vector<int> passesToDelete;
+ HashMap<int, OwnPtr<CachedTexture> >::const_iterator passIterator;
+ for (passIterator = m_renderPassTextures.begin(); passIterator != m_renderPassTextures.end(); ++passIterator) {
+ const CCRenderPass* renderPassInFrame = renderPassesInFrame.get(passIterator->first);
+ if (!renderPassInFrame) {
+ passesToDelete.append(passIterator->first);
+ continue;
+ }
+
+ const IntSize& requiredSize = renderPassTextureSize(renderPassInFrame);
+ GC3Denum requiredFormat = renderPassTextureFormat(renderPassInFrame);
+ CachedTexture* texture = passIterator->second.get();
+ ASSERT(texture);
+
+ if (texture->id() && (texture->size() != requiredSize || texture->format() != requiredFormat))
+ texture->free();
+ }
+
+ // Delete RenderPass textures from the previous frame that will not be used again.
+ for (size_t i = 0; i < passesToDelete.size(); ++i)
+ m_renderPassTextures.remove(passesToDelete[i]);
+
+ for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i) {
+ if (!m_renderPassTextures.contains(renderPassesInDrawOrder[i]->id())) {
+ OwnPtr<CachedTexture> texture = CachedTexture::create(m_resourceProvider);
+ m_renderPassTextures.set(renderPassesInDrawOrder[i]->id(), texture.release());
+ }
+ }
+}
+
+void CCDirectRenderer::drawFrame(const CCRenderPassList& renderPassesInDrawOrder, const CCRenderPassIdHashMap& renderPassesById)
+{
+ const CCRenderPass* rootRenderPass = renderPassesInDrawOrder.last();
+ ASSERT(rootRenderPass);
+
+ DrawingFrame frame;
+ frame.renderPassesById = &renderPassesById;
+ frame.rootRenderPass = rootRenderPass;
+ frame.rootDamageRect = capabilities().usingPartialSwap ? rootRenderPass->damageRect() : rootRenderPass->outputRect();
+ frame.rootDamageRect.intersect(IntRect(IntPoint::zero(), viewportSize()));
+
+ beginDrawingFrame(frame);
+ for (size_t i = 0; i < renderPassesInDrawOrder.size(); ++i)
+ drawRenderPass(frame, renderPassesInDrawOrder[i]);
+ finishDrawingFrame(frame);
+}
+
+void CCDirectRenderer::drawRenderPass(DrawingFrame& frame, const CCRenderPass* renderPass)
+{
+ if (!useRenderPass(frame, renderPass))
+ return;
+
+ frame.scissorRectInRenderPassSpace = frame.currentRenderPass->outputRect();
+ if (frame.rootDamageRect != frame.rootRenderPass->outputRect()) {
+ WebTransformationMatrix inverseTransformToRoot = frame.currentRenderPass->transformToRootTarget().inverse();
+ frame.scissorRectInRenderPassSpace.intersect(CCMathUtil::projectClippedRect(inverseTransformToRoot, frame.rootDamageRect));
+ }
+
+ enableScissorTestRect(moveScissorToWindowSpace(frame, frame.scissorRectInRenderPassSpace));
+ clearFramebuffer(frame);
+
+ const CCQuadList& quadList = renderPass->quadList();
+ for (CCQuadList::constBackToFrontIterator it = quadList.backToFrontBegin(); it != quadList.backToFrontEnd(); ++it) {
+ FloatRect quadScissorRect = frame.scissorRectInRenderPassSpace;
+ quadScissorRect.intersect(it->get()->clippedRectInTarget());
+ if (!quadScissorRect.isEmpty()) {
+ enableScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect));
+ drawQuad(frame, it->get());
+ }
+ }
+
+ CachedTexture* texture = m_renderPassTextures.get(renderPass->id());
+ if (texture)
+ texture->setIsComplete(!renderPass->hasOcclusionFromOutsideTargetSurface());
+}
+
+bool CCDirectRenderer::useRenderPass(DrawingFrame& frame, const CCRenderPass* renderPass)
+{
+ frame.currentRenderPass = renderPass;
+ frame.currentTexture = 0;
+
+ if (renderPass == frame.rootRenderPass) {
+ bindFramebufferToOutputSurface(frame);
+ initializeMatrices(frame, renderPass->outputRect(), true);
+ setDrawViewportSize(renderPass->outputRect().size());
+ return true;
+ }
+
+ CachedTexture* texture = m_renderPassTextures.get(renderPass->id());
+ ASSERT(texture);
+ if (!texture->id() && !texture->allocate(CCRenderer::ImplPool, renderPassTextureSize(renderPass), renderPassTextureFormat(renderPass), CCResourceProvider::TextureUsageFramebuffer))
+ return false;
+
+ return bindFramebufferToTexture(frame, texture, renderPass->outputRect());
+}
+
+bool CCDirectRenderer::haveCachedResourcesForRenderPassId(int id) const
+{
+ CachedTexture* texture = m_renderPassTextures.get(id);
+ return texture && texture->id() && texture->isComplete();
+}
+
+// static
+IntSize CCDirectRenderer::renderPassTextureSize(const CCRenderPass* pass)
+{
+ return pass->outputRect().size();
+}
+
+// static
+GC3Denum CCDirectRenderer::renderPassTextureFormat(const CCRenderPass*)
+{
+ return GraphicsContext3D::RGBA;
+}
+
+}
diff --git a/cc/CCDirectRenderer.h b/cc/CCDirectRenderer.h
new file mode 100644
index 0000000..097d5fa
--- /dev/null
+++ b/cc/CCDirectRenderer.h
@@ -0,0 +1,106 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDirectRenderer_h
+#define CCDirectRenderer_h
+
+#include "CCRenderer.h"
+#include "CCResourceProvider.h"
+#include "CCScopedTexture.h"
+
+namespace WebCore {
+
+class CCResourceProvider;
+
+// This is the base class for code shared between the GL and software
+// renderer implementations. "Direct" refers to the fact that it does not
+// delegate rendering to another compositor.
+class CCDirectRenderer : public CCRenderer {
+ WTF_MAKE_NONCOPYABLE(CCDirectRenderer);
+public:
+ virtual ~CCDirectRenderer() { }
+
+ CCResourceProvider* resourceProvider() const { return m_resourceProvider; }
+
+ virtual void decideRenderPassAllocationsForFrame(const CCRenderPassList& renderPassesInDrawOrder) OVERRIDE;
+ virtual bool haveCachedResourcesForRenderPassId(int id) const OVERRIDE;
+ virtual void drawFrame(const CCRenderPassList& renderPassesInDrawOrder, const CCRenderPassIdHashMap& renderPassesById) OVERRIDE;
+
+protected:
+ CCDirectRenderer(CCRendererClient* client, CCResourceProvider* resourceProvider)
+ : CCRenderer(client)
+ , m_resourceProvider(resourceProvider)
+ {
+ }
+
+ struct DrawingFrame {
+ const CCRenderPassIdHashMap* renderPassesById;
+ const CCRenderPass* rootRenderPass;
+ const CCRenderPass* currentRenderPass;
+ const CCScopedTexture* currentTexture;
+
+ FloatRect rootDamageRect;
+
+ WebKit::WebTransformationMatrix projectionMatrix;
+ WebKit::WebTransformationMatrix windowMatrix;
+ bool flippedY;
+ FloatRect scissorRectInRenderPassSpace;
+
+ DrawingFrame()
+ : rootRenderPass(0)
+ , currentRenderPass(0)
+ , currentTexture(0)
+ , flippedY(false)
+ { }
+ };
+
+ class CachedTexture : public CCScopedTexture {
+ WTF_MAKE_NONCOPYABLE(CachedTexture);
+ public:
+ static PassOwnPtr<CachedTexture> create(CCResourceProvider* resourceProvider) { return adoptPtr(new CachedTexture(resourceProvider)); }
+ virtual ~CachedTexture() { }
+
+ bool isComplete() const { return m_isComplete; }
+ void setIsComplete(bool isComplete) { m_isComplete = isComplete; }
+
+ protected:
+ explicit CachedTexture(CCResourceProvider* resourceProvider)
+ : CCScopedTexture(resourceProvider)
+ , m_isComplete(false)
+ {
+ }
+
+ private:
+ bool m_isComplete;
+ };
+
+ static FloatRect quadVertexRect();
+ static void quadRectTransform(WebKit::WebTransformationMatrix* quadRectTransform, const WebKit::WebTransformationMatrix& quadTransform, const FloatRect& quadRect);
+ static void initializeMatrices(DrawingFrame&, const IntRect& drawRect, bool flipY);
+ static IntRect moveScissorToWindowSpace(const DrawingFrame&, FloatRect scissorRect);
+
+ bool haveCachedResources(int renderPassId) const;
+ static IntSize renderPassTextureSize(const CCRenderPass*);
+ static GC3Denum renderPassTextureFormat(const CCRenderPass*);
+
+ void drawRenderPass(DrawingFrame&, const CCRenderPass*);
+ bool useRenderPass(DrawingFrame&, const CCRenderPass*);
+
+ virtual void bindFramebufferToOutputSurface(DrawingFrame&) = 0;
+ virtual bool bindFramebufferToTexture(DrawingFrame&, const CCScopedTexture*, const IntRect& framebufferRect) = 0;
+ virtual void setDrawViewportSize(const IntSize&) = 0;
+ virtual void enableScissorTestRect(const IntRect& scissorRect) = 0;
+ virtual void disableScissorTest() = 0;
+ virtual void clearFramebuffer(DrawingFrame&) = 0;
+ virtual void drawQuad(DrawingFrame&, const CCDrawQuad*) = 0;
+ virtual void beginDrawingFrame(DrawingFrame&) = 0;
+ virtual void finishDrawingFrame(DrawingFrame&) = 0;
+
+ HashMap<int, OwnPtr<CachedTexture> > m_renderPassTextures;
+ CCResourceProvider* m_resourceProvider;
+};
+
+}
+
+#endif // CCDirectRenderer_h
diff --git a/cc/CCDrawQuad.cpp b/cc/CCDrawQuad.cpp
new file mode 100644
index 0000000..ad14b84
--- /dev/null
+++ b/cc/CCDrawQuad.cpp
@@ -0,0 +1,85 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "CCDrawQuad.h"
+
+#include "CCCheckerboardDrawQuad.h"
+#include "CCDebugBorderDrawQuad.h"
+#include "CCIOSurfaceDrawQuad.h"
+#include "CCRenderPassDrawQuad.h"
+#include "CCSolidColorDrawQuad.h"
+#include "CCStreamVideoDrawQuad.h"
+#include "CCTextureDrawQuad.h"
+#include "CCTileDrawQuad.h"
+#include "CCYUVVideoDrawQuad.h"
+#include "IntRect.h"
+
+namespace WebCore {
+
+CCDrawQuad::CCDrawQuad(const CCSharedQuadState* sharedQuadState, Material material, const IntRect& quadRect)
+ : m_sharedQuadState(sharedQuadState)
+ , m_sharedQuadStateId(sharedQuadState->id)
+ , m_material(material)
+ , m_quadRect(quadRect)
+ , m_quadVisibleRect(quadRect)
+ , m_quadOpaque(true)
+ , m_needsBlending(false)
+{
+ ASSERT(m_sharedQuadState);
+ ASSERT(m_material != Invalid);
+}
+
+IntRect CCDrawQuad::opaqueRect() const
+{
+ if (opacity() != 1)
+ return IntRect();
+ if (m_sharedQuadState->opaque && m_quadOpaque)
+ return m_quadRect;
+ return m_opaqueRect;
+}
+
+void CCDrawQuad::setQuadVisibleRect(const IntRect& quadVisibleRect)
+{
+ IntRect intersection = quadVisibleRect;
+ intersection.intersect(m_quadRect);
+ m_quadVisibleRect = intersection;
+}
+
+unsigned CCDrawQuad::size() const
+{
+ switch (material()) {
+ case Checkerboard:
+ return sizeof(CCCheckerboardDrawQuad);
+ case DebugBorder:
+ return sizeof(CCDebugBorderDrawQuad);
+ case IOSurfaceContent:
+ return sizeof(CCIOSurfaceDrawQuad);
+ case TextureContent:
+ return sizeof(CCTextureDrawQuad);
+ case SolidColor:
+ return sizeof(CCSolidColorDrawQuad);
+ case TiledContent:
+ return sizeof(CCTileDrawQuad);
+ case StreamVideoContent:
+ return sizeof(CCStreamVideoDrawQuad);
+ case RenderPass:
+ return sizeof(CCRenderPassDrawQuad);
+ case YUVVideoContent:
+ return sizeof(CCYUVVideoDrawQuad);
+ case Invalid:
+ break;
+ }
+
+ CRASH();
+ return sizeof(CCDrawQuad);
+}
+
+void CCDrawQuad::setSharedQuadState(const CCSharedQuadState* sharedQuadState)
+{
+ m_sharedQuadState = sharedQuadState;
+ m_sharedQuadStateId = sharedQuadState->id;
+}
+
+}
diff --git a/cc/CCDrawQuad.h b/cc/CCDrawQuad.h
new file mode 100644
index 0000000..ab603fc
--- /dev/null
+++ b/cc/CCDrawQuad.h
@@ -0,0 +1,94 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCDrawQuad_h
+#define CCDrawQuad_h
+
+#include "CCSharedQuadState.h"
+
+namespace WebCore {
+
+// WARNING! All CCXYZDrawQuad classes must remain PODs (plain old data).
+// They are intended to be "serializable" by copying their raw bytes, so they
+// must not contain any non-bit-copyable member variables!
+//
+// Furthermore, the class members need to be packed so they are aligned
+// properly and don't have paddings/gaps, otherwise memory check tools
+// like Valgrind will complain about uninitialized memory usage when
+// transferring these classes over the wire.
+#pragma pack(push, 4)
+
+// CCDrawQuad is a bag of data used for drawing a quad. Because different
+// materials need different bits of per-quad data to render, classes that derive
+// from CCDrawQuad store additional data in their derived instance. The Material
+// enum is used to "safely" downcast to the derived class.
+class CCDrawQuad {
+public:
+ enum Material {
+ Invalid,
+ Checkerboard,
+ DebugBorder,
+ IOSurfaceContent,
+ RenderPass,
+ TextureContent,
+ SolidColor,
+ TiledContent,
+ YUVVideoContent,
+ StreamVideoContent,
+ };
+
+ IntRect quadRect() const { return m_quadRect; }
+ const WebKit::WebTransformationMatrix& quadTransform() const { return m_sharedQuadState->quadTransform; }
+ IntRect visibleContentRect() const { return m_sharedQuadState->visibleContentRect; }
+ IntRect clippedRectInTarget() const { return m_sharedQuadState->clippedRectInTarget; }
+ float opacity() const { return m_sharedQuadState->opacity; }
+ // For the purposes of blending, what part of the contents of this quad are opaque?
+ IntRect opaqueRect() const;
+ bool needsBlending() const { return m_needsBlending || !opaqueRect().contains(m_quadVisibleRect); }
+
+ // Allows changing the rect that gets drawn to make it smaller. Parameter passed
+ // in will be clipped to quadRect().
+ void setQuadVisibleRect(const IntRect&);
+ IntRect quadVisibleRect() const { return m_quadVisibleRect; }
+ bool isDebugQuad() const { return m_material == DebugBorder; }
+
+ Material material() const { return m_material; }
+
+ // Returns transfer size of this object based on the derived class (by
+ // looking at the material type).
+ unsigned size() const;
+
+ const CCSharedQuadState* sharedQuadState() const { return m_sharedQuadState; }
+ int sharedQuadStateId() const { return m_sharedQuadStateId; }
+ void setSharedQuadState(const CCSharedQuadState*);
+
+protected:
+ CCDrawQuad(const CCSharedQuadState*, Material, const IntRect&);
+
+ // Stores state common to a large bundle of quads; kept separate for memory
+ // efficiency. There is special treatment to reconstruct these pointers
+ // during serialization.
+ const CCSharedQuadState* m_sharedQuadState;
+ int m_sharedQuadStateId;
+
+ Material m_material;
+ IntRect m_quadRect;
+ IntRect m_quadVisibleRect;
+
+ // By default, the shared quad state determines whether or not this quad is
+ // opaque or needs blending. Derived classes can override with these
+ // variables.
+ bool m_quadOpaque;
+ bool m_needsBlending;
+
+ // Be default, this rect is empty. It is used when the shared quad state and above
+ // variables determine that the quad is not fully opaque but may be partially opaque.
+ IntRect m_opaqueRect;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCFontAtlas.cpp b/cc/CCFontAtlas.cpp
new file mode 100644
index 0000000..44695e1
--- /dev/null
+++ b/cc/CCFontAtlas.cpp
@@ -0,0 +1,70 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "CCFontAtlas.h"
+
+#include "CCProxy.h"
+#include "SkCanvas.h"
+
+namespace WebCore {
+
+using namespace std;
+
+CCFontAtlas::CCFontAtlas(SkBitmap bitmap, IntRect asciiToRectTable[128], int fontHeight)
+ : m_atlas(bitmap)
+ , m_fontHeight(fontHeight)
+{
+ for (size_t i = 0; i < 128; ++i)
+ m_asciiToRectTable[i] = asciiToRectTable[i];
+}
+
+CCFontAtlas::~CCFontAtlas()
+{
+}
+
+void CCFontAtlas::drawText(SkCanvas* canvas, const SkPaint& paint, const String& text, const IntPoint& destPosition, const IntSize& clip) const
+{
+ ASSERT(CCProxy::isImplThread());
+
+ Vector<String> lines;
+ text.split('\n', lines);
+
+ IntPoint position = destPosition;
+ for (size_t i = 0; i < lines.size(); ++i) {
+ drawOneLineOfTextInternal(canvas, paint, lines[i], position);
+ position.setY(position.y() + m_fontHeight);
+ if (position.y() > clip.height())
+ return;
+ }
+}
+
+void CCFontAtlas::drawOneLineOfTextInternal(SkCanvas* canvas, const SkPaint& paint, const String& textLine, const IntPoint& destPosition) const
+{
+ ASSERT(CCProxy::isImplThread());
+
+ IntPoint position = destPosition;
+ for (unsigned i = 0; i < textLine.length(); ++i) {
+ // If the ASCII code is out of bounds, then index 0 is used, which is just a plain rectangle glyph.
+ int asciiIndex = (textLine[i] < 128) ? textLine[i] : 0;
+ IntRect glyphBounds = m_asciiToRectTable[asciiIndex];
+ SkIRect source = SkIRect::MakeXYWH(glyphBounds.x(), glyphBounds.y(), glyphBounds.width(), glyphBounds.height());
+ canvas->drawBitmapRect(m_atlas, &source, SkRect::MakeXYWH(position.x(), position.y(), glyphBounds.width(), glyphBounds.height()), &paint);
+ position.setX(position.x() + glyphBounds.width());
+ }
+}
+
+void CCFontAtlas::drawDebugAtlas(SkCanvas* canvas, const IntPoint& destPosition) const
+{
+ ASSERT(CCProxy::isImplThread());
+
+ SkIRect source = SkIRect::MakeWH(m_atlas.width(), m_atlas.height());
+ canvas->drawBitmapRect(m_atlas, &source, SkRect::MakeXYWH(destPosition.x(), destPosition.y(), m_atlas.width(), m_atlas.height()));
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCFontAtlas.h b/cc/CCFontAtlas.h
new file mode 100644
index 0000000..df4818f
--- /dev/null
+++ b/cc/CCFontAtlas.h
@@ -0,0 +1,65 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCFontAtlas_h
+#define CCFontAtlas_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "IntRect.h"
+#include "SkBitmap.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/text/WTFString.h>
+
+class SkCanvas;
+
+namespace WebCore {
+
+class Color;
+class FontDescription;
+class GraphicsContext;
+class IntPoint;
+class IntSize;
+
+// This class provides basic ability to draw text onto the heads-up display.
+class CCFontAtlas {
+ WTF_MAKE_NONCOPYABLE(CCFontAtlas);
+public:
+ static PassOwnPtr<CCFontAtlas> create(SkBitmap bitmap, IntRect asciiToRectTable[128], int fontHeight)
+ {
+ return adoptPtr(new CCFontAtlas(bitmap, asciiToRectTable, fontHeight));
+ }
+ ~CCFontAtlas();
+
+ // Draws multiple lines of text where each line of text is separated by '\n'.
+ // - Correct glyphs will be drawn for ASCII codes in the range 32-127; any characters
+ // outside that range will be displayed as a default rectangle glyph.
+ // - IntSize clip is used to avoid wasting time drawing things that are outside the
+ // target canvas bounds.
+ // - Should only be called only on the impl thread.
+ void drawText(SkCanvas*, const SkPaint&, const String& text, const IntPoint& destPosition, const IntSize& clip) const;
+
+ // Draws the entire atlas at the specified position, just for debugging purposes.
+ void drawDebugAtlas(SkCanvas*, const IntPoint& destPosition) const;
+
+private:
+ CCFontAtlas(SkBitmap, IntRect asciiToRectTable[128], int fontHeight);
+
+ void drawOneLineOfTextInternal(SkCanvas*, const SkPaint&, const String&, const IntPoint& destPosition) const;
+
+ // The actual texture atlas containing all the pre-rendered glyphs.
+ SkBitmap m_atlas;
+
+ // The look-up tables mapping ascii characters to their IntRect locations on the atlas.
+ IntRect m_asciiToRectTable[128];
+
+ int m_fontHeight;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCFrameRateController.cpp b/cc/CCFrameRateController.cpp
new file mode 100644
index 0000000..6f021f1
--- /dev/null
+++ b/cc/CCFrameRateController.cpp
@@ -0,0 +1,142 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCFrameRateController.h"
+
+#include "CCDelayBasedTimeSource.h"
+#include "CCTimeSource.h"
+#include "TraceEvent.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+class CCFrameRateControllerTimeSourceAdapter : public CCTimeSourceClient {
+public:
+ static PassOwnPtr<CCFrameRateControllerTimeSourceAdapter> create(CCFrameRateController* frameRateController)
+ {
+ return adoptPtr(new CCFrameRateControllerTimeSourceAdapter(frameRateController));
+ }
+ virtual ~CCFrameRateControllerTimeSourceAdapter() { }
+
+ virtual void onTimerTick() OVERRIDE { m_frameRateController->onTimerTick(); }
+private:
+ explicit CCFrameRateControllerTimeSourceAdapter(CCFrameRateController* frameRateController)
+ : m_frameRateController(frameRateController) { }
+
+ CCFrameRateController* m_frameRateController;
+};
+
+CCFrameRateController::CCFrameRateController(PassRefPtr<CCTimeSource> timer)
+ : m_client(0)
+ , m_numFramesPending(0)
+ , m_maxFramesPending(0)
+ , m_timeSource(timer)
+ , m_active(false)
+ , m_isTimeSourceThrottling(true)
+{
+ m_timeSourceClientAdapter = CCFrameRateControllerTimeSourceAdapter::create(this);
+ m_timeSource->setClient(m_timeSourceClientAdapter.get());
+}
+
+CCFrameRateController::CCFrameRateController(CCThread* thread)
+ : m_client(0)
+ , m_numFramesPending(0)
+ , m_maxFramesPending(0)
+ , m_active(false)
+ , m_isTimeSourceThrottling(false)
+{
+ m_manualTicker = adoptPtr(new CCTimer(thread, this));
+}
+
+CCFrameRateController::~CCFrameRateController()
+{
+ if (m_isTimeSourceThrottling)
+ m_timeSource->setActive(false);
+}
+
+void CCFrameRateController::setActive(bool active)
+{
+ TRACE_EVENT1("cc", "CCFrameRateController::setActive", "active", active);
+ if (m_active == active)
+ return;
+ m_active = active;
+
+ if (m_isTimeSourceThrottling)
+ m_timeSource->setActive(active);
+ else {
+ if (active)
+ postManualTick();
+ else
+ m_manualTicker->stop();
+ }
+}
+
+void CCFrameRateController::setMaxFramesPending(int maxFramesPending)
+{
+ m_maxFramesPending = maxFramesPending;
+}
+
+void CCFrameRateController::setTimebaseAndInterval(double timebase, double intervalSeconds)
+{
+ if (m_isTimeSourceThrottling)
+ m_timeSource->setTimebaseAndInterval(timebase, intervalSeconds);
+}
+
+void CCFrameRateController::onTimerTick()
+{
+ ASSERT(m_active);
+
+ // Don't forward the tick if we have too many frames in flight.
+ if (m_maxFramesPending && m_numFramesPending >= m_maxFramesPending) {
+ TRACE_EVENT0("cc", "CCFrameRateController::onTimerTickButMaxFramesPending");
+ return;
+ }
+
+ if (m_client)
+ m_client->vsyncTick();
+
+ if (!m_isTimeSourceThrottling
+ && (!m_maxFramesPending || m_numFramesPending < m_maxFramesPending))
+ postManualTick();
+}
+
+void CCFrameRateController::postManualTick()
+{
+ if (m_active)
+ m_manualTicker->startOneShot(0);
+}
+
+void CCFrameRateController::onTimerFired()
+{
+ onTimerTick();
+}
+
+void CCFrameRateController::didBeginFrame()
+{
+ m_numFramesPending++;
+}
+
+void CCFrameRateController::didFinishFrame()
+{
+ m_numFramesPending--;
+ if (!m_isTimeSourceThrottling)
+ postManualTick();
+}
+
+void CCFrameRateController::didAbortAllPendingFrames()
+{
+ m_numFramesPending = 0;
+}
+
+double CCFrameRateController::nextTickTimeIfActivated()
+{
+ if (m_isTimeSourceThrottling)
+ return m_timeSource->nextTickTimeIfActivated();
+
+ return monotonicallyIncreasingTime();
+}
+
+}
diff --git a/cc/CCFrameRateController.h b/cc/CCFrameRateController.h
new file mode 100644
index 0000000..0aa3fdf
--- /dev/null
+++ b/cc/CCFrameRateController.h
@@ -0,0 +1,76 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCFrameRateController_h
+#define CCFrameRateController_h
+
+#include "CCTimer.h"
+
+#include <wtf/Deque.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCThread;
+class CCTimeSource;
+
+class CCFrameRateControllerClient {
+public:
+ virtual void vsyncTick() = 0;
+
+protected:
+ virtual ~CCFrameRateControllerClient() { }
+};
+
+class CCFrameRateControllerTimeSourceAdapter;
+
+class CCFrameRateController : public CCTimerClient {
+public:
+ explicit CCFrameRateController(PassRefPtr<CCTimeSource>);
+ // Alternate form of CCFrameRateController with unthrottled frame-rate.
+ explicit CCFrameRateController(CCThread*);
+ virtual ~CCFrameRateController();
+
+ void setClient(CCFrameRateControllerClient* client) { m_client = client; }
+
+ void setActive(bool);
+
+ // Use the following methods to adjust target frame rate.
+ //
+ // Multiple frames can be in-progress, but for every didBeginFrame, a
+ // didFinishFrame should be posted.
+ //
+ // If the rendering pipeline crashes, call didAbortAllPendingFrames.
+ void didBeginFrame();
+ void didFinishFrame();
+ void didAbortAllPendingFrames();
+ void setMaxFramesPending(int); // 0 for unlimited.
+ double nextTickTimeIfActivated();
+
+ void setTimebaseAndInterval(double timebase, double intervalSeconds);
+
+protected:
+ friend class CCFrameRateControllerTimeSourceAdapter;
+ void onTimerTick();
+
+ void postManualTick();
+
+ // CCTimerClient implementation (used for unthrottled frame-rate).
+ virtual void onTimerFired() OVERRIDE;
+
+ CCFrameRateControllerClient* m_client;
+ int m_numFramesPending;
+ int m_maxFramesPending;
+ RefPtr<CCTimeSource> m_timeSource;
+ OwnPtr<CCFrameRateControllerTimeSourceAdapter> m_timeSourceClientAdapter;
+ bool m_active;
+
+ // Members for unthrottled frame-rate.
+ bool m_isTimeSourceThrottling;
+ OwnPtr<CCTimer> m_manualTicker;
+};
+
+}
+#endif // CCFrameRateController_h
diff --git a/cc/CCFrameRateControllerTest.cpp b/cc/CCFrameRateControllerTest.cpp
new file mode 100644
index 0000000..721db12
--- /dev/null
+++ b/cc/CCFrameRateControllerTest.cpp
@@ -0,0 +1,169 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCFrameRateController.h"
+
+#include "CCSchedulerTestCommon.h"
+#include <gtest/gtest.h>
+
+using namespace WTF;
+using namespace WebCore;
+using namespace WebKitTests;
+
+namespace {
+
+class FakeCCFrameRateControllerClient : public WebCore::CCFrameRateControllerClient {
+public:
+ FakeCCFrameRateControllerClient() { reset(); }
+
+ void reset() { m_vsyncTicked = false; }
+ bool vsyncTicked() const { return m_vsyncTicked; }
+
+ virtual void vsyncTick() { m_vsyncTicked = true; }
+
+protected:
+ bool m_vsyncTicked;
+};
+
+
+TEST(CCFrameRateControllerTest, TestFrameThrottling_ImmediateAck)
+{
+ FakeCCThread thread;
+ FakeCCFrameRateControllerClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ CCFrameRateController controller(timeSource);
+
+ controller.setClient(&client);
+ controller.setActive(true);
+
+ double elapsed = 0; // Muck around with time a bit
+
+ // Trigger one frame, make sure the vsync callback is called
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // Tell the controller we drew
+ controller.didBeginFrame();
+
+ // Tell the controller the frame ended 5ms later
+ timeSource->setMonotonicTimeNow(timeSource->monotonicTimeNow() + 0.005);
+ controller.didFinishFrame();
+
+ // Trigger another frame, make sure vsync runs again
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ EXPECT_TRUE(elapsed >= timeSource->monotonicTimeNow()); // Sanity check that previous code didn't move time backward.
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+}
+
+TEST(CCFrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight)
+{
+ FakeCCThread thread;
+ FakeCCFrameRateControllerClient client;
+ RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread);
+ CCFrameRateController controller(timeSource);
+
+ controller.setClient(&client);
+ controller.setActive(true);
+ controller.setMaxFramesPending(2);
+
+ double elapsed = 0; // Muck around with time a bit
+
+ // Trigger one frame, make sure the vsync callback is called
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // Tell the controller we drew
+ controller.didBeginFrame();
+
+ // Trigger another frame, make sure vsync callback runs again
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ EXPECT_TRUE(elapsed >= timeSource->monotonicTimeNow()); // Sanity check that previous code didn't move time backward.
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // Tell the controller we drew, again.
+ controller.didBeginFrame();
+
+ // Trigger another frame. Since two frames are pending, we should not draw.
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ EXPECT_TRUE(elapsed >= timeSource->monotonicTimeNow()); // Sanity check that previous code didn't move time backward.
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_FALSE(client.vsyncTicked());
+
+ // Tell the controller the first frame ended 5ms later
+ timeSource->setMonotonicTimeNow(timeSource->monotonicTimeNow() + 0.005);
+ controller.didFinishFrame();
+
+ // Tick should not have been called
+ EXPECT_FALSE(client.vsyncTicked());
+
+ // Trigger yet another frame. Since one frames is pending, another vsync callback should run.
+ elapsed += thread.pendingDelayMs() / 1000.0;
+ EXPECT_TRUE(elapsed >= timeSource->monotonicTimeNow()); // Sanity check that previous code didn't move time backward.
+ timeSource->setMonotonicTimeNow(elapsed);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+}
+
+TEST(CCFrameRateControllerTest, TestFrameThrottling_Unthrottled)
+{
+ FakeCCThread thread;
+ FakeCCFrameRateControllerClient client;
+ CCFrameRateController controller(&thread);
+
+ controller.setClient(&client);
+ controller.setMaxFramesPending(2);
+
+ // setActive triggers 1st frame, make sure the vsync callback is called
+ controller.setActive(true);
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // Even if we don't call didBeginFrame, CCFrameRateController should
+ // still attempt to vsync tick multiple times until it does result in
+ // a didBeginFrame.
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // didBeginFrame triggers 2nd frame, make sure the vsync callback is called
+ controller.didBeginFrame();
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+ client.reset();
+
+ // didBeginFrame triggers 3rd frame (> maxFramesPending), make sure the vsync callback is NOT called
+ controller.didBeginFrame();
+ thread.runPendingTask();
+ EXPECT_FALSE(client.vsyncTicked());
+ client.reset();
+
+ // Make sure there is no pending task since we can't do anything until we receive a didFinishFrame anyway.
+ EXPECT_FALSE(thread.hasPendingTask());
+
+ // didFinishFrame triggers a frame, make sure the vsync callback is called
+ controller.didFinishFrame();
+ thread.runPendingTask();
+ EXPECT_TRUE(client.vsyncTicked());
+}
+
+}
diff --git a/cc/CCFrameRateCounter.cpp b/cc/CCFrameRateCounter.cpp
new file mode 100644
index 0000000..bd5b140
--- /dev/null
+++ b/cc/CCFrameRateCounter.cpp
@@ -0,0 +1,130 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "CCFrameRateCounter.h"
+
+#include "CCProxy.h"
+#include <public/Platform.h>
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+const double CCFrameRateCounter::kFrameTooFast = 1.0 / 70.0; // measured in seconds
+const double CCFrameRateCounter::kFrameTooSlow = 1.0 / 12.0;
+const double CCFrameRateCounter::kDroppedFrameTime = 1.0 / 50.0;
+
+// safeMod works on -1, returning m-1 in that case.
+static inline int safeMod(int number, int modulus)
+{
+ return (number + modulus) % modulus;
+}
+
+inline double CCFrameRateCounter::frameInterval(int frameNumber) const
+{
+ return m_timeStampHistory[frameIndex(frameNumber)] -
+ m_timeStampHistory[frameIndex(frameNumber - 1)];
+}
+
+inline int CCFrameRateCounter::frameIndex(int frameNumber) const
+{
+ return safeMod(frameNumber, kTimeStampHistorySize);
+}
+
+CCFrameRateCounter::CCFrameRateCounter()
+ : m_currentFrameNumber(1)
+ , m_droppedFrameCount(0)
+{
+ m_timeStampHistory[0] = currentTime();
+ m_timeStampHistory[1] = m_timeStampHistory[0];
+ for (int i = 2; i < kTimeStampHistorySize; i++)
+ m_timeStampHistory[i] = 0;
+}
+
+void CCFrameRateCounter::markBeginningOfFrame(double timestamp)
+{
+ m_timeStampHistory[frameIndex(m_currentFrameNumber)] = timestamp;
+ double frameIntervalSeconds = frameInterval(m_currentFrameNumber);
+
+ if (CCProxy::hasImplThread() && m_currentFrameNumber > 0) {
+ double drawDelayMs = frameIntervalSeconds * 1000.0;
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.CompositorThreadImplDrawDelay", static_cast<int>(drawDelayMs), 1, 120, 60);
+ }
+
+ if (!isBadFrameInterval(frameIntervalSeconds) && frameIntervalSeconds > kDroppedFrameTime)
+ ++m_droppedFrameCount;
+}
+
+void CCFrameRateCounter::markEndOfFrame()
+{
+ m_currentFrameNumber += 1;
+}
+
+bool CCFrameRateCounter::isBadFrameInterval(double intervalBetweenConsecutiveFrames) const
+{
+ bool schedulerAllowsDoubleFrames = !CCProxy::hasImplThread();
+ bool intervalTooFast = schedulerAllowsDoubleFrames && intervalBetweenConsecutiveFrames < kFrameTooFast;
+ bool intervalTooSlow = intervalBetweenConsecutiveFrames > kFrameTooSlow;
+ return intervalTooFast || intervalTooSlow;
+}
+
+bool CCFrameRateCounter::isBadFrame(int frameNumber) const
+{
+ return isBadFrameInterval(frameInterval(frameNumber));
+}
+
+void CCFrameRateCounter::getAverageFPSAndStandardDeviation(double& averageFPS, double& standardDeviation) const
+{
+ int frame = m_currentFrameNumber - 1;
+ averageFPS = 0;
+ int averageFPSCount = 0;
+ double fpsVarianceNumerator = 0;
+
+ // Walk backwards through the samples looking for a run of good frame
+ // timings from which to compute the mean and standard deviation.
+ //
+ // Slow frames occur just because the user is inactive, and should be
+ // ignored. Fast frames are ignored if the scheduler is in single-thread
+ // mode in order to represent the true frame rate in spite of the fact that
+ // the first few swapbuffers happen instantly which skews the statistics
+ // too much for short lived animations.
+ //
+ // isBadFrame encapsulates the frame too slow/frame too fast logic.
+ while (1) {
+ if (!isBadFrame(frame)) {
+ averageFPSCount++;
+ double secForLastFrame = m_timeStampHistory[frameIndex(frame)] -
+ m_timeStampHistory[frameIndex(frame - 1)];
+ double x = 1.0 / secForLastFrame;
+ double deltaFromAverage = x - averageFPS;
+ // Change with caution - numerics. http://en.wikipedia.org/wiki/Standard_deviation
+ averageFPS = averageFPS + deltaFromAverage / averageFPSCount;
+ fpsVarianceNumerator = fpsVarianceNumerator + deltaFromAverage * (x - averageFPS);
+ }
+ if (averageFPSCount && isBadFrame(frame)) {
+ // We've gathered a run of good samples, so stop.
+ break;
+ }
+ --frame;
+ if (frameIndex(frame) == frameIndex(m_currentFrameNumber) || frame < 0) {
+ // We've gone through all available historical data, so stop.
+ break;
+ }
+ }
+
+ standardDeviation = sqrt(fpsVarianceNumerator / averageFPSCount);
+}
+
+double CCFrameRateCounter::timeStampOfRecentFrame(int n)
+{
+ ASSERT(n >= 0 && n < kTimeStampHistorySize);
+ int desiredIndex = (frameIndex(m_currentFrameNumber) + n) % kTimeStampHistorySize;
+ return m_timeStampHistory[desiredIndex];
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCFrameRateCounter.h b/cc/CCFrameRateCounter.h
new file mode 100644
index 0000000..67763bc
--- /dev/null
+++ b/cc/CCFrameRateCounter.h
@@ -0,0 +1,71 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCFrameRateCounter_h
+#define CCFrameRateCounter_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+// This class maintains a history of timestamps, and provides functionality to
+// intelligently compute average frames per second (and standard deviation).
+class CCFrameRateCounter {
+ WTF_MAKE_NONCOPYABLE(CCFrameRateCounter);
+public:
+ static PassOwnPtr<CCFrameRateCounter> create()
+ {
+ return adoptPtr(new CCFrameRateCounter());
+ }
+
+ void markBeginningOfFrame(double timestamp);
+ void markEndOfFrame();
+ int currentFrameNumber() const { return m_currentFrameNumber; }
+ void getAverageFPSAndStandardDeviation(double& averageFPS, double& standardDeviation) const;
+ int timeStampHistorySize() const { return kTimeStampHistorySize; }
+
+ // n = 0 returns the oldest frame retained in the history,
+ // while n = timeStampHistorySize() - 1 returns the timestamp most recent frame.
+ double timeStampOfRecentFrame(int /* n */);
+
+ // This is a heuristic that can be used to ignore frames in a reasonable way. Returns
+ // true if the given frame interval is too fast or too slow, based on constant thresholds.
+ bool isBadFrameInterval(double intervalBetweenConsecutiveFrames) const;
+
+ int droppedFrameCount() const { return m_droppedFrameCount; }
+
+private:
+ CCFrameRateCounter();
+
+ double frameInterval(int frameNumber) const;
+ int frameIndex(int frameNumber) const;
+ bool isBadFrame(int frameNumber) const;
+
+ // Two thresholds (measured in seconds) that describe what is considered to be a "no-op frame" that should not be counted.
+ // - if the frame is too fast, then given our compositor implementation, the frame probably was a no-op and did not draw.
+ // - if the frame is too slow, then there is probably not animating content, so we should not pollute the average.
+ static const double kFrameTooFast;
+ static const double kFrameTooSlow;
+
+ // If a frame takes longer than this threshold (measured in seconds) then we
+ // (naively) assume that it missed a screen refresh; that is, we dropped a frame.
+ // FIXME: Determine this threshold based on monitor refresh rate, crbug.com/138642.
+ static const double kDroppedFrameTime;
+
+ static const int kTimeStampHistorySize = 120;
+
+ int m_currentFrameNumber;
+ double m_timeStampHistory[kTimeStampHistorySize];
+
+ int m_droppedFrameCount;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCGestureCurve.h b/cc/CCGestureCurve.h
new file mode 100644
index 0000000..a240e20
--- /dev/null
+++ b/cc/CCGestureCurve.h
@@ -0,0 +1,31 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCGestureCurve_h
+#define CCGestureCurve_h
+
+namespace WebCore {
+
+class IntPoint;
+
+class CCGestureCurveTarget {
+public:
+ virtual ~CCGestureCurveTarget() { }
+
+ virtual void scrollBy(const IntPoint&) = 0;
+ // FIXME: add interfaces for scroll(), etc.
+};
+
+class CCGestureCurve {
+public:
+ virtual ~CCGestureCurve() { }
+
+ virtual const char* debugName() const = 0;
+
+ virtual bool apply(double monotonicTime, CCGestureCurveTarget*) = 0;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCGraphicsContext.h b/cc/CCGraphicsContext.h
new file mode 100644
index 0000000..592291c
--- /dev/null
+++ b/cc/CCGraphicsContext.h
@@ -0,0 +1,21 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCGraphicsContext_h
+#define CCGraphicsContext_h
+
+#include <public/WebCompositorOutputSurface.h>
+#include <public/WebGraphicsContext3D.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+// FIXME: rename fully to CCOutputSurface.
+typedef WebKit::WebCompositorOutputSurface CCGraphicsContext;
+
+}
+
+#endif // CCGraphicsContext_h
diff --git a/cc/CCHeadsUpDisplayLayerImpl.cpp b/cc/CCHeadsUpDisplayLayerImpl.cpp
new file mode 100644
index 0000000..dc6274d
--- /dev/null
+++ b/cc/CCHeadsUpDisplayLayerImpl.cpp
@@ -0,0 +1,285 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCHeadsUpDisplayLayerImpl.h"
+
+#include "CCDebugRectHistory.h"
+#include "CCFontAtlas.h"
+#include "CCFrameRateCounter.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCQuadSink.h"
+#include "CCTextureDrawQuad.h"
+#include "Extensions3DChromium.h"
+#include "GraphicsContext3D.h"
+#include "SkBitmap.h"
+#include "SkColorMatrixFilter.h"
+#include "SkPaint.h"
+#include "skia/ext/platform_canvas.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+static inline SkPaint createPaint()
+{
+ // The SkCanvas is in RGBA but the shader is expecting BGRA, so we need to
+ // swizzle our colors when drawing to the SkCanvas.
+ SkColorMatrix swizzleMatrix;
+ for (int i = 0; i < 20; ++i)
+ swizzleMatrix.fMat[i] = 0;
+ swizzleMatrix.fMat[0 + 5 * 2] = 1;
+ swizzleMatrix.fMat[1 + 5 * 1] = 1;
+ swizzleMatrix.fMat[2 + 5 * 0] = 1;
+ swizzleMatrix.fMat[3 + 5 * 3] = 1;
+
+ SkPaint paint;
+ paint.setColorFilter(new SkColorMatrixFilter(swizzleMatrix))->unref();
+ return paint;
+}
+
+CCHeadsUpDisplayLayerImpl::CCHeadsUpDisplayLayerImpl(int id)
+ : CCLayerImpl(id)
+{
+}
+
+CCHeadsUpDisplayLayerImpl::~CCHeadsUpDisplayLayerImpl()
+{
+}
+
+void CCHeadsUpDisplayLayerImpl::setFontAtlas(PassOwnPtr<CCFontAtlas> fontAtlas)
+{
+ m_fontAtlas = fontAtlas;
+}
+
+void CCHeadsUpDisplayLayerImpl::willDraw(CCResourceProvider* resourceProvider)
+{
+ CCLayerImpl::willDraw(resourceProvider);
+
+ if (!m_hudTexture)
+ m_hudTexture = CCScopedTexture::create(resourceProvider);
+
+ // FIXME: Scale the HUD by deviceScale to make it more friendly under high DPI.
+
+ if (m_hudTexture->size() != bounds())
+ m_hudTexture->free();
+
+ if (!m_hudTexture->id())
+ m_hudTexture->allocate(CCRenderer::ImplPool, bounds(), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+}
+
+void CCHeadsUpDisplayLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ if (!m_hudTexture->id())
+ return;
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+
+ IntRect quadRect(IntPoint(), bounds());
+ bool premultipliedAlpha = true;
+ FloatRect uvRect(0, 0, 1, 1);
+ bool flipped = false;
+ quadSink.append(CCTextureDrawQuad::create(sharedQuadState, quadRect, m_hudTexture->id(), premultipliedAlpha, uvRect, flipped));
+}
+
+void CCHeadsUpDisplayLayerImpl::updateHudTexture(CCResourceProvider* resourceProvider)
+{
+ if (!m_hudTexture->id())
+ return;
+
+ SkISize canvasSize;
+ if (m_hudCanvas)
+ canvasSize = m_hudCanvas->getDeviceSize();
+ else
+ canvasSize.set(0, 0);
+
+ if (canvasSize.fWidth != bounds().width() || canvasSize.fHeight != bounds().height() || !m_hudCanvas)
+ m_hudCanvas = adoptPtr(skia::CreateBitmapCanvas(bounds().width(), bounds().height(), false /* opaque */));
+
+ m_hudCanvas->clear(SkColorSetARGB(0, 0, 0, 0));
+ drawHudContents(m_hudCanvas.get());
+
+ const SkBitmap* bitmap = &m_hudCanvas->getDevice()->accessBitmap(false);
+ SkAutoLockPixels locker(*bitmap);
+
+ IntRect layerRect(IntPoint(), bounds());
+ ASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config);
+ resourceProvider->upload(m_hudTexture->id(), static_cast<const uint8_t*>(bitmap->getPixels()), layerRect, layerRect, IntSize());
+}
+
+void CCHeadsUpDisplayLayerImpl::didDraw(CCResourceProvider* resourceProvider)
+{
+ CCLayerImpl::didDraw(resourceProvider);
+
+ if (!m_hudTexture->id())
+ return;
+
+ // FIXME: the following assert will not be true when sending resources to a
+ // parent compositor. We will probably need to hold on to m_hudTexture for
+ // longer, and have several HUD textures in the pipeline.
+ ASSERT(!resourceProvider->inUseByConsumer(m_hudTexture->id()));
+}
+
+void CCHeadsUpDisplayLayerImpl::didLoseContext()
+{
+ m_hudTexture.clear();
+}
+
+void CCHeadsUpDisplayLayerImpl::drawHudContents(SkCanvas* canvas)
+{
+ const CCLayerTreeSettings& settings = layerTreeHostImpl()->settings();
+
+ if (settings.showPlatformLayerTree) {
+ SkPaint paint = createPaint();
+ paint.setColor(SkColorSetARGB(192, 0, 0, 0));
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()), paint);
+ }
+
+ int fpsCounterHeight = 40;
+ int fpsCounterTop = 2;
+ int platformLayerTreeTop;
+
+ if (settings.showFPSCounter)
+ platformLayerTreeTop = fpsCounterTop + fpsCounterHeight;
+ else
+ platformLayerTreeTop = 0;
+
+ if (settings.showFPSCounter)
+ drawFPSCounter(canvas, layerTreeHostImpl()->fpsCounter(), fpsCounterTop, fpsCounterHeight);
+
+ if (settings.showPlatformLayerTree && m_fontAtlas) {
+ String layerTree = layerTreeHostImpl()->layerTreeAsText();
+ m_fontAtlas->drawText(canvas, createPaint(), layerTree, IntPoint(2, platformLayerTreeTop), bounds());
+ }
+
+ if (settings.showDebugRects())
+ drawDebugRects(canvas, layerTreeHostImpl()->debugRectHistory());
+}
+
+void CCHeadsUpDisplayLayerImpl::drawFPSCounter(SkCanvas* canvas, CCFrameRateCounter* fpsCounter, int top, int height)
+{
+ float textWidth = 170; // so text fits on linux.
+ float graphWidth = fpsCounter->timeStampHistorySize();
+
+ // Draw the FPS text.
+ drawFPSCounterText(canvas, fpsCounter, top, textWidth, height);
+
+ // Draw FPS graph.
+ const double loFPS = 0;
+ const double hiFPS = 80;
+ SkPaint paint = createPaint();
+ paint.setColor(SkColorSetRGB(154, 205, 50));
+ canvas->drawRect(SkRect::MakeXYWH(2 + textWidth, top, graphWidth, height / 2), paint);
+
+ paint.setColor(SkColorSetRGB(255, 250, 205));
+ canvas->drawRect(SkRect::MakeXYWH(2 + textWidth, top + height / 2, graphWidth, height / 2), paint);
+
+ int graphLeft = static_cast<int>(textWidth + 3);
+ int x = 0;
+ double h = static_cast<double>(height - 2);
+ SkPath path;
+ for (int i = 0; i < fpsCounter->timeStampHistorySize() - 1; ++i) {
+ int j = i + 1;
+ double delta = fpsCounter->timeStampOfRecentFrame(j) - fpsCounter->timeStampOfRecentFrame(i);
+
+ // Skip plotting this particular instantaneous frame rate if it is not likely to have been valid.
+ if (fpsCounter->isBadFrameInterval(delta)) {
+ x += 1;
+ continue;
+ }
+
+ double fps = 1.0 / delta;
+
+ // Clamp the FPS to the range we want to plot visually.
+ double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
+ if (p < 0)
+ p = 0;
+ if (p > 1)
+ p = 1;
+
+ // Plot this data point.
+ SkPoint cur = SkPoint::Make(graphLeft + x, 1 + top + p*h);
+ if (path.isEmpty())
+ path.moveTo(cur);
+ else
+ path.lineTo(cur);
+ x += 1;
+ }
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(1);
+ paint.setAntiAlias(true);
+ canvas->drawPath(path, paint);
+}
+
+void CCHeadsUpDisplayLayerImpl::drawFPSCounterText(SkCanvas* canvas, CCFrameRateCounter* fpsCounter, int top, int width, int height)
+{
+ double averageFPS, stdDeviation;
+ fpsCounter->getAverageFPSAndStandardDeviation(averageFPS, stdDeviation);
+
+ // Draw background.
+ SkPaint paint = createPaint();
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawRect(SkRect::MakeXYWH(2, top, width, height), paint);
+
+ // Draw FPS text.
+ if (m_fontAtlas)
+ m_fontAtlas->drawText(canvas, createPaint(), String::format("FPS: %4.1f +/- %3.1f", averageFPS, stdDeviation), IntPoint(10, height / 3), IntSize(width, height));
+}
+
+void CCHeadsUpDisplayLayerImpl::drawDebugRects(SkCanvas* canvas, CCDebugRectHistory* debugRectHistory)
+{
+ const Vector<CCDebugRect>& debugRects = debugRectHistory->debugRects();
+
+ for (size_t i = 0; i < debugRects.size(); ++i) {
+ SkColor strokeColor = 0;
+ SkColor fillColor = 0;
+
+ switch (debugRects[i].type) {
+ case PaintRectType:
+ // Paint rects in red
+ strokeColor = SkColorSetARGB(255, 255, 0, 0);
+ fillColor = SkColorSetARGB(30, 255, 0, 0);
+ break;
+ case PropertyChangedRectType:
+ // Property-changed rects in blue
+ strokeColor = SkColorSetARGB(255, 255, 0, 0);
+ fillColor = SkColorSetARGB(30, 0, 0, 255);
+ break;
+ case SurfaceDamageRectType:
+ // Surface damage rects in yellow-orange
+ strokeColor = SkColorSetARGB(255, 200, 100, 0);
+ fillColor = SkColorSetARGB(30, 200, 100, 0);
+ break;
+ case ReplicaScreenSpaceRectType:
+ // Screen space rects in green.
+ strokeColor = SkColorSetARGB(255, 100, 200, 0);
+ fillColor = SkColorSetARGB(30, 100, 200, 0);
+ break;
+ case ScreenSpaceRectType:
+ // Screen space rects in purple.
+ strokeColor = SkColorSetARGB(255, 100, 0, 200);
+ fillColor = SkColorSetARGB(10, 100, 0, 200);
+ break;
+ case OccludingRectType:
+ // Occluding rects in a reddish color.
+ strokeColor = SkColorSetARGB(255, 200, 0, 100);
+ fillColor = SkColorSetARGB(10, 200, 0, 100);
+ break;
+ }
+
+ const FloatRect& rect = debugRects[i].rect;
+ SkRect skRect = SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
+ SkPaint paint = createPaint();
+ paint.setColor(fillColor);
+ canvas->drawRect(skRect, paint);
+
+ paint.setColor(strokeColor);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(2);
+ canvas->drawRect(skRect, paint);
+ }
+}
+
+}
diff --git a/cc/CCHeadsUpDisplayLayerImpl.h b/cc/CCHeadsUpDisplayLayerImpl.h
new file mode 100644
index 0000000..6ad825a
--- /dev/null
+++ b/cc/CCHeadsUpDisplayLayerImpl.h
@@ -0,0 +1,56 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCHeadsUpDisplayLayerImpl_h
+#define CCHeadsUpDisplayLayerImpl_h
+
+#include "CCFontAtlas.h"
+#include "CCLayerImpl.h"
+#include "CCScopedTexture.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+class CCDebugRectHistory;
+class CCFontAtlas;
+class CCFrameRateCounter;
+
+class CCHeadsUpDisplayLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCHeadsUpDisplayLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCHeadsUpDisplayLayerImpl(id));
+ }
+ virtual ~CCHeadsUpDisplayLayerImpl();
+
+ void setFontAtlas(PassOwnPtr<CCFontAtlas>);
+
+ virtual void willDraw(CCResourceProvider*) OVERRIDE;
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+ void updateHudTexture(CCResourceProvider*);
+ virtual void didDraw(CCResourceProvider*) OVERRIDE;
+
+ virtual void didLoseContext() OVERRIDE;
+
+ virtual bool layerIsAlwaysDamaged() const OVERRIDE { return true; }
+
+private:
+ explicit CCHeadsUpDisplayLayerImpl(int);
+
+ virtual const char* layerTypeAsString() const OVERRIDE { return "HeadsUpDisplayLayer"; }
+
+ void drawHudContents(SkCanvas*);
+ void drawFPSCounter(SkCanvas*, CCFrameRateCounter*, int top, int height);
+ void drawFPSCounterText(SkCanvas*, CCFrameRateCounter*, int top, int width, int height);
+ void drawDebugRects(SkCanvas*, CCDebugRectHistory*);
+
+ OwnPtr<CCFontAtlas> m_fontAtlas;
+ OwnPtr<CCScopedTexture> m_hudTexture;
+ OwnPtr<SkCanvas> m_hudCanvas;
+};
+
+}
+
+#endif // CCHeadsUpDisplayLayerImpl_h
diff --git a/cc/CCIOSurfaceDrawQuad.cpp b/cc/CCIOSurfaceDrawQuad.cpp
new file mode 100644
index 0000000..3ba6318
--- /dev/null
+++ b/cc/CCIOSurfaceDrawQuad.cpp
@@ -0,0 +1,30 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCIOSurfaceDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCIOSurfaceDrawQuad> CCIOSurfaceDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const IntSize& ioSurfaceSize, unsigned ioSurfaceTextureId, Orientation orientation)
+{
+ return adoptPtr(new CCIOSurfaceDrawQuad(sharedQuadState, quadRect, ioSurfaceSize, ioSurfaceTextureId, orientation));
+}
+
+CCIOSurfaceDrawQuad::CCIOSurfaceDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const IntSize& ioSurfaceSize, unsigned ioSurfaceTextureId, Orientation orientation)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::IOSurfaceContent, quadRect)
+ , m_ioSurfaceSize(ioSurfaceSize)
+ , m_ioSurfaceTextureId(ioSurfaceTextureId)
+ , m_orientation(orientation)
+{
+}
+
+const CCIOSurfaceDrawQuad* CCIOSurfaceDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::IOSurfaceContent);
+ return static_cast<const CCIOSurfaceDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCIOSurfaceDrawQuad.h b/cc/CCIOSurfaceDrawQuad.h
new file mode 100644
index 0000000..44b5e23
--- /dev/null
+++ b/cc/CCIOSurfaceDrawQuad.h
@@ -0,0 +1,42 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCIOSurfaceDrawQuad_h
+#define CCIOSurfaceDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "IntSize.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCIOSurfaceDrawQuad : public CCDrawQuad {
+public:
+ enum Orientation {
+ Flipped,
+ Unflipped
+ };
+
+ static PassOwnPtr<CCIOSurfaceDrawQuad> create(const CCSharedQuadState*, const IntRect&, const IntSize& ioSurfaceSize, unsigned ioSurfaceTextureId, Orientation);
+
+ IntSize ioSurfaceSize() const { return m_ioSurfaceSize; }
+ unsigned ioSurfaceTextureId() const { return m_ioSurfaceTextureId; }
+ Orientation orientation() const { return m_orientation; }
+
+ static const CCIOSurfaceDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCIOSurfaceDrawQuad(const CCSharedQuadState*, const IntRect&, const IntSize& ioSurfaceSize, unsigned ioSurfaceTextureId, Orientation);
+
+ IntSize m_ioSurfaceSize;
+ unsigned m_ioSurfaceTextureId;
+ Orientation m_orientation;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCIOSurfaceLayerImpl.cpp b/cc/CCIOSurfaceLayerImpl.cpp
new file mode 100644
index 0000000..4d070b0
--- /dev/null
+++ b/cc/CCIOSurfaceLayerImpl.cpp
@@ -0,0 +1,112 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCIOSurfaceLayerImpl.h"
+
+#include "CCGraphicsContext.h"
+#include "CCIOSurfaceDrawQuad.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCQuadSink.h"
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "Extensions3D.h"
+#include "TextStream.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+CCIOSurfaceLayerImpl::CCIOSurfaceLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_ioSurfaceId(0)
+ , m_ioSurfaceChanged(false)
+ , m_ioSurfaceTextureId(0)
+{
+}
+
+CCIOSurfaceLayerImpl::~CCIOSurfaceLayerImpl()
+{
+ if (!m_ioSurfaceTextureId)
+ return;
+
+ CCGraphicsContext* context = layerTreeHostImpl()->context();
+ // FIXME: Implement this path for software compositing.
+ WebKit::WebGraphicsContext3D* context3d = context->context3D();
+ if (context3d)
+ context3d->deleteTexture(m_ioSurfaceTextureId);
+}
+
+void CCIOSurfaceLayerImpl::willDraw(CCResourceProvider* resourceProvider)
+{
+ CCLayerImpl::willDraw(resourceProvider);
+
+ if (m_ioSurfaceChanged) {
+ WebKit::WebGraphicsContext3D* context3d = resourceProvider->graphicsContext3D();
+ if (!context3d) {
+ // FIXME: Implement this path for software compositing.
+ return;
+ }
+
+ // FIXME: Do this in a way that we can track memory usage.
+ if (!m_ioSurfaceTextureId)
+ m_ioSurfaceTextureId = context3d->createTexture();
+
+ GLC(context3d, context3d->activeTexture(GraphicsContext3D::TEXTURE0));
+ GLC(context3d, context3d->bindTexture(Extensions3D::TEXTURE_RECTANGLE_ARB, m_ioSurfaceTextureId));
+ GLC(context3d, context3d->texParameteri(Extensions3D::TEXTURE_RECTANGLE_ARB, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context3d, context3d->texParameteri(Extensions3D::TEXTURE_RECTANGLE_ARB, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context3d, context3d->texParameteri(Extensions3D::TEXTURE_RECTANGLE_ARB, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
+ GLC(context3d, context3d->texParameteri(Extensions3D::TEXTURE_RECTANGLE_ARB, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
+ context3d->texImageIOSurface2DCHROMIUM(Extensions3D::TEXTURE_RECTANGLE_ARB,
+ m_ioSurfaceSize.width(),
+ m_ioSurfaceSize.height(),
+ m_ioSurfaceId,
+ 0);
+ // Do not check for error conditions. texImageIOSurface2DCHROMIUM is supposed to hold on to
+ // the last good IOSurface if the new one is already closed. This is only a possibility
+ // during live resizing of plugins. However, it seems that this is not sufficient to
+ // completely guard against garbage being drawn. If this is found to be a significant issue,
+ // it may be necessary to explicitly tell the embedder when to free the surfaces it has
+ // allocated.
+ m_ioSurfaceChanged = false;
+ }
+}
+
+void CCIOSurfaceLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ IntRect quadRect(IntPoint(), contentBounds());
+ quadSink.append(CCIOSurfaceDrawQuad::create(sharedQuadState, quadRect, m_ioSurfaceSize, m_ioSurfaceTextureId, CCIOSurfaceDrawQuad::Flipped));
+}
+
+void CCIOSurfaceLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << "iosurface id: " << m_ioSurfaceId << " texture id: " << m_ioSurfaceTextureId;
+ CCLayerImpl::dumpLayerProperties(ts, indent);
+}
+
+void CCIOSurfaceLayerImpl::didLoseContext()
+{
+ // We don't have a valid texture ID in the new context; however,
+ // the IOSurface is still valid.
+ m_ioSurfaceTextureId = 0;
+ m_ioSurfaceChanged = true;
+}
+
+void CCIOSurfaceLayerImpl::setIOSurfaceProperties(unsigned ioSurfaceId, const IntSize& size)
+{
+ if (m_ioSurfaceId != ioSurfaceId)
+ m_ioSurfaceChanged = true;
+
+ m_ioSurfaceId = ioSurfaceId;
+ m_ioSurfaceSize = size;
+}
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCIOSurfaceLayerImpl.h b/cc/CCIOSurfaceLayerImpl.h
new file mode 100644
index 0000000..9a10cbb
--- /dev/null
+++ b/cc/CCIOSurfaceLayerImpl.h
@@ -0,0 +1,43 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCIOSurfaceLayerImpl_h
+#define CCIOSurfaceLayerImpl_h
+
+#include "CCLayerImpl.h"
+#include "IntSize.h"
+
+namespace WebCore {
+
+class CCIOSurfaceLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCIOSurfaceLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCIOSurfaceLayerImpl(id));
+ }
+ virtual ~CCIOSurfaceLayerImpl();
+
+ void setIOSurfaceProperties(unsigned ioSurfaceId, const IntSize&);
+
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+
+ virtual void willDraw(CCResourceProvider*) OVERRIDE;
+ virtual void didLoseContext() OVERRIDE;
+
+ virtual void dumpLayerProperties(TextStream&, int indent) const OVERRIDE;
+
+private:
+ explicit CCIOSurfaceLayerImpl(int);
+
+ virtual const char* layerTypeAsString() const OVERRIDE { return "IOSurfaceLayer"; }
+
+ unsigned m_ioSurfaceId;
+ IntSize m_ioSurfaceSize;
+ bool m_ioSurfaceChanged;
+ unsigned m_ioSurfaceTextureId;
+};
+
+}
+
+#endif // CCIOSurfaceLayerImpl_h
diff --git a/cc/CCInputHandler.h b/cc/CCInputHandler.h
new file mode 100644
index 0000000..db0cbf6
--- /dev/null
+++ b/cc/CCInputHandler.h
@@ -0,0 +1,85 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCInputHandler_h
+#define CCInputHandler_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCActiveGestureAnimation;
+class CCGestureCurveTarget;
+class IntPoint;
+class IntSize;
+
+// The CCInputHandler is a way for the embedders to interact with
+// the impl thread side of the compositor implementation.
+//
+// There is one CCInputHandler for every CCLayerTreeHost. It is
+// created and used only on the impl thread.
+//
+// The CCInputHandler is constructed with an InputHandlerClient, which is the
+// interface by which the handler can manipulate the LayerTree.
+class CCInputHandlerClient {
+ WTF_MAKE_NONCOPYABLE(CCInputHandlerClient);
+public:
+ enum ScrollStatus { ScrollOnMainThread, ScrollStarted, ScrollIgnored };
+ enum ScrollInputType { Gesture, Wheel };
+
+ // Selects a layer to be scrolled at a given point in window coordinates.
+ // Returns ScrollStarted if the layer at the coordinates can be scrolled,
+ // ScrollOnMainThread if the scroll event should instead be delegated to the
+ // main thread, or ScrollIgnored if there is nothing to be scrolled at the
+ // given coordinates.
+ virtual ScrollStatus scrollBegin(const IntPoint&, ScrollInputType) = 0;
+
+ // Scroll the selected layer starting at the given window coordinate. If
+ // there is no room to move the layer in the requested direction, its first
+ // ancestor layer that can be scrolled will be moved instead. Should only be
+ // called if scrollBegin() returned ScrollStarted.
+ virtual void scrollBy(const IntPoint&, const IntSize&) = 0;
+
+ // Stop scrolling the selected layer. Should only be called if scrollBegin()
+ // returned ScrollStarted.
+ virtual void scrollEnd() = 0;
+
+ virtual void pinchGestureBegin() = 0;
+ virtual void pinchGestureUpdate(float magnifyDelta, const IntPoint& anchor) = 0;
+ virtual void pinchGestureEnd() = 0;
+
+ virtual void startPageScaleAnimation(const IntSize& targetPosition,
+ bool anchorPoint,
+ float pageScale,
+ double startTime,
+ double duration) = 0;
+
+ virtual CCActiveGestureAnimation* activeGestureAnimation() = 0;
+ virtual void setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAnimation>) = 0;
+
+ // Request another callback to CCInputHandler::animate().
+ virtual void scheduleAnimation() = 0;
+
+protected:
+ CCInputHandlerClient() { }
+ virtual ~CCInputHandlerClient() { }
+};
+
+class CCInputHandler {
+ WTF_MAKE_NONCOPYABLE(CCInputHandler);
+public:
+ static PassOwnPtr<CCInputHandler> create(CCInputHandlerClient*);
+ virtual ~CCInputHandler() { }
+
+ virtual int identifier() const = 0;
+ virtual void animate(double monotonicTime) = 0;
+
+protected:
+ CCInputHandler() { }
+};
+
+}
+
+#endif
diff --git a/cc/CCKeyframedAnimationCurve.cpp b/cc/CCKeyframedAnimationCurve.cpp
new file mode 100644
index 0000000..d056b64
--- /dev/null
+++ b/cc/CCKeyframedAnimationCurve.cpp
@@ -0,0 +1,221 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCKeyframedAnimationCurve.h"
+
+#include <wtf/OwnPtr.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+namespace {
+
+template <class Keyframe>
+void insertKeyframe(PassOwnPtr<Keyframe> popKeyframe, Vector<OwnPtr<Keyframe> >& keyframes)
+{
+ OwnPtr<Keyframe> keyframe = popKeyframe;
+
+ // Usually, the keyframes will be added in order, so this loop would be unnecessary and
+ // we should skip it if possible.
+ if (!keyframes.isEmpty() && keyframe->time() < keyframes.last()->time()) {
+ for (size_t i = 0; i < keyframes.size(); ++i) {
+ if (keyframe->time() < keyframes[i]->time()) {
+ keyframes.insert(i, keyframe.release());
+ return;
+ }
+ }
+ }
+
+ keyframes.append(keyframe.release());
+}
+
+PassOwnPtr<CCTimingFunction> cloneTimingFunction(const CCTimingFunction* timingFunction)
+{
+ ASSERT(timingFunction);
+ OwnPtr<CCAnimationCurve> curve(timingFunction->clone());
+ return adoptPtr(static_cast<CCTimingFunction*>(curve.leakPtr()));
+}
+
+} // namespace
+
+CCKeyframe::CCKeyframe(double time, PassOwnPtr<CCTimingFunction> timingFunction)
+ : m_time(time)
+ , m_timingFunction(timingFunction)
+{
+}
+
+CCKeyframe::~CCKeyframe()
+{
+}
+
+double CCKeyframe::time() const
+{
+ return m_time;
+}
+
+const CCTimingFunction* CCKeyframe::timingFunction() const
+{
+ return m_timingFunction.get();
+}
+
+PassOwnPtr<CCFloatKeyframe> CCFloatKeyframe::create(double time, float value, PassOwnPtr<CCTimingFunction> timingFunction)
+{
+ return adoptPtr(new CCFloatKeyframe(time, value, timingFunction));
+}
+
+CCFloatKeyframe::CCFloatKeyframe(double time, float value, PassOwnPtr<CCTimingFunction> timingFunction)
+ : CCKeyframe(time, timingFunction)
+ , m_value(value)
+{
+}
+
+CCFloatKeyframe::~CCFloatKeyframe()
+{
+}
+
+float CCFloatKeyframe::value() const
+{
+ return m_value;
+}
+
+PassOwnPtr<CCFloatKeyframe> CCFloatKeyframe::clone() const
+{
+ return CCFloatKeyframe::create(time(), value(), timingFunction() ? cloneTimingFunction(timingFunction()) : nullptr);
+}
+
+PassOwnPtr<CCTransformKeyframe> CCTransformKeyframe::create(double time, const WebKit::WebTransformOperations& value, PassOwnPtr<CCTimingFunction> timingFunction)
+{
+ return adoptPtr(new CCTransformKeyframe(time, value, timingFunction));
+}
+
+CCTransformKeyframe::CCTransformKeyframe(double time, const WebKit::WebTransformOperations& value, PassOwnPtr<CCTimingFunction> timingFunction)
+ : CCKeyframe(time, timingFunction)
+ , m_value(value)
+{
+}
+
+CCTransformKeyframe::~CCTransformKeyframe()
+{
+}
+
+const WebKit::WebTransformOperations& CCTransformKeyframe::value() const
+{
+ return m_value;
+}
+
+PassOwnPtr<CCTransformKeyframe> CCTransformKeyframe::clone() const
+{
+ return CCTransformKeyframe::create(time(), value(), timingFunction() ? cloneTimingFunction(timingFunction()) : nullptr);
+}
+
+PassOwnPtr<CCKeyframedFloatAnimationCurve> CCKeyframedFloatAnimationCurve::create()
+{
+ return adoptPtr(new CCKeyframedFloatAnimationCurve);
+}
+
+CCKeyframedFloatAnimationCurve::CCKeyframedFloatAnimationCurve()
+{
+}
+
+CCKeyframedFloatAnimationCurve::~CCKeyframedFloatAnimationCurve()
+{
+}
+
+void CCKeyframedFloatAnimationCurve::addKeyframe(PassOwnPtr<CCFloatKeyframe> keyframe)
+{
+ insertKeyframe(keyframe, m_keyframes);
+}
+
+double CCKeyframedFloatAnimationCurve::duration() const
+{
+ return m_keyframes.last()->time() - m_keyframes.first()->time();
+}
+
+PassOwnPtr<CCAnimationCurve> CCKeyframedFloatAnimationCurve::clone() const
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> toReturn(CCKeyframedFloatAnimationCurve::create());
+ for (size_t i = 0; i < m_keyframes.size(); ++i)
+ toReturn->addKeyframe(m_keyframes[i]->clone());
+ return toReturn.release();
+}
+
+float CCKeyframedFloatAnimationCurve::getValue(double t) const
+{
+ if (t <= m_keyframes.first()->time())
+ return m_keyframes.first()->value();
+
+ if (t >= m_keyframes.last()->time())
+ return m_keyframes.last()->value();
+
+ size_t i = 0;
+ for (; i < m_keyframes.size() - 1; ++i) {
+ if (t < m_keyframes[i+1]->time())
+ break;
+ }
+
+ float progress = static_cast<float>((t - m_keyframes[i]->time()) / (m_keyframes[i+1]->time() - m_keyframes[i]->time()));
+
+ if (m_keyframes[i]->timingFunction())
+ progress = m_keyframes[i]->timingFunction()->getValue(progress);
+
+ return m_keyframes[i]->value() + (m_keyframes[i+1]->value() - m_keyframes[i]->value()) * progress;
+}
+
+PassOwnPtr<CCKeyframedTransformAnimationCurve> CCKeyframedTransformAnimationCurve::create()
+{
+ return adoptPtr(new CCKeyframedTransformAnimationCurve);
+}
+
+CCKeyframedTransformAnimationCurve::CCKeyframedTransformAnimationCurve()
+{
+}
+
+CCKeyframedTransformAnimationCurve::~CCKeyframedTransformAnimationCurve()
+{
+}
+
+void CCKeyframedTransformAnimationCurve::addKeyframe(PassOwnPtr<CCTransformKeyframe> keyframe)
+{
+ insertKeyframe(keyframe, m_keyframes);
+}
+
+double CCKeyframedTransformAnimationCurve::duration() const
+{
+ return m_keyframes.last()->time() - m_keyframes.first()->time();
+}
+
+PassOwnPtr<CCAnimationCurve> CCKeyframedTransformAnimationCurve::clone() const
+{
+ OwnPtr<CCKeyframedTransformAnimationCurve> toReturn(CCKeyframedTransformAnimationCurve::create());
+ for (size_t i = 0; i < m_keyframes.size(); ++i)
+ toReturn->addKeyframe(m_keyframes[i]->clone());
+ return toReturn.release();
+}
+
+WebTransformationMatrix CCKeyframedTransformAnimationCurve::getValue(double t) const
+{
+ if (t <= m_keyframes.first()->time())
+ return m_keyframes.first()->value().apply();
+
+ if (t >= m_keyframes.last()->time())
+ return m_keyframes.last()->value().apply();
+
+ size_t i = 0;
+ for (; i < m_keyframes.size() - 1; ++i) {
+ if (t < m_keyframes[i+1]->time())
+ break;
+ }
+
+ double progress = (t - m_keyframes[i]->time()) / (m_keyframes[i+1]->time() - m_keyframes[i]->time());
+
+ if (m_keyframes[i]->timingFunction())
+ progress = m_keyframes[i]->timingFunction()->getValue(progress);
+
+ return m_keyframes[i+1]->value().blend(m_keyframes[i]->value(), progress);
+}
+
+} // namespace WebCore
diff --git a/cc/CCKeyframedAnimationCurve.h b/cc/CCKeyframedAnimationCurve.h
new file mode 100644
index 0000000..01d0c9b
--- /dev/null
+++ b/cc/CCKeyframedAnimationCurve.h
@@ -0,0 +1,111 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCKeyframedAnimationCurve_h
+#define CCKeyframedAnimationCurve_h
+
+#include "CCAnimationCurve.h"
+#include "CCTimingFunction.h"
+#include <public/WebTransformOperations.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCKeyframe {
+public:
+ double time() const;
+ const CCTimingFunction* timingFunction() const;
+
+protected:
+ CCKeyframe(double time, PassOwnPtr<CCTimingFunction>);
+ virtual ~CCKeyframe();
+
+private:
+ double m_time;
+ OwnPtr<CCTimingFunction> m_timingFunction;
+};
+
+class CCFloatKeyframe : public CCKeyframe {
+public:
+ static PassOwnPtr<CCFloatKeyframe> create(double time, float value, PassOwnPtr<CCTimingFunction>);
+ virtual ~CCFloatKeyframe();
+
+ float value() const;
+
+ PassOwnPtr<CCFloatKeyframe> clone() const;
+
+private:
+ CCFloatKeyframe(double time, float value, PassOwnPtr<CCTimingFunction>);
+
+ float m_value;
+};
+
+class CCTransformKeyframe : public CCKeyframe {
+public:
+ static PassOwnPtr<CCTransformKeyframe> create(double time, const WebKit::WebTransformOperations& value, PassOwnPtr<CCTimingFunction>);
+ virtual ~CCTransformKeyframe();
+
+ const WebKit::WebTransformOperations& value() const;
+
+ PassOwnPtr<CCTransformKeyframe> clone() const;
+
+private:
+ CCTransformKeyframe(double time, const WebKit::WebTransformOperations& value, PassOwnPtr<CCTimingFunction>);
+
+ WebKit::WebTransformOperations m_value;
+};
+
+class CCKeyframedFloatAnimationCurve : public CCFloatAnimationCurve {
+public:
+ // It is required that the keyframes be sorted by time.
+ static PassOwnPtr<CCKeyframedFloatAnimationCurve> create();
+
+ virtual ~CCKeyframedFloatAnimationCurve();
+
+ void addKeyframe(PassOwnPtr<CCFloatKeyframe>);
+
+ // CCAnimationCurve implementation
+ virtual double duration() const OVERRIDE;
+ virtual PassOwnPtr<CCAnimationCurve> clone() const OVERRIDE;
+
+ // CCFloatAnimationCurve implementation
+ virtual float getValue(double t) const OVERRIDE;
+
+private:
+ CCKeyframedFloatAnimationCurve();
+
+ // Always sorted in order of increasing time. No two keyframes have the
+ // same time.
+ Vector<OwnPtr<CCFloatKeyframe> > m_keyframes;
+};
+
+class CCKeyframedTransformAnimationCurve : public CCTransformAnimationCurve {
+public:
+ // It is required that the keyframes be sorted by time.
+ static PassOwnPtr<CCKeyframedTransformAnimationCurve> create();
+
+ virtual ~CCKeyframedTransformAnimationCurve();
+
+ void addKeyframe(PassOwnPtr<CCTransformKeyframe>);
+
+ // CCAnimationCurve implementation
+ virtual double duration() const OVERRIDE;
+ virtual PassOwnPtr<CCAnimationCurve> clone() const OVERRIDE;
+
+ // CCTransformAnimationCurve implementation
+ virtual WebKit::WebTransformationMatrix getValue(double t) const OVERRIDE;
+
+private:
+ CCKeyframedTransformAnimationCurve();
+
+ // Always sorted in order of increasing time. No two keyframes have the
+ // same time.
+ Vector<OwnPtr<CCTransformKeyframe> > m_keyframes;
+};
+
+} // namespace WebCore
+
+#endif // CCKeyframedAnimationCurve_h
diff --git a/cc/CCKeyframedAnimationCurveTest.cpp b/cc/CCKeyframedAnimationCurveTest.cpp
new file mode 100644
index 0000000..8f8a746
--- /dev/null
+++ b/cc/CCKeyframedAnimationCurveTest.cpp
@@ -0,0 +1,208 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCKeyframedAnimationCurve.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformOperations.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+void expectTranslateX(double translateX, const WebTransformationMatrix& matrix)
+{
+ EXPECT_FLOAT_EQ(translateX, matrix.m41());
+}
+
+// Tests that a float animation with one keyframe works as expected.
+TEST(CCKeyframedAnimationCurveTest, OneFloatKeyframe)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
+ EXPECT_FLOAT_EQ(2, curve->getValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->getValue(0));
+ EXPECT_FLOAT_EQ(2, curve->getValue(0.5));
+ EXPECT_FLOAT_EQ(2, curve->getValue(1));
+ EXPECT_FLOAT_EQ(2, curve->getValue(2));
+}
+
+// Tests that a float animation with two keyframes works as expected.
+TEST(CCKeyframedAnimationCurveTest, TwoFloatKeyframe)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
+ EXPECT_FLOAT_EQ(2, curve->getValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->getValue(0));
+ EXPECT_FLOAT_EQ(3, curve->getValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->getValue(1));
+ EXPECT_FLOAT_EQ(4, curve->getValue(2));
+}
+
+// Tests that a float animation with three keyframes works as expected.
+TEST(CCKeyframedAnimationCurveTest, ThreeFloatKeyframe)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(2, 8, nullptr));
+ EXPECT_FLOAT_EQ(2, curve->getValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->getValue(0));
+ EXPECT_FLOAT_EQ(3, curve->getValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->getValue(1));
+ EXPECT_FLOAT_EQ(6, curve->getValue(1.5));
+ EXPECT_FLOAT_EQ(8, curve->getValue(2));
+ EXPECT_FLOAT_EQ(8, curve->getValue(3));
+}
+
+// Tests that a float animation with multiple keys at a given time works sanely.
+TEST(CCKeyframedAnimationCurveTest, RepeatedFloatKeyTimes)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(0, 4, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 6, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(2, 6, nullptr));
+
+ EXPECT_FLOAT_EQ(4, curve->getValue(-1));
+ EXPECT_FLOAT_EQ(4, curve->getValue(0));
+ EXPECT_FLOAT_EQ(4, curve->getValue(0.5));
+
+ // There is a discontinuity at 1. Any value between 4 and 6 is valid.
+ float value = curve->getValue(1);
+ EXPECT_TRUE(value >= 4 && value <= 6);
+
+ EXPECT_FLOAT_EQ(6, curve->getValue(1.5));
+ EXPECT_FLOAT_EQ(6, curve->getValue(2));
+ EXPECT_FLOAT_EQ(6, curve->getValue(3));
+}
+
+
+// Tests that a transform animation with one keyframe works as expected.
+TEST(CCKeyframedAnimationCurveTest, OneTransformKeyframe)
+{
+ OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
+ WebKit::WebTransformOperations operations;
+ operations.appendTranslate(2, 0, 0);
+ curve->addKeyframe(CCTransformKeyframe::create(0, operations, nullptr));
+
+ expectTranslateX(2, curve->getValue(-1));
+ expectTranslateX(2, curve->getValue(0));
+ expectTranslateX(2, curve->getValue(0.5));
+ expectTranslateX(2, curve->getValue(1));
+ expectTranslateX(2, curve->getValue(2));
+}
+
+// Tests that a transform animation with two keyframes works as expected.
+TEST(CCKeyframedAnimationCurveTest, TwoTransformKeyframe)
+{
+ OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
+ WebKit::WebTransformOperations operations1;
+ operations1.appendTranslate(2, 0, 0);
+ WebKit::WebTransformOperations operations2;
+ operations2.appendTranslate(4, 0, 0);
+
+ curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
+ expectTranslateX(2, curve->getValue(-1));
+ expectTranslateX(2, curve->getValue(0));
+ expectTranslateX(3, curve->getValue(0.5));
+ expectTranslateX(4, curve->getValue(1));
+ expectTranslateX(4, curve->getValue(2));
+}
+
+// Tests that a transform animation with three keyframes works as expected.
+TEST(CCKeyframedAnimationCurveTest, ThreeTransformKeyframe)
+{
+ OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
+ WebKit::WebTransformOperations operations1;
+ operations1.appendTranslate(2, 0, 0);
+ WebKit::WebTransformOperations operations2;
+ operations2.appendTranslate(4, 0, 0);
+ WebKit::WebTransformOperations operations3;
+ operations3.appendTranslate(8, 0, 0);
+ curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(2, operations3, nullptr));
+ expectTranslateX(2, curve->getValue(-1));
+ expectTranslateX(2, curve->getValue(0));
+ expectTranslateX(3, curve->getValue(0.5));
+ expectTranslateX(4, curve->getValue(1));
+ expectTranslateX(6, curve->getValue(1.5));
+ expectTranslateX(8, curve->getValue(2));
+ expectTranslateX(8, curve->getValue(3));
+}
+
+// Tests that a transform animation with multiple keys at a given time works sanely.
+TEST(CCKeyframedAnimationCurveTest, RepeatedTransformKeyTimes)
+{
+ OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
+ // A step function.
+ WebKit::WebTransformOperations operations1;
+ operations1.appendTranslate(4, 0, 0);
+ WebKit::WebTransformOperations operations2;
+ operations2.appendTranslate(4, 0, 0);
+ WebKit::WebTransformOperations operations3;
+ operations3.appendTranslate(6, 0, 0);
+ WebKit::WebTransformOperations operations4;
+ operations4.appendTranslate(6, 0, 0);
+ curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(1, operations3, nullptr));
+ curve->addKeyframe(CCTransformKeyframe::create(2, operations4, nullptr));
+
+ expectTranslateX(4, curve->getValue(-1));
+ expectTranslateX(4, curve->getValue(0));
+ expectTranslateX(4, curve->getValue(0.5));
+
+ // There is a discontinuity at 1. Any value between 4 and 6 is valid.
+ WebTransformationMatrix value = curve->getValue(1);
+ EXPECT_TRUE(value.m41() >= 4 && value.m41() <= 6);
+
+ expectTranslateX(6, curve->getValue(1.5));
+ expectTranslateX(6, curve->getValue(2));
+ expectTranslateX(6, curve->getValue(3));
+}
+
+// Tests that the keyframes may be added out of order.
+TEST(CCKeyframedAnimationCurveTest, UnsortedKeyframes)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(2, 8, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(0, 2, nullptr));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 4, nullptr));
+ EXPECT_FLOAT_EQ(2, curve->getValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->getValue(0));
+ EXPECT_FLOAT_EQ(3, curve->getValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->getValue(1));
+ EXPECT_FLOAT_EQ(6, curve->getValue(1.5));
+ EXPECT_FLOAT_EQ(8, curve->getValue(2));
+ EXPECT_FLOAT_EQ(8, curve->getValue(3));
+}
+
+// Tests that a cubic bezier timing function works as expected.
+TEST(CCKeyframedAnimationCurveTest, CubicBezierTimingFunction)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+ curve->addKeyframe(CCFloatKeyframe::create(0, 0, CCCubicBezierTimingFunction::create(0.25, 0, 0.75, 1)));
+ curve->addKeyframe(CCFloatKeyframe::create(1, 1, nullptr));
+
+ EXPECT_FLOAT_EQ(0, curve->getValue(0));
+ EXPECT_LT(0, curve->getValue(0.25));
+ EXPECT_GT(0.25, curve->getValue(0.25));
+ EXPECT_FLOAT_EQ(0.5, curve->getValue(0.5));
+ EXPECT_LT(0.75, curve->getValue(0.75));
+ EXPECT_GT(1, curve->getValue(0.75));
+ EXPECT_FLOAT_EQ(1, curve->getValue(1));
+}
+
+} // namespace
diff --git a/cc/CCLayerAnimationController.cpp b/cc/CCLayerAnimationController.cpp
new file mode 100644
index 0000000..bb60758
--- /dev/null
+++ b/cc/CCLayerAnimationController.cpp
@@ -0,0 +1,407 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerAnimationController.h"
+
+#include "CCActiveAnimation.h"
+#include "CCKeyframedAnimationCurve.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/HashMap.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+CCLayerAnimationController::CCLayerAnimationController(CCLayerAnimationControllerClient* client)
+ : m_forceSync(false)
+ , m_client(client)
+{
+}
+
+CCLayerAnimationController::~CCLayerAnimationController()
+{
+}
+
+PassOwnPtr<CCLayerAnimationController> CCLayerAnimationController::create(CCLayerAnimationControllerClient* client)
+{
+ return adoptPtr(new CCLayerAnimationController(client));
+}
+
+void CCLayerAnimationController::pauseAnimation(int animationId, double timeOffset)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->id() == animationId)
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Paused, timeOffset + m_activeAnimations[i]->startTime());
+ }
+}
+
+void CCLayerAnimationController::removeAnimation(int animationId)
+{
+ for (size_t i = 0; i < m_activeAnimations.size();) {
+ if (m_activeAnimations[i]->id() == animationId)
+ m_activeAnimations.remove(i);
+ else
+ i++;
+ }
+}
+
+void CCLayerAnimationController::removeAnimation(int animationId, CCActiveAnimation::TargetProperty targetProperty)
+{
+ for (size_t i = 0; i < m_activeAnimations.size();) {
+ if (m_activeAnimations[i]->id() == animationId && m_activeAnimations[i]->targetProperty() == targetProperty)
+ m_activeAnimations.remove(i);
+ else
+ i++;
+ }
+}
+
+// According to render layer backing, these are for testing only.
+void CCLayerAnimationController::suspendAnimations(double monotonicTime)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (!m_activeAnimations[i]->isFinished())
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Paused, monotonicTime);
+ }
+}
+
+// Looking at GraphicsLayerCA, this appears to be the analog to suspendAnimations, which is for testing.
+void CCLayerAnimationController::resumeAnimations(double monotonicTime)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::Paused)
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, monotonicTime);
+ }
+}
+
+// Ensures that the list of active animations on the main thread and the impl thread
+// are kept in sync.
+void CCLayerAnimationController::pushAnimationUpdatesTo(CCLayerAnimationController* controllerImpl)
+{
+ if (m_forceSync) {
+ replaceImplThreadAnimations(controllerImpl);
+ m_forceSync = false;
+ } else {
+ purgeAnimationsMarkedForDeletion();
+ pushNewAnimationsToImplThread(controllerImpl);
+
+ // Remove finished impl side animations only after pushing,
+ // and only after the animations are deleted on the main thread
+ // this insures we will never push an animation twice.
+ removeAnimationsCompletedOnMainThread(controllerImpl);
+
+ pushPropertiesToImplThread(controllerImpl);
+ }
+}
+
+void CCLayerAnimationController::animate(double monotonicTime, CCAnimationEventsVector* events)
+{
+ startAnimationsWaitingForNextTick(monotonicTime, events);
+ startAnimationsWaitingForStartTime(monotonicTime, events);
+ startAnimationsWaitingForTargetAvailability(monotonicTime, events);
+ resolveConflicts(monotonicTime);
+ tickAnimations(monotonicTime);
+ markAnimationsForDeletion(monotonicTime, events);
+ startAnimationsWaitingForTargetAvailability(monotonicTime, events);
+}
+
+void CCLayerAnimationController::addAnimation(PassOwnPtr<CCActiveAnimation> animation)
+{
+ m_activeAnimations.append(animation);
+}
+
+CCActiveAnimation* CCLayerAnimationController::getActiveAnimation(int groupId, CCActiveAnimation::TargetProperty targetProperty) const
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i)
+ if (m_activeAnimations[i]->group() == groupId && m_activeAnimations[i]->targetProperty() == targetProperty)
+ return m_activeAnimations[i].get();
+ return 0;
+}
+
+CCActiveAnimation* CCLayerAnimationController::getActiveAnimation(CCActiveAnimation::TargetProperty targetProperty) const
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ size_t index = m_activeAnimations.size() - i - 1;
+ if (m_activeAnimations[index]->targetProperty() == targetProperty)
+ return m_activeAnimations[index].get();
+ }
+ return 0;
+}
+
+bool CCLayerAnimationController::hasActiveAnimation() const
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (!m_activeAnimations[i]->isFinished())
+ return true;
+ }
+ return false;
+}
+
+bool CCLayerAnimationController::isAnimatingProperty(CCActiveAnimation::TargetProperty targetProperty) const
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() != CCActiveAnimation::Finished && m_activeAnimations[i]->runState() != CCActiveAnimation::Aborted && m_activeAnimations[i]->targetProperty() == targetProperty)
+ return true;
+ }
+ return false;
+}
+
+void CCLayerAnimationController::notifyAnimationStarted(const CCAnimationEvent& event)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->group() == event.groupId && m_activeAnimations[i]->targetProperty() == event.targetProperty && m_activeAnimations[i]->needsSynchronizedStartTime()) {
+ m_activeAnimations[i]->setNeedsSynchronizedStartTime(false);
+ m_activeAnimations[i]->setStartTime(event.monotonicTime);
+ return;
+ }
+ }
+}
+
+void CCLayerAnimationController::setClient(CCLayerAnimationControllerClient* client)
+{
+ m_client = client;
+}
+
+void CCLayerAnimationController::pushNewAnimationsToImplThread(CCLayerAnimationController* controllerImpl) const
+{
+ // Any new animations owned by the main thread's controller are cloned and adde to the impl thread's controller.
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ // If the animation is already running on the impl thread, there is no need to copy it over.
+ if (controllerImpl->getActiveAnimation(m_activeAnimations[i]->group(), m_activeAnimations[i]->targetProperty()))
+ continue;
+
+ // If the animation is not running on the impl thread, it does not necessarily mean that it needs
+ // to be copied over and started; it may have already finished. In this case, the impl thread animation
+ // will have already notified that it has started and the main thread animation will no longer need
+ // a synchronized start time.
+ if (!m_activeAnimations[i]->needsSynchronizedStartTime())
+ continue;
+
+ // The new animation should be set to run as soon as possible.
+ CCActiveAnimation::RunState initialRunState = CCActiveAnimation::WaitingForTargetAvailability;
+ double startTime = 0;
+ OwnPtr<CCActiveAnimation> toAdd(m_activeAnimations[i]->cloneAndInitialize(CCActiveAnimation::ControllingInstance, initialRunState, startTime));
+ ASSERT(!toAdd->needsSynchronizedStartTime());
+ controllerImpl->addAnimation(toAdd.release());
+ }
+}
+
+void CCLayerAnimationController::removeAnimationsCompletedOnMainThread(CCLayerAnimationController* controllerImpl) const
+{
+ // Delete all impl thread animations for which there is no corresponding main thread animation.
+ // Each iteration, controller->m_activeAnimations.size() is decremented or i is incremented
+ // guaranteeing progress towards loop termination.
+ for (size_t i = 0; i < controllerImpl->m_activeAnimations.size();) {
+ CCActiveAnimation* current = getActiveAnimation(controllerImpl->m_activeAnimations[i]->group(), controllerImpl->m_activeAnimations[i]->targetProperty());
+ if (!current)
+ controllerImpl->m_activeAnimations.remove(i);
+ else
+ i++;
+ }
+}
+
+void CCLayerAnimationController::pushPropertiesToImplThread(CCLayerAnimationController* controllerImpl) const
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ CCActiveAnimation* currentImpl = controllerImpl->getActiveAnimation(m_activeAnimations[i]->group(), m_activeAnimations[i]->targetProperty());
+ if (currentImpl)
+ m_activeAnimations[i]->pushPropertiesTo(currentImpl);
+ }
+}
+
+void CCLayerAnimationController::startAnimationsWaitingForNextTick(double monotonicTime, CCAnimationEventsVector* events)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForNextTick) {
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, monotonicTime);
+ if (!m_activeAnimations[i]->hasSetStartTime())
+ m_activeAnimations[i]->setStartTime(monotonicTime);
+ if (events)
+ events->append(CCAnimationEvent(CCAnimationEvent::Started, m_client->id(), m_activeAnimations[i]->group(), m_activeAnimations[i]->targetProperty(), monotonicTime));
+ }
+ }
+}
+
+void CCLayerAnimationController::startAnimationsWaitingForStartTime(double monotonicTime, CCAnimationEventsVector* events)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForStartTime && m_activeAnimations[i]->startTime() <= monotonicTime) {
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, monotonicTime);
+ if (events)
+ events->append(CCAnimationEvent(CCAnimationEvent::Started, m_client->id(), m_activeAnimations[i]->group(), m_activeAnimations[i]->targetProperty(), monotonicTime));
+ }
+ }
+}
+
+void CCLayerAnimationController::startAnimationsWaitingForTargetAvailability(double monotonicTime, CCAnimationEventsVector* events)
+{
+ // First collect running properties.
+ TargetProperties blockedProperties;
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running || m_activeAnimations[i]->runState() == CCActiveAnimation::Finished)
+ blockedProperties.add(m_activeAnimations[i]->targetProperty());
+ }
+
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForTargetAvailability) {
+ // Collect all properties for animations with the same group id (they should all also be in the list of animations).
+ TargetProperties enqueuedProperties;
+ enqueuedProperties.add(m_activeAnimations[i]->targetProperty());
+ for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+ if (m_activeAnimations[i]->group() == m_activeAnimations[j]->group())
+ enqueuedProperties.add(m_activeAnimations[j]->targetProperty());
+ }
+
+ // Check to see if intersection of the list of properties affected by the group and the list of currently
+ // blocked properties is null. In any case, the group's target properties need to be added to the list
+ // of blocked properties.
+ bool nullIntersection = true;
+ for (TargetProperties::iterator pIter = enqueuedProperties.begin(); pIter != enqueuedProperties.end(); ++pIter) {
+ if (!blockedProperties.add(*pIter).isNewEntry)
+ nullIntersection = false;
+ }
+
+ // If the intersection is null, then we are free to start the animations in the group.
+ if (nullIntersection) {
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, monotonicTime);
+ if (!m_activeAnimations[i]->hasSetStartTime())
+ m_activeAnimations[i]->setStartTime(monotonicTime);
+ if (events)
+ events->append(CCAnimationEvent(CCAnimationEvent::Started, m_client->id(), m_activeAnimations[i]->group(), m_activeAnimations[i]->targetProperty(), monotonicTime));
+ for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+ if (m_activeAnimations[i]->group() == m_activeAnimations[j]->group()) {
+ m_activeAnimations[j]->setRunState(CCActiveAnimation::Running, monotonicTime);
+ if (!m_activeAnimations[j]->hasSetStartTime())
+ m_activeAnimations[j]->setStartTime(monotonicTime);
+ }
+ }
+ }
+ }
+ }
+}
+
+void CCLayerAnimationController::resolveConflicts(double monotonicTime)
+{
+ // Find any animations that are animating the same property and resolve the
+ // confict. We could eventually blend, but for now we'll just abort the
+ // previous animation (where 'previous' means: (1) has a prior start time or
+ // (2) has an equal start time, but was added to the queue earlier, i.e.,
+ // has a lower index in m_activeAnimations).
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running) {
+ for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+ if (m_activeAnimations[j]->runState() == CCActiveAnimation::Running && m_activeAnimations[i]->targetProperty() == m_activeAnimations[j]->targetProperty()) {
+ if (m_activeAnimations[i]->startTime() > m_activeAnimations[j]->startTime())
+ m_activeAnimations[j]->setRunState(CCActiveAnimation::Aborted, monotonicTime);
+ else
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Aborted, monotonicTime);
+ }
+ }
+ }
+ }
+}
+
+void CCLayerAnimationController::markAnimationsForDeletion(double monotonicTime, CCAnimationEventsVector* events)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); i++) {
+ int groupId = m_activeAnimations[i]->group();
+ bool allAnimsWithSameIdAreFinished = false;
+ // If an animation is finished, and not already marked for deletion,
+ // Find out if all other animations in the same group are also finished.
+ if (m_activeAnimations[i]->isFinished()) {
+ allAnimsWithSameIdAreFinished = true;
+ for (size_t j = 0; j < m_activeAnimations.size(); ++j) {
+ if (groupId == m_activeAnimations[j]->group() && !m_activeAnimations[j]->isFinished()) {
+ allAnimsWithSameIdAreFinished = false;
+ break;
+ }
+ }
+ }
+ if (allAnimsWithSameIdAreFinished) {
+ // We now need to remove all animations with the same group id as groupId
+ // (and send along animation finished notifications, if necessary).
+ for (size_t j = i; j < m_activeAnimations.size(); j++) {
+ if (groupId == m_activeAnimations[j]->group()) {
+ if (events)
+ events->append(CCAnimationEvent(CCAnimationEvent::Finished, m_client->id(), m_activeAnimations[j]->group(), m_activeAnimations[j]->targetProperty(), monotonicTime));
+ m_activeAnimations[j]->setRunState(CCActiveAnimation::WaitingForDeletion, monotonicTime);
+ }
+ }
+ }
+ }
+}
+
+void CCLayerAnimationController::purgeAnimationsMarkedForDeletion()
+{
+ for (size_t i = 0; i < m_activeAnimations.size();) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForDeletion)
+ m_activeAnimations.remove(i);
+ else
+ i++;
+ }
+}
+
+void CCLayerAnimationController::replaceImplThreadAnimations(CCLayerAnimationController* controllerImpl) const
+{
+ controllerImpl->m_activeAnimations.clear();
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ OwnPtr<CCActiveAnimation> toAdd;
+ if (m_activeAnimations[i]->needsSynchronizedStartTime()) {
+ // We haven't received an animation started notification yet, so it
+ // is important that we add it in a 'waiting' and not 'running' state.
+ CCActiveAnimation::RunState initialRunState = CCActiveAnimation::WaitingForTargetAvailability;
+ double startTime = 0;
+ toAdd = m_activeAnimations[i]->cloneAndInitialize(CCActiveAnimation::ControllingInstance, initialRunState, startTime);
+ } else
+ toAdd = m_activeAnimations[i]->clone(CCActiveAnimation::ControllingInstance);
+
+ controllerImpl->addAnimation(toAdd.release());
+ }
+}
+
+void CCLayerAnimationController::tickAnimations(double monotonicTime)
+{
+ for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+ if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running || m_activeAnimations[i]->runState() == CCActiveAnimation::Paused) {
+ double trimmed = m_activeAnimations[i]->trimTimeToCurrentIteration(monotonicTime);
+
+ // Animation assumes its initial value until it gets the synchronized start time
+ // from the impl thread and can start ticking.
+ if (m_activeAnimations[i]->needsSynchronizedStartTime())
+ trimmed = 0;
+
+ switch (m_activeAnimations[i]->targetProperty()) {
+
+ case CCActiveAnimation::Transform: {
+ const CCTransformAnimationCurve* transformAnimationCurve = m_activeAnimations[i]->curve()->toTransformAnimationCurve();
+ const WebTransformationMatrix matrix = transformAnimationCurve->getValue(trimmed);
+ if (m_activeAnimations[i]->isFinishedAt(monotonicTime))
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Finished, monotonicTime);
+
+ m_client->setTransformFromAnimation(matrix);
+ break;
+ }
+
+ case CCActiveAnimation::Opacity: {
+ const CCFloatAnimationCurve* floatAnimationCurve = m_activeAnimations[i]->curve()->toFloatAnimationCurve();
+ const float opacity = floatAnimationCurve->getValue(trimmed);
+ if (m_activeAnimations[i]->isFinishedAt(monotonicTime))
+ m_activeAnimations[i]->setRunState(CCActiveAnimation::Finished, monotonicTime);
+
+ m_client->setOpacityFromAnimation(opacity);
+ break;
+ }
+
+ // Do nothing for sentinel value.
+ case CCActiveAnimation::TargetPropertyEnumSize:
+ ASSERT_NOT_REACHED();
+
+ }
+ }
+ }
+}
+
+} // namespace WebCore
diff --git a/cc/CCLayerAnimationController.h b/cc/CCLayerAnimationController.h
new file mode 100644
index 0000000..1176991
--- /dev/null
+++ b/cc/CCLayerAnimationController.h
@@ -0,0 +1,112 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerAnimationController_h
+#define CCLayerAnimationController_h
+
+#include "CCAnimationEvents.h"
+
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebKit {
+class WebTransformationMatrix;
+}
+
+namespace WebCore {
+
+class Animation;
+class IntSize;
+class KeyframeValueList;
+
+class CCLayerAnimationControllerClient {
+public:
+ virtual ~CCLayerAnimationControllerClient() { }
+
+ virtual int id() const = 0;
+ virtual void setOpacityFromAnimation(float) = 0;
+ virtual float opacity() const = 0;
+ virtual void setTransformFromAnimation(const WebKit::WebTransformationMatrix&) = 0;
+ virtual const WebKit::WebTransformationMatrix& transform() const = 0;
+};
+
+class CCLayerAnimationController {
+ WTF_MAKE_NONCOPYABLE(CCLayerAnimationController);
+public:
+ static PassOwnPtr<CCLayerAnimationController> create(CCLayerAnimationControllerClient*);
+
+ virtual ~CCLayerAnimationController();
+
+ // These methods are virtual for testing.
+ virtual void addAnimation(PassOwnPtr<CCActiveAnimation>);
+ virtual void pauseAnimation(int animationId, double timeOffset);
+ virtual void removeAnimation(int animationId);
+ virtual void removeAnimation(int animationId, CCActiveAnimation::TargetProperty);
+ virtual void suspendAnimations(double monotonicTime);
+ virtual void resumeAnimations(double monotonicTime);
+
+ // Ensures that the list of active animations on the main thread and the impl thread
+ // are kept in sync. This function does not take ownership of the impl thread controller.
+ virtual void pushAnimationUpdatesTo(CCLayerAnimationController*);
+
+ void animate(double monotonicTime, CCAnimationEventsVector*);
+
+ // Returns the active animation in the given group, animating the given property, if such an
+ // animation exists.
+ CCActiveAnimation* getActiveAnimation(int groupId, CCActiveAnimation::TargetProperty) const;
+
+ // Returns the active animation animating the given property that is either running, or is
+ // next to run, if such an animation exists.
+ CCActiveAnimation* getActiveAnimation(CCActiveAnimation::TargetProperty) const;
+
+ // Returns true if there are any animations that have neither finished nor aborted.
+ bool hasActiveAnimation() const;
+
+ // Returns true if there is an animation currently animating the given property, or
+ // if there is an animation scheduled to animate this property in the future.
+ bool isAnimatingProperty(CCActiveAnimation::TargetProperty) const;
+
+ // This is called in response to an animation being started on the impl thread. This
+ // function updates the corresponding main thread animation's start time.
+ void notifyAnimationStarted(const CCAnimationEvent&);
+
+ // If a sync is forced, then the next time animation updates are pushed to the impl
+ // thread, all animations will be transferred.
+ void setForceSync() { m_forceSync = true; }
+
+ void setClient(CCLayerAnimationControllerClient*);
+
+protected:
+ explicit CCLayerAnimationController(CCLayerAnimationControllerClient*);
+
+private:
+ typedef HashSet<int, DefaultHash<int>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int> > TargetProperties;
+
+ void pushNewAnimationsToImplThread(CCLayerAnimationController*) const;
+ void removeAnimationsCompletedOnMainThread(CCLayerAnimationController*) const;
+ void pushPropertiesToImplThread(CCLayerAnimationController*) const;
+ void replaceImplThreadAnimations(CCLayerAnimationController*) const;
+
+ void startAnimationsWaitingForNextTick(double monotonicTime, CCAnimationEventsVector*);
+ void startAnimationsWaitingForStartTime(double monotonicTime, CCAnimationEventsVector*);
+ void startAnimationsWaitingForTargetAvailability(double monotonicTime, CCAnimationEventsVector*);
+ void resolveConflicts(double monotonicTime);
+ void markAnimationsForDeletion(double monotonicTime, CCAnimationEventsVector*);
+ void purgeAnimationsMarkedForDeletion();
+
+ void tickAnimations(double monotonicTime);
+
+ // If this is true, we force a sync to the impl thread.
+ bool m_forceSync;
+
+ CCLayerAnimationControllerClient* m_client;
+ Vector<OwnPtr<CCActiveAnimation> > m_activeAnimations;
+};
+
+} // namespace WebCore
+
+#endif // CCLayerAnimationController_h
diff --git a/cc/CCLayerAnimationControllerTest.cpp b/cc/CCLayerAnimationControllerTest.cpp
new file mode 100644
index 0000000..9f03744
--- /dev/null
+++ b/cc/CCLayerAnimationControllerTest.cpp
@@ -0,0 +1,562 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerAnimationController.h"
+
+#include "CCActiveAnimation.h"
+#include "CCAnimationCurve.h"
+#include "CCAnimationTestCommon.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+using namespace WebKitTests;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+void expectTranslateX(double translateX, const WebTransformationMatrix& matrix)
+{
+ EXPECT_FLOAT_EQ(translateX, matrix.m41());
+}
+
+PassOwnPtr<CCActiveAnimation> createActiveAnimation(PassOwnPtr<CCAnimationCurve> curve, int id, CCActiveAnimation::TargetProperty property)
+{
+ return CCActiveAnimation::create(curve, 0, id, property);
+}
+
+TEST(CCLayerAnimationControllerTest, syncNewAnimation)
+{
+ FakeLayerAnimationControllerClient dummyImpl;
+ OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy));
+
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+
+ addOpacityTransitionToController(*controller, 1, 0, 1, false);
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+ EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+}
+
+// If an animation is started on the impl thread before it is ticked on the main
+// thread, we must be sure to respect the synchronized start time.
+TEST(CCLayerAnimationControllerTest, doNotClobberStartTimes)
+{
+ FakeLayerAnimationControllerClient dummyImpl;
+ OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy));
+
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+
+ addOpacityTransitionToController(*controller, 1, 0, 1, false);
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+ EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ CCAnimationEventsVector events;
+ controllerImpl->animate(1, &events);
+
+ // Synchronize the start times.
+ EXPECT_EQ(1u, events.size());
+ controller->notifyAnimationStarted(events[0]);
+ EXPECT_EQ(controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime(), controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime());
+
+ // Start the animation on the main thread. Should not affect the start time.
+ controller->animate(1.5, 0);
+ EXPECT_EQ(controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime(), controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime());
+}
+
+TEST(CCLayerAnimationControllerTest, syncPauseAndResume)
+{
+ FakeLayerAnimationControllerClient dummyImpl;
+ OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy));
+
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+
+ addOpacityTransitionToController(*controller, 1, 0, 1, false);
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+ EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // Start the animations on each controller.
+ CCAnimationEventsVector events;
+ controllerImpl->animate(0, &events);
+ controller->animate(0, 0);
+ EXPECT_EQ(CCActiveAnimation::Running, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+ EXPECT_EQ(CCActiveAnimation::Running, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // Pause the main-thread animation.
+ controller->suspendAnimations(1);
+ EXPECT_EQ(CCActiveAnimation::Paused, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // The pause run state change should make it to the impl thread controller.
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+ EXPECT_EQ(CCActiveAnimation::Paused, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // Resume the main-thread animation.
+ controller->resumeAnimations(2);
+ EXPECT_EQ(CCActiveAnimation::Running, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // The pause run state change should make it to the impl thread controller.
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+ EXPECT_EQ(CCActiveAnimation::Running, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+}
+
+TEST(CCLayerAnimationControllerTest, doNotSyncFinishedAnimation)
+{
+ FakeLayerAnimationControllerClient dummyImpl;
+ OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy));
+
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+
+ addOpacityTransitionToController(*controller, 1, 0, 1, false);
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+ EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState());
+
+ // Notify main thread controller that the animation has started.
+ CCAnimationEvent animationStartedEvent(CCAnimationEvent::Started, 0, 0, CCActiveAnimation::Opacity, 0);
+ controller->notifyAnimationStarted(animationStartedEvent);
+
+ // Force animation to complete on impl thread.
+ controllerImpl->removeAnimation(0);
+
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ // Even though the main thread has a 'new' animation, it should not be pushed because the animation has already completed on the impl thread.
+ EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity));
+}
+
+// Tests that transitioning opacity from 0 to 1 works as expected.
+TEST(CCLayerAnimationControllerTest, TrivialTransition)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+ controller->addAnimation(toAdd.release());
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_EQ(1, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests animations that are waiting for a synchronized start time do not finish.
+TEST(CCLayerAnimationControllerTest, AnimationsWaitingForStartTimeDoNotFinishIfTheyWaitLongerToStartThanTheirDuration)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ toAdd->setNeedsSynchronizedStartTime(true);
+
+ // We should pause at the first keyframe indefinitely waiting for that animation to start.
+ controller->addAnimation(toAdd.release());
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(2, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+
+ // Send the synchronized start time.
+ controller->notifyAnimationStarted(CCAnimationEvent(CCAnimationEvent::Started, 0, 1, CCActiveAnimation::Opacity, 2));
+ controller->animate(5, events.get());
+ EXPECT_EQ(1, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests that two queued animations affecting the same property run in sequence.
+TEST(CCLayerAnimationControllerTest, TrivialQueuing)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity));
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+ controller->animate(2, events.get());
+ EXPECT_EQ(0.5, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests interrupting a transition with another transition.
+TEST(CCLayerAnimationControllerTest, Interrupt)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity));
+ toAdd->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+ controller->addAnimation(toAdd.release());
+
+ // Since the animation was in the WaitingForNextTick state, it should start right in
+ // this call to animate.
+ controller->animate(0.5, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+ controller->animate(1.5, events.get());
+ EXPECT_EQ(0.5, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling two animations to run together when only one property is free.
+TEST(CCLayerAnimationControllerTest, ScheduleTogetherWhenAPropertyIsBlocked)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity));
+
+ controller->animate(0, events.get());
+ EXPECT_EQ(0, dummy.opacity());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ controller->animate(1, events.get());
+ // Should not have started the float transition yet.
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ // The float animation should have started at time 1 and should be done.
+ controller->animate(2, events.get());
+ EXPECT_EQ(1, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling two animations to run together with different lengths and another
+// animation queued to start when the shorter animation finishes (should wait
+// for both to finish).
+TEST(CCLayerAnimationControllerTest, ScheduleTogetherWithAnAnimWaiting)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity));
+
+ // Animations with id 1 should both start now.
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ // The opacity animation should have finished at time 1, but the group
+ // of animations with id 1 don't finish until time 2 because of the length
+ // of the transform animation.
+ controller->animate(2, events.get());
+ // Should not have started the float transition yet.
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+
+ // The second opacity animation should start at time 2 and should be done by time 3
+ controller->animate(3, events.get());
+ EXPECT_EQ(0.5, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future.
+TEST(CCLayerAnimationControllerTest, ScheduleAnimation)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+ toAdd->setStartTime(1);
+ controller->addAnimation(toAdd.release());
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(2, events.get());
+ EXPECT_EQ(1, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future that's interrupting a running animation.
+TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimation)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0.5, 0)), 2, CCActiveAnimation::Opacity));
+ toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+ toAdd->setStartTime(1);
+ controller->addAnimation(toAdd.release());
+
+ // First 2s opacity transition should start immediately.
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(0.5, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.5, dummy.opacity());
+ controller->animate(2, events.get());
+ EXPECT_EQ(0, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future that interrupts a running animation
+// and there is yet another animation queued to start later.
+TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimationWithAnimInQueue)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0.5, 0)), 2, CCActiveAnimation::Opacity));
+ toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+ toAdd->setStartTime(1);
+ controller->addAnimation(toAdd.release());
+
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 0.75)), 3, CCActiveAnimation::Opacity));
+
+ // First 2s opacity transition should start immediately.
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(0.5, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.5, dummy.opacity());
+ controller->animate(3, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(4, events.get());
+ EXPECT_EQ(0.75, dummy.opacity());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Test that a looping animation loops and for the correct number of iterations.
+TEST(CCLayerAnimationControllerTest, TrivialLooping)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+ toAdd->setIterations(3);
+ controller->addAnimation(toAdd.release());
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1.25, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ controller->animate(1.75, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+ controller->animate(2.25, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ controller->animate(2.75, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+ controller->animate(3, events.get());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+
+ // Just be extra sure.
+ controller->animate(4, events.get());
+ EXPECT_EQ(1, dummy.opacity());
+}
+
+// Test that an infinitely looping animation does indeed go until aborted.
+TEST(CCLayerAnimationControllerTest, InfiniteLooping)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ const int id = 1;
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity));
+ toAdd->setIterations(-1);
+ controller->addAnimation(toAdd.release());
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1.25, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ controller->animate(1.75, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+
+ controller->animate(1073741824.25, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.25, dummy.opacity());
+ controller->animate(1073741824.75, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+
+ EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+ controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 0.75);
+ EXPECT_FALSE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+}
+
+// Test that pausing and resuming work as expected.
+TEST(CCLayerAnimationControllerTest, PauseResume)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ const int id = 1;
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity));
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(0.5, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.5, dummy.opacity());
+
+ EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+ controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Paused, 0.5);
+
+ controller->animate(1024, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.5, dummy.opacity());
+
+ EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+ controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Running, 1024);
+
+ controller->animate(1024.25, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+ controller->animate(1024.5, events.get());
+ EXPECT_FALSE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+}
+
+TEST(CCLayerAnimationControllerTest, AbortAGroupedAnimation)
+{
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ const int id = 1;
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity));
+ controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.75)), 2, CCActiveAnimation::Opacity));
+
+ controller->animate(0, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0, dummy.opacity());
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(0.5, dummy.opacity());
+
+ EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+ controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 1);
+ controller->animate(1, events.get());
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ EXPECT_EQ(1, dummy.opacity());
+ controller->animate(2, events.get());
+ EXPECT_TRUE(!controller->hasActiveAnimation());
+ EXPECT_EQ(0.75, dummy.opacity());
+}
+
+TEST(CCLayerAnimationControllerTest, ForceSyncWhenSynchronizedStartTimeNeeded)
+{
+ FakeLayerAnimationControllerClient dummyImpl;
+ OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl));
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+ FakeLayerAnimationControllerClient dummy;
+ OwnPtr<CCLayerAnimationController> controller(
+ CCLayerAnimationController::create(&dummy));
+
+ OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 0, CCActiveAnimation::Opacity));
+ toAdd->setNeedsSynchronizedStartTime(true);
+ controller->addAnimation(toAdd.release());
+
+ controller->animate(0, 0);
+ EXPECT_TRUE(controller->hasActiveAnimation());
+ CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Opacity);
+ EXPECT_TRUE(activeAnimation);
+ EXPECT_TRUE(activeAnimation->needsSynchronizedStartTime());
+
+ controller->setForceSync();
+
+ controller->pushAnimationUpdatesTo(controllerImpl.get());
+
+ activeAnimation = controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity);
+ EXPECT_TRUE(activeAnimation);
+ EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, activeAnimation->runState());
+}
+
+} // namespace
diff --git a/cc/CCLayerImpl.cpp b/cc/CCLayerImpl.cpp
new file mode 100644
index 0000000..0a5df9e
--- /dev/null
+++ b/cc/CCLayerImpl.cpp
@@ -0,0 +1,626 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerImpl.h"
+
+#include "CCDebugBorderDrawQuad.h"
+#include "CCLayerSorter.h"
+#include "CCMathUtil.h"
+#include "CCProxy.h"
+#include "CCQuadSink.h"
+#include "CCScrollbarAnimationController.h"
+#include "TextStream.h"
+#include "TraceEvent.h"
+#include <wtf/text/WTFString.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+CCLayerImpl::CCLayerImpl(int id)
+ : m_parent(0)
+ , m_maskLayerId(-1)
+ , m_replicaLayerId(-1)
+ , m_layerId(id)
+ , m_layerTreeHostImpl(0)
+ , m_anchorPoint(0.5, 0.5)
+ , m_anchorPointZ(0)
+ , m_scrollable(false)
+ , m_shouldScrollOnMainThread(false)
+ , m_haveWheelEventHandlers(false)
+ , m_backgroundColor(0)
+ , m_doubleSided(true)
+ , m_layerPropertyChanged(false)
+ , m_layerSurfacePropertyChanged(false)
+ , m_masksToBounds(false)
+ , m_opaque(false)
+ , m_opacity(1.0)
+ , m_preserves3D(false)
+ , m_useParentBackfaceVisibility(false)
+ , m_drawCheckerboardForMissingTiles(false)
+ , m_useLCDText(false)
+ , m_drawsContent(false)
+ , m_forceRenderSurface(false)
+ , m_isContainerForFixedPositionLayers(false)
+ , m_fixedToContainerLayer(false)
+ , m_pageScaleDelta(1)
+ , m_renderTarget(0)
+ , m_drawDepth(0)
+ , m_drawOpacity(0)
+ , m_drawOpacityIsAnimating(false)
+ , m_debugBorderColor(0)
+ , m_debugBorderWidth(0)
+ , m_drawTransformIsAnimating(false)
+ , m_screenSpaceTransformIsAnimating(false)
+#ifndef NDEBUG
+ , m_betweenWillDrawAndDidDraw(false)
+#endif
+ , m_layerAnimationController(CCLayerAnimationController::create(this))
+{
+ ASSERT(CCProxy::isImplThread());
+ ASSERT(m_layerId > 0);
+}
+
+CCLayerImpl::~CCLayerImpl()
+{
+ ASSERT(CCProxy::isImplThread());
+#ifndef NDEBUG
+ ASSERT(!m_betweenWillDrawAndDidDraw);
+#endif
+}
+
+void CCLayerImpl::addChild(PassOwnPtr<CCLayerImpl> child)
+{
+ child->setParent(this);
+ m_children.append(child);
+}
+
+void CCLayerImpl::removeFromParent()
+{
+ if (!m_parent)
+ return;
+
+ CCLayerImpl* parent = m_parent;
+ m_parent = 0;
+
+ for (size_t i = 0; i < parent->m_children.size(); ++i) {
+ if (parent->m_children[i].get() == this) {
+ parent->m_children.remove(i);
+ return;
+ }
+ }
+}
+
+void CCLayerImpl::removeAllChildren()
+{
+ while (m_children.size())
+ m_children[0]->removeFromParent();
+}
+
+void CCLayerImpl::clearChildList()
+{
+ m_children.clear();
+}
+
+void CCLayerImpl::createRenderSurface()
+{
+ ASSERT(!m_renderSurface);
+ m_renderSurface = adoptPtr(new CCRenderSurface(this));
+ setRenderTarget(this);
+}
+
+bool CCLayerImpl::descendantDrawsContent()
+{
+ for (size_t i = 0; i < m_children.size(); ++i) {
+ if (m_children[i]->drawsContent() || m_children[i]->descendantDrawsContent())
+ return true;
+ }
+ return false;
+}
+
+PassOwnPtr<CCSharedQuadState> CCLayerImpl::createSharedQuadState() const
+{
+ return CCSharedQuadState::create(m_drawTransform, m_visibleContentRect, m_drawableContentRect, m_drawOpacity, m_opaque);
+}
+
+void CCLayerImpl::willDraw(CCResourceProvider*)
+{
+#ifndef NDEBUG
+ // willDraw/didDraw must be matched.
+ ASSERT(!m_betweenWillDrawAndDidDraw);
+ m_betweenWillDrawAndDidDraw = true;
+#endif
+}
+
+void CCLayerImpl::didDraw(CCResourceProvider*)
+{
+#ifndef NDEBUG
+ ASSERT(m_betweenWillDrawAndDidDraw);
+ m_betweenWillDrawAndDidDraw = false;
+#endif
+}
+
+void CCLayerImpl::appendDebugBorderQuad(CCQuadSink& quadList, const CCSharedQuadState* sharedQuadState) const
+{
+ if (!hasDebugBorders())
+ return;
+
+ IntRect contentRect(IntPoint(), contentBounds());
+ quadList.append(CCDebugBorderDrawQuad::create(sharedQuadState, contentRect, debugBorderColor(), debugBorderWidth()));
+}
+
+CCResourceProvider::ResourceId CCLayerImpl::contentsResourceId() const
+{
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void CCLayerImpl::scrollBy(const FloatSize& scroll)
+{
+ IntSize minDelta = -toSize(m_scrollPosition);
+ IntSize maxDelta = m_maxScrollPosition - toSize(m_scrollPosition);
+ // Clamp newDelta so that position + delta stays within scroll bounds.
+ FloatSize newDelta = (m_scrollDelta + scroll).expandedTo(minDelta).shrunkTo(maxDelta);
+
+ if (m_scrollDelta == newDelta)
+ return;
+
+ m_scrollDelta = newDelta;
+ if (m_scrollbarAnimationController)
+ m_scrollbarAnimationController->updateScrollOffset(this);
+ noteLayerPropertyChangedForSubtree();
+}
+
+CCInputHandlerClient::ScrollStatus CCLayerImpl::tryScroll(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType type) const
+{
+ if (shouldScrollOnMainThread()) {
+ TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed shouldScrollOnMainThread");
+ return CCInputHandlerClient::ScrollOnMainThread;
+ }
+
+ if (!screenSpaceTransform().isInvertible()) {
+ TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Ignored nonInvertibleTransform");
+ return CCInputHandlerClient::ScrollIgnored;
+ }
+
+ if (!nonFastScrollableRegion().isEmpty()) {
+ bool clipped = false;
+ FloatPoint hitTestPointInLocalSpace = CCMathUtil::projectPoint(screenSpaceTransform().inverse(), FloatPoint(viewportPoint), clipped);
+ if (!clipped && nonFastScrollableRegion().contains(flooredIntPoint(hitTestPointInLocalSpace))) {
+ TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed nonFastScrollableRegion");
+ return CCInputHandlerClient::ScrollOnMainThread;
+ }
+ }
+
+ if (type == CCInputHandlerClient::Wheel && haveWheelEventHandlers()) {
+ TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Failed wheelEventHandlers");
+ return CCInputHandlerClient::ScrollOnMainThread;
+ }
+
+ if (!scrollable()) {
+ TRACE_EVENT0("cc", "CCLayerImpl::tryScroll: Ignored not scrollable");
+ return CCInputHandlerClient::ScrollIgnored;
+ }
+
+ return CCInputHandlerClient::ScrollStarted;
+}
+
+void CCLayerImpl::writeIndent(TextStream& ts, int indent)
+{
+ for (int i = 0; i != indent; ++i)
+ ts << " ";
+}
+
+void CCLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << "layer ID: " << m_layerId << "\n";
+
+ writeIndent(ts, indent);
+ ts << "bounds: " << bounds().width() << ", " << bounds().height() << "\n";
+
+ if (m_renderTarget) {
+ writeIndent(ts, indent);
+ ts << "renderTarget: " << m_renderTarget->m_layerId << "\n";
+ }
+
+ writeIndent(ts, indent);
+ ts << "drawTransform: ";
+ ts << m_drawTransform.m11() << ", " << m_drawTransform.m12() << ", " << m_drawTransform.m13() << ", " << m_drawTransform.m14() << " // ";
+ ts << m_drawTransform.m21() << ", " << m_drawTransform.m22() << ", " << m_drawTransform.m23() << ", " << m_drawTransform.m24() << " // ";
+ ts << m_drawTransform.m31() << ", " << m_drawTransform.m32() << ", " << m_drawTransform.m33() << ", " << m_drawTransform.m34() << " // ";
+ ts << m_drawTransform.m41() << ", " << m_drawTransform.m42() << ", " << m_drawTransform.m43() << ", " << m_drawTransform.m44() << "\n";
+
+ writeIndent(ts, indent);
+ ts << "drawsContent: " << (m_drawsContent ? "yes" : "no") << "\n";
+}
+
+void sortLayers(Vector<CCLayerImpl*>::iterator first, Vector<CCLayerImpl*>::iterator end, CCLayerSorter* layerSorter)
+{
+ TRACE_EVENT0("cc", "CCLayerImpl::sortLayers");
+ layerSorter->sort(first, end);
+}
+
+String CCLayerImpl::layerTreeAsText() const
+{
+ TextStream ts;
+ dumpLayer(ts, 0);
+ return ts.release();
+}
+
+void CCLayerImpl::dumpLayer(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << layerTypeAsString() << "(" << m_debugName << ")\n";
+ dumpLayerProperties(ts, indent+2);
+ if (m_replicaLayer) {
+ writeIndent(ts, indent+2);
+ ts << "Replica:\n";
+ m_replicaLayer->dumpLayer(ts, indent+3);
+ }
+ if (m_maskLayer) {
+ writeIndent(ts, indent+2);
+ ts << "Mask:\n";
+ m_maskLayer->dumpLayer(ts, indent+3);
+ }
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->dumpLayer(ts, indent+1);
+}
+
+void CCLayerImpl::setStackingOrderChanged(bool stackingOrderChanged)
+{
+ // We don't need to store this flag; we only need to track that the change occurred.
+ if (stackingOrderChanged)
+ noteLayerPropertyChangedForSubtree();
+}
+
+bool CCLayerImpl::layerSurfacePropertyChanged() const
+{
+ if (m_layerSurfacePropertyChanged)
+ return true;
+
+ // If this layer's surface property hasn't changed, we want to see if
+ // some layer above us has changed this property. This is done for the
+ // case when such parent layer does not draw content, and therefore will
+ // not be traversed by the damage tracker. We need to make sure that
+ // property change on such layer will be caught by its descendants.
+ CCLayerImpl* current = this->m_parent;
+ while (current && !current->m_renderSurface) {
+ if (current->m_layerSurfacePropertyChanged)
+ return true;
+ current = current->m_parent;
+ }
+
+ return false;
+}
+
+void CCLayerImpl::noteLayerPropertyChangedForSubtree()
+{
+ m_layerPropertyChanged = true;
+ noteLayerPropertyChangedForDescendants();
+}
+
+void CCLayerImpl::noteLayerPropertyChangedForDescendants()
+{
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::resetAllChangeTrackingForSubtree()
+{
+ m_layerPropertyChanged = false;
+ m_layerSurfacePropertyChanged = false;
+
+ m_updateRect = FloatRect();
+
+ if (m_renderSurface)
+ m_renderSurface->resetPropertyChangedFlag();
+
+ if (m_maskLayer)
+ m_maskLayer->resetAllChangeTrackingForSubtree();
+
+ if (m_replicaLayer)
+ m_replicaLayer->resetAllChangeTrackingForSubtree(); // also resets the replica mask, if it exists.
+
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->resetAllChangeTrackingForSubtree();
+}
+
+void CCLayerImpl::setOpacityFromAnimation(float opacity)
+{
+ setOpacity(opacity);
+}
+
+void CCLayerImpl::setTransformFromAnimation(const WebTransformationMatrix& transform)
+{
+ setTransform(transform);
+}
+
+void CCLayerImpl::setBounds(const IntSize& bounds)
+{
+ if (m_bounds == bounds)
+ return;
+
+ m_bounds = bounds;
+
+ if (masksToBounds())
+ noteLayerPropertyChangedForSubtree();
+ else
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setMaskLayer(PassOwnPtr<CCLayerImpl> maskLayer)
+{
+ m_maskLayer = maskLayer;
+
+ int newLayerId = m_maskLayer ? m_maskLayer->id() : -1;
+ if (newLayerId == m_maskLayerId)
+ return;
+
+ m_maskLayerId = newLayerId;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setReplicaLayer(PassOwnPtr<CCLayerImpl> replicaLayer)
+{
+ m_replicaLayer = replicaLayer;
+
+ int newLayerId = m_replicaLayer ? m_replicaLayer->id() : -1;
+ if (newLayerId == m_replicaLayerId)
+ return;
+
+ m_replicaLayerId = newLayerId;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setDrawsContent(bool drawsContent)
+{
+ if (m_drawsContent == drawsContent)
+ return;
+
+ m_drawsContent = drawsContent;
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setAnchorPoint(const FloatPoint& anchorPoint)
+{
+ if (m_anchorPoint == anchorPoint)
+ return;
+
+ m_anchorPoint = anchorPoint;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setAnchorPointZ(float anchorPointZ)
+{
+ if (m_anchorPointZ == anchorPointZ)
+ return;
+
+ m_anchorPointZ = anchorPointZ;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setBackgroundColor(SkColor backgroundColor)
+{
+ if (m_backgroundColor == backgroundColor)
+ return;
+
+ m_backgroundColor = backgroundColor;
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setFilters(const WebKit::WebFilterOperations& filters)
+{
+ if (m_filters == filters)
+ return;
+
+ m_filters = filters;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setBackgroundFilters(const WebKit::WebFilterOperations& backgroundFilters)
+{
+ if (m_backgroundFilters == backgroundFilters)
+ return;
+
+ m_backgroundFilters = backgroundFilters;
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setMasksToBounds(bool masksToBounds)
+{
+ if (m_masksToBounds == masksToBounds)
+ return;
+
+ m_masksToBounds = masksToBounds;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setOpaque(bool opaque)
+{
+ if (m_opaque == opaque)
+ return;
+
+ m_opaque = opaque;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setOpacity(float opacity)
+{
+ if (m_opacity == opacity)
+ return;
+
+ m_opacity = opacity;
+ m_layerSurfacePropertyChanged = true;
+}
+
+bool CCLayerImpl::opacityIsAnimating() const
+{
+ return m_layerAnimationController->isAnimatingProperty(CCActiveAnimation::Opacity);
+}
+
+void CCLayerImpl::setPosition(const FloatPoint& position)
+{
+ if (m_position == position)
+ return;
+
+ m_position = position;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setPreserves3D(bool preserves3D)
+{
+ if (m_preserves3D == preserves3D)
+ return;
+
+ m_preserves3D = preserves3D;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setSublayerTransform(const WebTransformationMatrix& sublayerTransform)
+{
+ if (m_sublayerTransform == sublayerTransform)
+ return;
+
+ m_sublayerTransform = sublayerTransform;
+ // sublayer transform does not affect the current layer; it affects only its children.
+ noteLayerPropertyChangedForDescendants();
+}
+
+void CCLayerImpl::setTransform(const WebTransformationMatrix& transform)
+{
+ if (m_transform == transform)
+ return;
+
+ m_transform = transform;
+ m_layerSurfacePropertyChanged = true;
+}
+
+bool CCLayerImpl::transformIsAnimating() const
+{
+ return m_layerAnimationController->isAnimatingProperty(CCActiveAnimation::Transform);
+}
+
+void CCLayerImpl::setDebugBorderColor(SkColor debugBorderColor)
+{
+ if (m_debugBorderColor == debugBorderColor)
+ return;
+
+ m_debugBorderColor = debugBorderColor;
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setDebugBorderWidth(float debugBorderWidth)
+{
+ if (m_debugBorderWidth == debugBorderWidth)
+ return;
+
+ m_debugBorderWidth = debugBorderWidth;
+ m_layerPropertyChanged = true;
+}
+
+bool CCLayerImpl::hasDebugBorders() const
+{
+ return SkColorGetA(m_debugBorderColor) && debugBorderWidth() > 0;
+}
+
+void CCLayerImpl::setContentBounds(const IntSize& contentBounds)
+{
+ if (m_contentBounds == contentBounds)
+ return;
+
+ m_contentBounds = contentBounds;
+ m_layerPropertyChanged = true;
+}
+
+void CCLayerImpl::setScrollPosition(const IntPoint& scrollPosition)
+{
+ if (m_scrollPosition == scrollPosition)
+ return;
+
+ m_scrollPosition = scrollPosition;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setScrollDelta(const FloatSize& scrollDelta)
+{
+ if (m_scrollDelta == scrollDelta)
+ return;
+
+ m_scrollDelta = scrollDelta;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setPageScaleDelta(float pageScaleDelta)
+{
+ if (m_pageScaleDelta == pageScaleDelta)
+ return;
+
+ m_pageScaleDelta = pageScaleDelta;
+ noteLayerPropertyChangedForSubtree();
+}
+
+void CCLayerImpl::setDoubleSided(bool doubleSided)
+{
+ if (m_doubleSided == doubleSided)
+ return;
+
+ m_doubleSided = doubleSided;
+ noteLayerPropertyChangedForSubtree();
+}
+
+Region CCLayerImpl::visibleContentOpaqueRegion() const
+{
+ if (opaque())
+ return visibleContentRect();
+ return Region();
+}
+
+void CCLayerImpl::didLoseContext()
+{
+}
+
+void CCLayerImpl::setMaxScrollPosition(const IntSize& maxScrollPosition)
+{
+ m_maxScrollPosition = maxScrollPosition;
+
+ if (!m_scrollbarAnimationController)
+ return;
+ m_scrollbarAnimationController->updateScrollOffset(this);
+}
+
+CCScrollbarLayerImpl* CCLayerImpl::horizontalScrollbarLayer() const
+{
+ return m_scrollbarAnimationController ? m_scrollbarAnimationController->horizontalScrollbarLayer() : 0;
+}
+
+void CCLayerImpl::setHorizontalScrollbarLayer(CCScrollbarLayerImpl* scrollbarLayer)
+{
+ if (!m_scrollbarAnimationController)
+ m_scrollbarAnimationController = CCScrollbarAnimationController::create(this);
+ m_scrollbarAnimationController->setHorizontalScrollbarLayer(scrollbarLayer);
+ m_scrollbarAnimationController->updateScrollOffset(this);
+}
+
+CCScrollbarLayerImpl* CCLayerImpl::verticalScrollbarLayer() const
+{
+ return m_scrollbarAnimationController ? m_scrollbarAnimationController->verticalScrollbarLayer() : 0;
+}
+
+void CCLayerImpl::setVerticalScrollbarLayer(CCScrollbarLayerImpl* scrollbarLayer)
+{
+ if (!m_scrollbarAnimationController)
+ m_scrollbarAnimationController = CCScrollbarAnimationController::create(this);
+ m_scrollbarAnimationController->setVerticalScrollbarLayer(scrollbarLayer);
+ m_scrollbarAnimationController->updateScrollOffset(this);
+}
+
+}
+
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCLayerImpl.h b/cc/CCLayerImpl.h
new file mode 100644
index 0000000..3e42478
--- /dev/null
+++ b/cc/CCLayerImpl.h
@@ -0,0 +1,390 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerImpl_h
+#define CCLayerImpl_h
+
+#include "CCInputHandler.h"
+#include "CCLayerAnimationController.h"
+#include "CCRenderSurface.h"
+#include "CCResourceProvider.h"
+#include "CCSharedQuadState.h"
+#include "FloatRect.h"
+#include "IntRect.h"
+#include "Region.h"
+#include "SkColor.h"
+#include "TextStream.h"
+#include <public/WebFilterOperations.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CCLayerSorter;
+class CCLayerTreeHostImpl;
+class CCRenderer;
+class CCQuadSink;
+class CCScrollbarAnimationController;
+class CCScrollbarLayerImpl;
+class LayerChromium;
+
+class CCLayerImpl : public CCLayerAnimationControllerClient {
+public:
+ static PassOwnPtr<CCLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCLayerImpl(id));
+ }
+
+ virtual ~CCLayerImpl();
+
+ // CCLayerAnimationControllerClient implementation.
+ virtual int id() const OVERRIDE { return m_layerId; }
+ virtual void setOpacityFromAnimation(float) OVERRIDE;
+ virtual float opacity() const OVERRIDE { return m_opacity; }
+ virtual void setTransformFromAnimation(const WebKit::WebTransformationMatrix&) OVERRIDE;
+ virtual const WebKit::WebTransformationMatrix& transform() const OVERRIDE { return m_transform; }
+
+ // Tree structure.
+ CCLayerImpl* parent() const { return m_parent; }
+ const Vector<OwnPtr<CCLayerImpl> >& children() const { return m_children; }
+ void addChild(PassOwnPtr<CCLayerImpl>);
+ void removeFromParent();
+ void removeAllChildren();
+
+ void setMaskLayer(PassOwnPtr<CCLayerImpl>);
+ CCLayerImpl* maskLayer() const { return m_maskLayer.get(); }
+
+ void setReplicaLayer(PassOwnPtr<CCLayerImpl>);
+ CCLayerImpl* replicaLayer() const { return m_replicaLayer.get(); }
+
+ bool hasMask() const { return m_maskLayer; }
+ bool hasReplica() const { return m_replicaLayer; }
+ bool replicaHasMask() const { return m_replicaLayer && (m_maskLayer || m_replicaLayer->m_maskLayer); }
+
+ CCLayerTreeHostImpl* layerTreeHostImpl() const { return m_layerTreeHostImpl; }
+ void setLayerTreeHostImpl(CCLayerTreeHostImpl* hostImpl) { m_layerTreeHostImpl = hostImpl; }
+
+ PassOwnPtr<CCSharedQuadState> createSharedQuadState() const;
+ // willDraw must be called before appendQuads. If willDraw is called,
+ // didDraw is guaranteed to be called before another willDraw or before
+ // the layer is destroyed. To enforce this, any class that overrides
+ // willDraw/didDraw must call the base class version.
+ virtual void willDraw(CCResourceProvider*);
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) { }
+ virtual void didDraw(CCResourceProvider*);
+
+ virtual CCResourceProvider::ResourceId contentsResourceId() const;
+
+ // Returns true if this layer has content to draw.
+ void setDrawsContent(bool);
+ bool drawsContent() const { return m_drawsContent; }
+
+ bool forceRenderSurface() const { return m_forceRenderSurface; }
+ void setForceRenderSurface(bool force) { m_forceRenderSurface = force; }
+
+ // Returns true if any of the layer's descendants has content to draw.
+ bool descendantDrawsContent();
+
+ void setAnchorPoint(const FloatPoint&);
+ const FloatPoint& anchorPoint() const { return m_anchorPoint; }
+
+ void setAnchorPointZ(float);
+ float anchorPointZ() const { return m_anchorPointZ; }
+
+ void setBackgroundColor(SkColor);
+ SkColor backgroundColor() const { return m_backgroundColor; }
+
+ void setFilters(const WebKit::WebFilterOperations&);
+ const WebKit::WebFilterOperations& filters() const { return m_filters; }
+
+ void setBackgroundFilters(const WebKit::WebFilterOperations&);
+ const WebKit::WebFilterOperations& backgroundFilters() const { return m_backgroundFilters; }
+
+ void setMasksToBounds(bool);
+ bool masksToBounds() const { return m_masksToBounds; }
+
+ void setOpaque(bool);
+ bool opaque() const { return m_opaque; }
+
+ void setOpacity(float);
+ bool opacityIsAnimating() const;
+
+ void setPosition(const FloatPoint&);
+ const FloatPoint& position() const { return m_position; }
+
+ void setIsContainerForFixedPositionLayers(bool isContainerForFixedPositionLayers) { m_isContainerForFixedPositionLayers = isContainerForFixedPositionLayers; }
+ bool isContainerForFixedPositionLayers() const { return m_isContainerForFixedPositionLayers; }
+
+ void setFixedToContainerLayer(bool fixedToContainerLayer = true) { m_fixedToContainerLayer = fixedToContainerLayer;}
+ bool fixedToContainerLayer() const { return m_fixedToContainerLayer; }
+
+ void setPreserves3D(bool);
+ bool preserves3D() const { return m_preserves3D; }
+
+ void setUseParentBackfaceVisibility(bool useParentBackfaceVisibility) { m_useParentBackfaceVisibility = useParentBackfaceVisibility; }
+ bool useParentBackfaceVisibility() const { return m_useParentBackfaceVisibility; }
+
+ void setUseLCDText(bool useLCDText) { m_useLCDText = useLCDText; }
+ bool useLCDText() const { return m_useLCDText; }
+
+ void setSublayerTransform(const WebKit::WebTransformationMatrix&);
+ const WebKit::WebTransformationMatrix& sublayerTransform() const { return m_sublayerTransform; }
+
+ // Debug layer border - visual effect only, do not change geometry/clipping/etc.
+ void setDebugBorderColor(SkColor);
+ SkColor debugBorderColor() const { return m_debugBorderColor; }
+ void setDebugBorderWidth(float);
+ float debugBorderWidth() const { return m_debugBorderWidth; }
+ bool hasDebugBorders() const;
+
+ // Debug layer name.
+ void setDebugName(const String& debugName) { m_debugName = debugName; }
+ String debugName() const { return m_debugName; }
+
+ CCRenderSurface* renderSurface() const { return m_renderSurface.get(); }
+ void createRenderSurface();
+ void clearRenderSurface() { m_renderSurface.clear(); }
+
+ float drawOpacity() const { return m_drawOpacity; }
+ void setDrawOpacity(float opacity) { m_drawOpacity = opacity; }
+
+ bool drawOpacityIsAnimating() const { return m_drawOpacityIsAnimating; }
+ void setDrawOpacityIsAnimating(bool drawOpacityIsAnimating) { m_drawOpacityIsAnimating = drawOpacityIsAnimating; }
+
+ CCLayerImpl* renderTarget() const { ASSERT(!m_renderTarget || m_renderTarget->renderSurface()); return m_renderTarget; }
+ void setRenderTarget(CCLayerImpl* target) { m_renderTarget = target; }
+
+ void setBounds(const IntSize&);
+ const IntSize& bounds() const { return m_bounds; }
+
+ const IntSize& contentBounds() const { return m_contentBounds; }
+ void setContentBounds(const IntSize&);
+
+ const IntPoint& scrollPosition() const { return m_scrollPosition; }
+ void setScrollPosition(const IntPoint&);
+
+ const IntSize& maxScrollPosition() const {return m_maxScrollPosition; }
+ void setMaxScrollPosition(const IntSize&);
+
+ const FloatSize& scrollDelta() const { return m_scrollDelta; }
+ void setScrollDelta(const FloatSize&);
+
+ float pageScaleDelta() const { return m_pageScaleDelta; }
+ void setPageScaleDelta(float);
+
+ const IntSize& sentScrollDelta() const { return m_sentScrollDelta; }
+ void setSentScrollDelta(const IntSize& sentScrollDelta) { m_sentScrollDelta = sentScrollDelta; }
+
+ void scrollBy(const FloatSize& scroll);
+
+ bool scrollable() const { return m_scrollable; }
+ void setScrollable(bool scrollable) { m_scrollable = scrollable; }
+
+ bool shouldScrollOnMainThread() const { return m_shouldScrollOnMainThread; }
+ void setShouldScrollOnMainThread(bool shouldScrollOnMainThread) { m_shouldScrollOnMainThread = shouldScrollOnMainThread; }
+
+ bool haveWheelEventHandlers() const { return m_haveWheelEventHandlers; }
+ void setHaveWheelEventHandlers(bool haveWheelEventHandlers) { m_haveWheelEventHandlers = haveWheelEventHandlers; }
+
+ const Region& nonFastScrollableRegion() const { return m_nonFastScrollableRegion; }
+ void setNonFastScrollableRegion(const Region& region) { m_nonFastScrollableRegion = region; }
+
+ void setDrawCheckerboardForMissingTiles(bool checkerboard) { m_drawCheckerboardForMissingTiles = checkerboard; }
+ bool drawCheckerboardForMissingTiles() const { return m_drawCheckerboardForMissingTiles; }
+
+ CCInputHandlerClient::ScrollStatus tryScroll(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType) const;
+
+ const IntRect& visibleContentRect() const { return m_visibleContentRect; }
+ void setVisibleContentRect(const IntRect& visibleContentRect) { m_visibleContentRect = visibleContentRect; }
+
+ bool doubleSided() const { return m_doubleSided; }
+ void setDoubleSided(bool);
+
+ void setTransform(const WebKit::WebTransformationMatrix&);
+ bool transformIsAnimating() const;
+
+ const WebKit::WebTransformationMatrix& drawTransform() const { return m_drawTransform; }
+ void setDrawTransform(const WebKit::WebTransformationMatrix& matrix) { m_drawTransform = matrix; }
+ const WebKit::WebTransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
+ void setScreenSpaceTransform(const WebKit::WebTransformationMatrix& matrix) { m_screenSpaceTransform = matrix; }
+
+ bool drawTransformIsAnimating() const { return m_drawTransformIsAnimating; }
+ void setDrawTransformIsAnimating(bool animating) { m_drawTransformIsAnimating = animating; }
+ bool screenSpaceTransformIsAnimating() const { return m_screenSpaceTransformIsAnimating; }
+ void setScreenSpaceTransformIsAnimating(bool animating) { m_screenSpaceTransformIsAnimating = animating; }
+
+ const IntRect& drawableContentRect() const { return m_drawableContentRect; }
+ void setDrawableContentRect(const IntRect& rect) { m_drawableContentRect = rect; }
+ const FloatRect& updateRect() const { return m_updateRect; }
+ void setUpdateRect(const FloatRect& updateRect) { m_updateRect = updateRect; }
+
+ String layerTreeAsText() const;
+
+ void setStackingOrderChanged(bool);
+
+ bool layerPropertyChanged() const { return m_layerPropertyChanged || layerIsAlwaysDamaged(); }
+ bool layerSurfacePropertyChanged() const;
+
+ void resetAllChangeTrackingForSubtree();
+
+ virtual bool layerIsAlwaysDamaged() const { return false; }
+
+ CCLayerAnimationController* layerAnimationController() { return m_layerAnimationController.get(); }
+
+ virtual Region visibleContentOpaqueRegion() const;
+
+ // Indicates that the context previously used to render this layer
+ // was lost and that a new one has been created. Won't be called
+ // until the new context has been created successfully.
+ virtual void didLoseContext();
+
+ CCScrollbarAnimationController* scrollbarAnimationController() const { return m_scrollbarAnimationController.get(); }
+
+ CCScrollbarLayerImpl* horizontalScrollbarLayer() const;
+ void setHorizontalScrollbarLayer(CCScrollbarLayerImpl*);
+
+ CCScrollbarLayerImpl* verticalScrollbarLayer() const;
+ void setVerticalScrollbarLayer(CCScrollbarLayerImpl*);
+
+protected:
+ explicit CCLayerImpl(int);
+
+ void appendDebugBorderQuad(CCQuadSink&, const CCSharedQuadState*) const;
+
+ virtual void dumpLayerProperties(TextStream&, int indent) const;
+ static void writeIndent(TextStream&, int indent);
+
+private:
+ void setParent(CCLayerImpl* parent) { m_parent = parent; }
+ friend class TreeSynchronizer;
+ void clearChildList(); // Warning: This does not preserve tree structure invariants and so is only exposed to the tree synchronizer.
+
+ void noteLayerPropertyChangedForSubtree();
+
+ // Note carefully this does not affect the current layer.
+ void noteLayerPropertyChangedForDescendants();
+
+ virtual const char* layerTypeAsString() const { return "LayerChromium"; }
+
+ void dumpLayer(TextStream&, int indent) const;
+
+ // Properties internal to CCLayerImpl
+ CCLayerImpl* m_parent;
+ Vector<OwnPtr<CCLayerImpl> > m_children;
+ // m_maskLayer can be temporarily stolen during tree sync, we need this ID to confirm newly assigned layer is still the previous one
+ int m_maskLayerId;
+ OwnPtr<CCLayerImpl> m_maskLayer;
+ int m_replicaLayerId; // ditto
+ OwnPtr<CCLayerImpl> m_replicaLayer;
+ int m_layerId;
+ CCLayerTreeHostImpl* m_layerTreeHostImpl;
+
+ // Properties synchronized from the associated LayerChromium.
+ FloatPoint m_anchorPoint;
+ float m_anchorPointZ;
+ IntSize m_bounds;
+ IntSize m_contentBounds;
+ IntPoint m_scrollPosition;
+ bool m_scrollable;
+ bool m_shouldScrollOnMainThread;
+ bool m_haveWheelEventHandlers;
+ Region m_nonFastScrollableRegion;
+ SkColor m_backgroundColor;
+
+ // Whether the "back" of this layer should draw.
+ bool m_doubleSided;
+
+ // Tracks if drawing-related properties have changed since last redraw.
+ bool m_layerPropertyChanged;
+
+ // Indicates that a property has changed on this layer that would not
+ // affect the pixels on its target surface, but would require redrawing
+ // but would require redrawing the targetSurface onto its ancestor targetSurface.
+ // For layers that do not own a surface this flag acts as m_layerPropertyChanged.
+ bool m_layerSurfacePropertyChanged;
+
+ // Uses layer's content space.
+ IntRect m_visibleContentRect;
+ bool m_masksToBounds;
+ bool m_opaque;
+ float m_opacity;
+ FloatPoint m_position;
+ bool m_preserves3D;
+ bool m_useParentBackfaceVisibility;
+ bool m_drawCheckerboardForMissingTiles;
+ WebKit::WebTransformationMatrix m_sublayerTransform;
+ WebKit::WebTransformationMatrix m_transform;
+ bool m_useLCDText;
+
+ bool m_drawsContent;
+ bool m_forceRenderSurface;
+
+ // Set for the layer that other layers are fixed to.
+ bool m_isContainerForFixedPositionLayers;
+ // This is true if the layer should be fixed to the closest ancestor container.
+ bool m_fixedToContainerLayer;
+
+ FloatSize m_scrollDelta;
+ IntSize m_sentScrollDelta;
+ IntSize m_maxScrollPosition;
+ float m_pageScaleDelta;
+
+ // The layer whose coordinate space this layer draws into. This can be
+ // either the same layer (m_renderTarget == this) or an ancestor of this
+ // layer.
+ CCLayerImpl* m_renderTarget;
+
+ // The global depth value of the center of the layer. This value is used
+ // to sort layers from back to front.
+ float m_drawDepth;
+ float m_drawOpacity;
+ bool m_drawOpacityIsAnimating;
+
+ // Debug borders.
+ SkColor m_debugBorderColor;
+ float m_debugBorderWidth;
+
+ // Debug layer name.
+ String m_debugName;
+
+ WebKit::WebFilterOperations m_filters;
+ WebKit::WebFilterOperations m_backgroundFilters;
+
+ WebKit::WebTransformationMatrix m_drawTransform;
+ WebKit::WebTransformationMatrix m_screenSpaceTransform;
+ bool m_drawTransformIsAnimating;
+ bool m_screenSpaceTransformIsAnimating;
+
+#ifndef NDEBUG
+ bool m_betweenWillDrawAndDidDraw;
+#endif
+
+ // Render surface associated with this layer. The layer and its descendants
+ // will render to this surface.
+ OwnPtr<CCRenderSurface> m_renderSurface;
+
+ // Hierarchical bounding rect containing the layer and its descendants.
+ // Uses target surface's space.
+ IntRect m_drawableContentRect;
+
+ // Rect indicating what was repainted/updated during update.
+ // Note that plugin layers bypass this and leave it empty.
+ // Uses layer's content space.
+ FloatRect m_updateRect;
+
+ // Manages animations for this layer.
+ OwnPtr<CCLayerAnimationController> m_layerAnimationController;
+
+ // Manages scrollbars for this layer
+ OwnPtr<CCScrollbarAnimationController> m_scrollbarAnimationController;
+};
+
+void sortLayers(Vector<CCLayerImpl*>::iterator first, Vector<CCLayerImpl*>::iterator end, CCLayerSorter*);
+
+}
+
+#endif // CCLayerImpl_h
diff --git a/cc/CCLayerImplTest.cpp b/cc/CCLayerImplTest.cpp
new file mode 100644
index 0000000..cc4dfbb
--- /dev/null
+++ b/cc/CCLayerImplTest.cpp
@@ -0,0 +1,160 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerImpl.h"
+
+#include "CCSingleThreadProxy.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+
+using namespace WebKit;
+using namespace WebCore;
+
+namespace {
+
+#define EXECUTE_AND_VERIFY_SUBTREE_CHANGED(codeToTest) \
+ root->resetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_TRUE(root->layerPropertyChanged()); \
+ EXPECT_TRUE(child->layerPropertyChanged()); \
+ EXPECT_TRUE(grandChild->layerPropertyChanged()); \
+ EXPECT_FALSE(root->layerSurfacePropertyChanged())
+
+
+#define EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(codeToTest) \
+ root->resetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_FALSE(root->layerPropertyChanged()); \
+ EXPECT_FALSE(child->layerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->layerPropertyChanged()); \
+ EXPECT_FALSE(root->layerSurfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(codeToTest) \
+ root->resetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_TRUE(root->layerPropertyChanged()); \
+ EXPECT_FALSE(child->layerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->layerPropertyChanged()); \
+ EXPECT_FALSE(root->layerSurfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(codeToTest) \
+ root->resetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_FALSE(root->layerPropertyChanged()); \
+ EXPECT_FALSE(child->layerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->layerPropertyChanged()); \
+ EXPECT_TRUE(root->layerSurfacePropertyChanged())
+
+TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly)
+{
+ //
+ // This test checks that layerPropertyChanged() has the correct behavior.
+ //
+
+ // The constructor on this will fake that we are on the correct thread.
+ DebugScopedSetImplThread setImplThread;
+
+ // Create a simple CCLayerImpl tree:
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->addChild(CCLayerImpl::create(2));
+ CCLayerImpl* child = root->children()[0].get();
+ child->addChild(CCLayerImpl::create(3));
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ // Adding children is an internal operation and should not mark layers as changed.
+ EXPECT_FALSE(root->layerPropertyChanged());
+ EXPECT_FALSE(child->layerPropertyChanged());
+ EXPECT_FALSE(grandChild->layerPropertyChanged());
+
+ FloatPoint arbitraryFloatPoint = FloatPoint(0.125f, 0.25f);
+ float arbitraryNumber = 0.352f;
+ IntSize arbitraryIntSize = IntSize(111, 222);
+ IntPoint arbitraryIntPoint = IntPoint(333, 444);
+ IntRect arbitraryIntRect = IntRect(arbitraryIntPoint, arbitraryIntSize);
+ FloatRect arbitraryFloatRect = FloatRect(arbitraryFloatPoint, FloatSize(1.234f, 5.678f));
+ SkColor arbitraryColor = SkColorSetRGB(10, 20, 30);
+ WebTransformationMatrix arbitraryTransform;
+ arbitraryTransform.scale3d(0.1, 0.2, 0.3);
+ WebFilterOperations arbitraryFilters;
+ arbitraryFilters.append(WebFilterOperation::createOpacityFilter(0.5));
+
+ // These properties are internal, and should not be considered "change" when they are used.
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setUseLCDText(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDrawOpacity(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setRenderTarget(0));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDrawTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setScreenSpaceTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDrawableContentRect(arbitraryIntRect));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setUpdateRect(arbitraryFloatRect));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setVisibleContentRect(arbitraryIntRect));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setMaxScrollPosition(arbitraryIntSize));
+
+ // Changing these properties affects the entire subtree of layers.
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setAnchorPoint(arbitraryFloatPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setAnchorPointZ(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setFilters(arbitraryFilters));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setMaskLayer(CCLayerImpl::create(4)));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setMasksToBounds(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setOpaque(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setReplicaLayer(CCLayerImpl::create(5)));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setPosition(arbitraryFloatPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setPreserves3D(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setDoubleSided(false)); // constructor initializes it to "true".
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->scrollBy(arbitraryIntSize));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setScrollDelta(IntSize()));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setScrollPosition(arbitraryIntPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setPageScaleDelta(arbitraryNumber));
+
+ // Changing these properties only affects the layer itself.
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setContentBounds(arbitraryIntSize));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setDebugBorderColor(arbitraryColor));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setDebugBorderWidth(arbitraryNumber));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setDrawsContent(true));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundColor(SK_ColorGRAY));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundFilters(arbitraryFilters));
+
+ // Changing these properties only affects how render surface is drawn
+ EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->setOpacity(arbitraryNumber));
+ EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->setTransform(arbitraryTransform));
+
+ // Special case: check that sublayer transform changes all layer's descendants, but not the layer itself.
+ root->resetAllChangeTrackingForSubtree();
+ root->setSublayerTransform(arbitraryTransform);
+ EXPECT_FALSE(root->layerPropertyChanged());
+ EXPECT_TRUE(child->layerPropertyChanged());
+ EXPECT_TRUE(grandChild->layerPropertyChanged());
+
+ // Special case: check that setBounds changes behavior depending on masksToBounds.
+ root->setMasksToBounds(false);
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBounds(IntSize(135, 246)));
+ root->setMasksToBounds(true);
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setBounds(arbitraryIntSize)); // should be a different size than previous call, to ensure it marks tree changed.
+
+ // After setting all these properties already, setting to the exact same values again should
+ // not cause any change.
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setAnchorPoint(arbitraryFloatPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setAnchorPointZ(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setMasksToBounds(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setPosition(arbitraryFloatPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setPreserves3D(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDoubleSided(false)); // constructor initializes it to "true".
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setScrollDelta(IntSize()));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setScrollPosition(arbitraryIntPoint));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setPageScaleDelta(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setContentBounds(arbitraryIntSize));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setOpaque(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setOpacity(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDebugBorderColor(arbitraryColor));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDebugBorderWidth(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setDrawsContent(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setSublayerTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->setBounds(arbitraryIntSize));
+}
+
+} // namespace
diff --git a/cc/CCLayerIterator.cpp b/cc/CCLayerIterator.cpp
new file mode 100644
index 0000000..d7de9a5
--- /dev/null
+++ b/cc/CCLayerIterator.cpp
@@ -0,0 +1,150 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerIterator.h"
+
+#include "CCLayerImpl.h"
+#include "CCRenderSurface.h"
+#include "LayerChromium.h"
+#include "RenderSurfaceChromium.h"
+
+namespace WebCore {
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::BackToFront::begin(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = 0;
+ it.m_currentLayerIndex = CCLayerIteratorValue::LayerIndexRepresentingTargetRenderSurface;
+
+ m_highestTargetRenderSurfaceLayer = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::BackToFront::end(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = CCLayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::BackToFront::next(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ // If the current layer has a RS, move to its layer list. Otherwise, visit the next layer in the current RS layer list.
+ if (it.currentLayerRepresentsContributingRenderSurface()) {
+ // Save our position in the childLayer list for the RenderSurface, then jump to the next RenderSurface. Save where we
+ // came from in the next RenderSurface so we can get back to it.
+ it.targetRenderSurface()->m_currentLayerIndexHistory = it.m_currentLayerIndex;
+ int previousTargetRenderSurfaceLayer = it.m_targetRenderSurfaceLayerIndex;
+
+ it.m_targetRenderSurfaceLayerIndex = ++m_highestTargetRenderSurfaceLayer;
+ it.m_currentLayerIndex = CCLayerIteratorValue::LayerIndexRepresentingTargetRenderSurface;
+
+ it.targetRenderSurface()->m_targetRenderSurfaceLayerIndexHistory = previousTargetRenderSurfaceLayer;
+ } else {
+ ++it.m_currentLayerIndex;
+
+ int targetRenderSurfaceNumChildren = it.targetRenderSurfaceChildren().size();
+ while (it.m_currentLayerIndex == targetRenderSurfaceNumChildren) {
+ // Jump back to the previous RenderSurface, and get back the position where we were in that list, and move to the next position there.
+ if (!it.m_targetRenderSurfaceLayerIndex) {
+ // End of the list
+ it.m_targetRenderSurfaceLayerIndex = CCLayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+ return;
+ }
+ it.m_targetRenderSurfaceLayerIndex = it.targetRenderSurface()->m_targetRenderSurfaceLayerIndexHistory;
+ it.m_currentLayerIndex = it.targetRenderSurface()->m_currentLayerIndexHistory + 1;
+
+ targetRenderSurfaceNumChildren = it.targetRenderSurfaceChildren().size();
+ }
+ }
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::FrontToBack::begin(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = 0;
+ it.m_currentLayerIndex = it.targetRenderSurfaceChildren().size() - 1;
+ goToHighestInSubtree(it);
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::FrontToBack::end(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = CCLayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::FrontToBack::next(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ // Moves to the previous layer in the current RS layer list. Then we check if the
+ // new current layer has its own RS, in which case there are things in that RS layer list that are higher, so
+ // we find the highest layer in that subtree.
+ // If we move back past the front of the list, we jump up to the previous RS layer list, picking up again where we
+ // had previously recursed into the current RS layer list.
+
+ if (!it.currentLayerRepresentsTargetRenderSurface()) {
+ // Subtracting one here will eventually cause the current layer to become that layer
+ // representing the target render surface.
+ --it.m_currentLayerIndex;
+ goToHighestInSubtree(it);
+ } else {
+ while (it.currentLayerRepresentsTargetRenderSurface()) {
+ if (!it.m_targetRenderSurfaceLayerIndex) {
+ // End of the list
+ it.m_targetRenderSurfaceLayerIndex = CCLayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+ return;
+ }
+ it.m_targetRenderSurfaceLayerIndex = it.targetRenderSurface()->m_targetRenderSurfaceLayerIndexHistory;
+ it.m_currentLayerIndex = it.targetRenderSurface()->m_currentLayerIndexHistory;
+ }
+ }
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void CCLayerIteratorActions::FrontToBack::goToHighestInSubtree(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ if (it.currentLayerRepresentsTargetRenderSurface())
+ return;
+ while (it.currentLayerRepresentsContributingRenderSurface()) {
+ // Save where we were in the current target surface, move to the next one, and save the target surface that we
+ // came from there so we can go back to it.
+ it.targetRenderSurface()->m_currentLayerIndexHistory = it.m_currentLayerIndex;
+ int previousTargetRenderSurfaceLayer = it.m_targetRenderSurfaceLayerIndex;
+
+ for (LayerType* layer = it.currentLayer(); it.targetRenderSurfaceLayer() != layer; ++it.m_targetRenderSurfaceLayerIndex) { }
+ it.m_currentLayerIndex = it.targetRenderSurfaceChildren().size() - 1;
+
+ it.targetRenderSurface()->m_targetRenderSurfaceLayerIndexHistory = previousTargetRenderSurfaceLayer;
+ }
+}
+
+// Declare each of the above functions for LayerChromium and CCLayerImpl classes so that they are linked.
+template void CCLayerIteratorActions::BackToFront::begin(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, BackToFront> &);
+template void CCLayerIteratorActions::BackToFront::end(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, BackToFront>&);
+template void CCLayerIteratorActions::BackToFront::next(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, BackToFront>&);
+
+template void CCLayerIteratorActions::BackToFront::begin(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, BackToFront>&);
+template void CCLayerIteratorActions::BackToFront::end(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, BackToFront>&);
+template void CCLayerIteratorActions::BackToFront::next(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, BackToFront>&);
+
+template void CCLayerIteratorActions::FrontToBack::next(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::end(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::begin(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::goToHighestInSubtree(CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, FrontToBack>&);
+
+template void CCLayerIteratorActions::FrontToBack::next(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::end(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::begin(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, FrontToBack>&);
+template void CCLayerIteratorActions::FrontToBack::goToHighestInSubtree(CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, FrontToBack>&);
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCLayerIterator.h b/cc/CCLayerIterator.h
new file mode 100644
index 0000000..907182d
--- /dev/null
+++ b/cc/CCLayerIterator.h
@@ -0,0 +1,210 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerIterator_h
+#define CCLayerIterator_h
+
+#include "CCLayerTreeHostCommon.h"
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+// These classes provide means to iterate over the RenderSurface-Layer tree.
+
+// Example code follows, for a tree of LayerChromium/RenderSurfaceChromium objects. See below for details.
+//
+// void doStuffOnLayers(const Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList)
+// {
+// typedef CCLayerIterator<LayerChromium, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
+//
+// CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
+// for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
+// // Only one of these will be true
+// if (it.representsTargetRenderSurface())
+// foo(*it); // *it is a layer representing a target RenderSurface
+// if (it.representsContributingRenderSurface())
+// bar(*it); // *it is a layer representing a RenderSurface that contributes to the layer's target RenderSurface
+// if (it.representsItself())
+// baz(*it); // *it is a layer representing itself, as it contributes to its own target RenderSurface
+// }
+// }
+
+// A RenderSurface R may be referred to in one of two different contexts. One RenderSurface is "current" at any time, for
+// whatever operation is being performed. This current surface is referred to as a target surface. For example, when R is
+// being painted it would be the target surface. Once R has been painted, its contents may be included into another
+// surface S. While S is considered the target surface when it is being painted, R is called a contributing surface
+// in this context as it contributes to the content of the target surface S.
+//
+// The iterator's current position in the tree always points to some layer. The state of the iterator indicates the role of the
+// layer, and will be one of the following three states. A single layer L will appear in the iteration process in at least one,
+// and possibly all, of these states.
+// 1. Representing the target surface: The iterator in this state, pointing at layer L, indicates that the target RenderSurface
+// is now the surface owned by L. This will occur exactly once for each RenderSurface in the tree.
+// 2. Representing a contributing surface: The iterator in this state, pointing at layer L, refers to the RenderSurface owned
+// by L as a contributing surface, without changing the current target RenderSurface.
+// 3. Representing itself: The iterator in this state, pointing at layer L, refers to the layer itself, as a child of the
+// current target RenderSurface.
+//
+// The BackToFront iterator will return a layer representing the target surface before returning layers representing themselves
+// as children of the current target surface. Whereas the FrontToBack ordering will iterate over children layers of a surface
+// before the layer representing the surface as a target surface.
+//
+// To use the iterators:
+//
+// Create a stepping iterator and end iterator by calling CCLayerIterator::begin() and CCLayerIterator::end() and passing in the
+// list of layers owning target RenderSurfaces. Step through the tree by incrementing the stepping iterator while it is != to
+// the end iterator. At each step the iterator knows what the layer is representing, and you can query the iterator to decide
+// what actions to perform with the layer given what it represents.
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Non-templated constants
+struct CCLayerIteratorValue {
+ static const int InvalidTargetRenderSurfaceLayerIndex = -1;
+ // This must be -1 since the iterator action code assumes that this value can be
+ // reached by subtracting one from the position of the first layer in the current
+ // target surface's child layer list, which is 0.
+ static const int LayerIndexRepresentingTargetRenderSurface = -1;
+};
+
+// The position of a layer iterator that is independent of its many template types.
+template <typename LayerType>
+struct CCLayerIteratorPosition {
+ bool representsTargetRenderSurface;
+ bool representsContributingRenderSurface;
+ bool representsItself;
+ LayerType* targetRenderSurfaceLayer;
+ LayerType* currentLayer;
+};
+
+// An iterator class for walking over layers in the RenderSurface-Layer tree.
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename IteratorActionType>
+class CCLayerIterator {
+ typedef CCLayerIterator<LayerType, LayerList, RenderSurfaceType, IteratorActionType> CCLayerIteratorType;
+
+public:
+ CCLayerIterator() : m_renderSurfaceLayerList(0) { }
+
+ static CCLayerIteratorType begin(const LayerList* renderSurfaceLayerList) { return CCLayerIteratorType(renderSurfaceLayerList, true); }
+ static CCLayerIteratorType end(const LayerList* renderSurfaceLayerList) { return CCLayerIteratorType(renderSurfaceLayerList, false); }
+
+ CCLayerIteratorType& operator++() { m_actions.next(*this); return *this; }
+ bool operator==(const CCLayerIterator& other) const
+ {
+ return m_targetRenderSurfaceLayerIndex == other.m_targetRenderSurfaceLayerIndex
+ && m_currentLayerIndex == other.m_currentLayerIndex;
+ }
+ bool operator!=(const CCLayerIteratorType& other) const { return !(*this == other); }
+
+ LayerType* operator->() const { return currentLayer(); }
+ LayerType* operator*() const { return currentLayer(); }
+
+ bool representsTargetRenderSurface() const { return currentLayerRepresentsTargetRenderSurface(); }
+ bool representsContributingRenderSurface() const { return !representsTargetRenderSurface() && currentLayerRepresentsContributingRenderSurface(); }
+ bool representsItself() const { return !representsTargetRenderSurface() && !representsContributingRenderSurface(); }
+
+ LayerType* targetRenderSurfaceLayer() const { return getRawPtr((*m_renderSurfaceLayerList)[m_targetRenderSurfaceLayerIndex]); }
+
+ operator const CCLayerIteratorPosition<LayerType>() const
+ {
+ CCLayerIteratorPosition<LayerType> position;
+ position.representsTargetRenderSurface = representsTargetRenderSurface();
+ position.representsContributingRenderSurface = representsContributingRenderSurface();
+ position.representsItself = representsItself();
+ position.targetRenderSurfaceLayer = targetRenderSurfaceLayer();
+ position.currentLayer = currentLayer();
+ return position;
+ }
+
+private:
+ CCLayerIterator(const LayerList* renderSurfaceLayerList, bool start)
+ : m_renderSurfaceLayerList(renderSurfaceLayerList)
+ , m_targetRenderSurfaceLayerIndex(0)
+ {
+ for (size_t i = 0; i < renderSurfaceLayerList->size(); ++i) {
+ if (!(*renderSurfaceLayerList)[i]->renderSurface()) {
+ ASSERT_NOT_REACHED();
+ m_actions.end(*this);
+ return;
+ }
+ }
+
+ if (start && !renderSurfaceLayerList->isEmpty())
+ m_actions.begin(*this);
+ else
+ m_actions.end(*this);
+ }
+
+ inline static LayerChromium* getRawPtr(const RefPtr<LayerChromium>& ptr) { return ptr.get(); }
+ inline static CCLayerImpl* getRawPtr(CCLayerImpl* ptr) { return ptr; }
+
+ inline LayerType* currentLayer() const { return currentLayerRepresentsTargetRenderSurface() ? targetRenderSurfaceLayer() : getRawPtr(targetRenderSurfaceChildren()[m_currentLayerIndex]); }
+
+ inline bool currentLayerRepresentsContributingRenderSurface() const { return CCLayerTreeHostCommon::renderSurfaceContributesToTarget<LayerType>(currentLayer(), targetRenderSurfaceLayer()->id()); }
+ inline bool currentLayerRepresentsTargetRenderSurface() const { return m_currentLayerIndex == CCLayerIteratorValue::LayerIndexRepresentingTargetRenderSurface; }
+
+ inline RenderSurfaceType* targetRenderSurface() const { return targetRenderSurfaceLayer()->renderSurface(); }
+ inline const LayerList& targetRenderSurfaceChildren() const { return targetRenderSurface()->layerList(); }
+
+ IteratorActionType m_actions;
+ const LayerList* m_renderSurfaceLayerList;
+
+ // The iterator's current position.
+
+ // A position in the renderSurfaceLayerList. This points to a layer which owns the current target surface.
+ // This is a value from 0 to n-1 (n = size of renderSurfaceLayerList = number of surfaces). A value outside of
+ // this range (for example, CCLayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex) is used to
+ // indicate a position outside the bounds of the tree.
+ int m_targetRenderSurfaceLayerIndex;
+ // A position in the list of layers that are children of the current target surface. When pointing to one of
+ // these layers, this is a value from 0 to n-1 (n = number of children). Since the iterator must also stop at
+ // the layers representing the target surface, this is done by setting the currentLayerIndex to a value of
+ // CCLayerIteratorValue::LayerRepresentingTargetRenderSurface.
+ int m_currentLayerIndex;
+
+ friend struct CCLayerIteratorActions;
+};
+
+// Orderings for iterating over the RenderSurface-Layer tree.
+struct CCLayerIteratorActions {
+ // Walks layers sorted by z-order from back to front.
+ class BackToFront {
+ public:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void begin(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void end(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void next(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ private:
+ int m_highestTargetRenderSurfaceLayer;
+ };
+
+ // Walks layers sorted by z-order from front to back
+ class FrontToBack {
+ public:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void begin(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void end(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void next(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ private:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void goToHighestInSubtree(CCLayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+ };
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCLayerIteratorTest.cpp b/cc/CCLayerIteratorTest.cpp
new file mode 100644
index 0000000..bc5b2f3
--- /dev/null
+++ b/cc/CCLayerIteratorTest.cpp
@@ -0,0 +1,255 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerIterator.h"
+
+#include "CCLayerTreeHostCommon.h"
+#include "LayerChromium.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+using ::testing::Mock;
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::AnyNumber;
+
+namespace {
+
+class TestLayerChromium : public LayerChromium {
+public:
+ static PassRefPtr<TestLayerChromium> create() { return adoptRef(new TestLayerChromium()); }
+
+ int m_countRepresentingTargetSurface;
+ int m_countRepresentingContributingSurface;
+ int m_countRepresentingItself;
+
+ virtual bool drawsContent() const OVERRIDE { return m_drawsContent; }
+ void setDrawsContent(bool drawsContent) { m_drawsContent = drawsContent; }
+
+private:
+ TestLayerChromium()
+ : LayerChromium()
+ , m_drawsContent(true)
+ {
+ setBounds(IntSize(100, 100));
+ setPosition(IntPoint::zero());
+ setAnchorPoint(IntPoint::zero());
+ }
+
+ bool m_drawsContent;
+};
+
+#define EXPECT_COUNT(layer, target, contrib, itself) \
+ EXPECT_EQ(target, layer->m_countRepresentingTargetSurface); \
+ EXPECT_EQ(contrib, layer->m_countRepresentingContributingSurface); \
+ EXPECT_EQ(itself, layer->m_countRepresentingItself);
+
+typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> FrontToBack;
+typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::BackToFront> BackToFront;
+
+void resetCounts(Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList)
+{
+ for (unsigned surfaceIndex = 0; surfaceIndex < renderSurfaceLayerList.size(); ++surfaceIndex) {
+ TestLayerChromium* renderSurfaceLayer = static_cast<TestLayerChromium*>(renderSurfaceLayerList[surfaceIndex].get());
+ RenderSurfaceChromium* renderSurface = renderSurfaceLayer->renderSurface();
+
+ renderSurfaceLayer->m_countRepresentingTargetSurface = -1;
+ renderSurfaceLayer->m_countRepresentingContributingSurface = -1;
+ renderSurfaceLayer->m_countRepresentingItself = -1;
+
+ for (unsigned layerIndex = 0; layerIndex < renderSurface->layerList().size(); ++layerIndex) {
+ TestLayerChromium* layer = static_cast<TestLayerChromium*>(renderSurface->layerList()[layerIndex].get());
+
+ layer->m_countRepresentingTargetSurface = -1;
+ layer->m_countRepresentingContributingSurface = -1;
+ layer->m_countRepresentingItself = -1;
+ }
+ }
+}
+
+void iterateFrontToBack(Vector<RefPtr<LayerChromium> >* renderSurfaceLayerList)
+{
+ resetCounts(*renderSurfaceLayerList);
+ int count = 0;
+ for (FrontToBack it = FrontToBack::begin(renderSurfaceLayerList); it != FrontToBack::end(renderSurfaceLayerList); ++it, ++count) {
+ TestLayerChromium* layer = static_cast<TestLayerChromium*>(*it);
+ if (it.representsTargetRenderSurface())
+ layer->m_countRepresentingTargetSurface = count;
+ if (it.representsContributingRenderSurface())
+ layer->m_countRepresentingContributingSurface = count;
+ if (it.representsItself())
+ layer->m_countRepresentingItself = count;
+ }
+}
+
+void iterateBackToFront(Vector<RefPtr<LayerChromium> >* renderSurfaceLayerList)
+{
+ resetCounts(*renderSurfaceLayerList);
+ int count = 0;
+ for (BackToFront it = BackToFront::begin(renderSurfaceLayerList); it != BackToFront::end(renderSurfaceLayerList); ++it, ++count) {
+ TestLayerChromium* layer = static_cast<TestLayerChromium*>(*it);
+ if (it.representsTargetRenderSurface())
+ layer->m_countRepresentingTargetSurface = count;
+ if (it.representsContributingRenderSurface())
+ layer->m_countRepresentingContributingSurface = count;
+ if (it.representsItself())
+ layer->m_countRepresentingItself = count;
+ }
+}
+
+TEST(CCLayerIteratorTest, emptyTree)
+{
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ iterateFrontToBack(&renderSurfaceLayerList);
+}
+
+TEST(CCLayerIteratorTest, simpleTree)
+{
+ RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> first = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> second = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> third = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> fourth = TestLayerChromium::create();
+
+ rootLayer->createRenderSurface();
+
+ rootLayer->addChild(first);
+ rootLayer->addChild(second);
+ rootLayer->addChild(third);
+ rootLayer->addChild(fourth);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer->bounds(), 1, 256, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(first, -1, -1, 2);
+ EXPECT_COUNT(second, -1, -1, 3);
+ EXPECT_COUNT(third, -1, -1, 4);
+ EXPECT_COUNT(fourth, -1, -1, 5);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 5, -1, 4);
+ EXPECT_COUNT(first, -1, -1, 3);
+ EXPECT_COUNT(second, -1, -1, 2);
+ EXPECT_COUNT(third, -1, -1, 1);
+ EXPECT_COUNT(fourth, -1, -1, 0);
+
+}
+
+TEST(CCLayerIteratorTest, complexTree)
+{
+ RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root1 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root2 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root3 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root21 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root22 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root23 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root221 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root231 = TestLayerChromium::create();
+
+ rootLayer->createRenderSurface();
+
+ rootLayer->addChild(root1);
+ rootLayer->addChild(root2);
+ rootLayer->addChild(root3);
+ root2->addChild(root21);
+ root2->addChild(root22);
+ root2->addChild(root23);
+ root22->addChild(root221);
+ root23->addChild(root231);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer->bounds(), 1, 256, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(root1, -1, -1, 2);
+ EXPECT_COUNT(root2, -1, -1, 3);
+ EXPECT_COUNT(root21, -1, -1, 4);
+ EXPECT_COUNT(root22, -1, -1, 5);
+ EXPECT_COUNT(root221, -1, -1, 6);
+ EXPECT_COUNT(root23, -1, -1, 7);
+ EXPECT_COUNT(root231, -1, -1, 8);
+ EXPECT_COUNT(root3, -1, -1, 9);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 9, -1, 8);
+ EXPECT_COUNT(root1, -1, -1, 7);
+ EXPECT_COUNT(root2, -1, -1, 6);
+ EXPECT_COUNT(root21, -1, -1, 5);
+ EXPECT_COUNT(root22, -1, -1, 4);
+ EXPECT_COUNT(root221, -1, -1, 3);
+ EXPECT_COUNT(root23, -1, -1, 2);
+ EXPECT_COUNT(root231, -1, -1, 1);
+ EXPECT_COUNT(root3, -1, -1, 0);
+
+}
+
+TEST(CCLayerIteratorTest, complexTreeMultiSurface)
+{
+ RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root1 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root2 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root3 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root21 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root22 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root23 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root221 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> root231 = TestLayerChromium::create();
+
+ rootLayer->createRenderSurface();
+ rootLayer->renderSurface()->setContentRect(IntRect(IntPoint(), rootLayer->bounds()));
+
+ rootLayer->addChild(root1);
+ rootLayer->addChild(root2);
+ rootLayer->addChild(root3);
+ root2->setDrawsContent(false);
+ root2->setOpacity(0.5); // Force the layer to own a new surface.
+ root2->addChild(root21);
+ root2->addChild(root22);
+ root2->addChild(root23);
+ root22->setOpacity(0.5);
+ root22->addChild(root221);
+ root23->setOpacity(0.5);
+ root23->addChild(root231);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer->bounds(), 1, 256, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(root1, -1, -1, 2);
+ EXPECT_COUNT(root2, 4, 3, -1);
+ EXPECT_COUNT(root21, -1, -1, 5);
+ EXPECT_COUNT(root22, 7, 6, 8);
+ EXPECT_COUNT(root221, -1, -1, 9);
+ EXPECT_COUNT(root23, 11, 10, 12);
+ EXPECT_COUNT(root231, -1, -1, 13);
+ EXPECT_COUNT(root3, -1, -1, 14);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 14, -1, 13);
+ EXPECT_COUNT(root1, -1, -1, 12);
+ EXPECT_COUNT(root2, 10, 11, -1);
+ EXPECT_COUNT(root21, -1, -1, 9);
+ EXPECT_COUNT(root22, 7, 8, 6);
+ EXPECT_COUNT(root221, -1, -1, 5);
+ EXPECT_COUNT(root23, 3, 4, 2);
+ EXPECT_COUNT(root231, -1, -1, 1);
+ EXPECT_COUNT(root3, -1, -1, 0);
+}
+
+} // namespace
diff --git a/cc/CCLayerQuad.cpp b/cc/CCLayerQuad.cpp
new file mode 100644
index 0000000..c6dfcc4
--- /dev/null
+++ b/cc/CCLayerQuad.cpp
@@ -0,0 +1,66 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerQuad.h"
+
+namespace WebCore {
+
+CCLayerQuad::Edge::Edge(const FloatPoint& p, const FloatPoint& q)
+{
+ ASSERT(p != q);
+
+ FloatPoint tangent(p.y() - q.y(), q.x() - p.x());
+ float cross2 = p.x() * q.y() - q.x() * p.y();
+
+ set(tangent.x(), tangent.y(), cross2);
+ scale(1.0f / tangent.length());
+}
+
+CCLayerQuad::CCLayerQuad(const FloatQuad& quad)
+{
+ // Create edges.
+ m_left = Edge(quad.p4(), quad.p1());
+ m_right = Edge(quad.p2(), quad.p3());
+ m_top = Edge(quad.p1(), quad.p2());
+ m_bottom = Edge(quad.p3(), quad.p4());
+
+ float sign = quad.isCounterclockwise() ? -1 : 1;
+ m_left.scale(sign);
+ m_right.scale(sign);
+ m_top.scale(sign);
+ m_bottom.scale(sign);
+}
+
+FloatQuad CCLayerQuad::floatQuad() const
+{
+ return FloatQuad(m_left.intersect(m_top),
+ m_top.intersect(m_right),
+ m_right.intersect(m_bottom),
+ m_bottom.intersect(m_left));
+}
+
+void CCLayerQuad::toFloatArray(float flattened[12]) const
+{
+ flattened[0] = m_left.x();
+ flattened[1] = m_left.y();
+ flattened[2] = m_left.z();
+ flattened[3] = m_top.x();
+ flattened[4] = m_top.y();
+ flattened[5] = m_top.z();
+ flattened[6] = m_right.x();
+ flattened[7] = m_right.y();
+ flattened[8] = m_right.z();
+ flattened[9] = m_bottom.x();
+ flattened[10] = m_bottom.y();
+ flattened[11] = m_bottom.z();
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCLayerQuad.h b/cc/CCLayerQuad.h
new file mode 100644
index 0000000..98688fb
--- /dev/null
+++ b/cc/CCLayerQuad.h
@@ -0,0 +1,112 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCLayerQuad_h
+#define CCLayerQuad_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatPoint3D.h"
+#include "FloatQuad.h"
+
+static const float kAntiAliasingInflateDistance = 0.5f;
+
+namespace WebCore {
+
+class CCLayerQuad {
+public:
+ class Edge {
+ public:
+ Edge()
+ : m_x(0)
+ , m_y(0)
+ , m_z(0)
+ {
+ }
+ Edge(const FloatPoint&, const FloatPoint&);
+
+ float x() const { return m_x; }
+ float y() const { return m_y; }
+ float z() const { return m_z; }
+
+ void setX(float x) { m_x = x; }
+ void setY(float y) { m_y = y; }
+ void setZ(float z) { m_z = z; }
+ void set(float x, float y, float z)
+ {
+ m_x = x;
+ m_y = y;
+ m_z = z;
+ }
+
+ void moveX(float dx) { m_x += dx; }
+ void moveY(float dy) { m_y += dy; }
+ void moveZ(float dz) { m_z += dz; }
+ void move(float dx, float dy, float dz)
+ {
+ m_x += dx;
+ m_y += dy;
+ m_z += dz;
+ }
+
+ void scaleX(float sx) { m_x *= sx; }
+ void scaleY(float sy) { m_y *= sy; }
+ void scaleZ(float sz) { m_z *= sz; }
+ void scale(float sx, float sy, float sz)
+ {
+ m_x *= sx;
+ m_y *= sy;
+ m_z *= sz;
+ }
+ void scale(float s) { scale(s, s, s); }
+
+ FloatPoint intersect(const Edge& e) const
+ {
+ return FloatPoint(
+ (y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()),
+ (x() * e.z() - e.x() * z()) / (e.x() * y() - x() * e.y()));
+ }
+
+ private:
+ float m_x;
+ float m_y;
+ float m_z;
+ };
+
+ CCLayerQuad(const Edge& left, const Edge& top, const Edge& right, const Edge& bottom)
+ : m_left(left)
+ , m_top(top)
+ , m_right(right)
+ , m_bottom(bottom)
+ {
+ }
+ CCLayerQuad(const FloatQuad&);
+
+ Edge left() const { return m_left; }
+ Edge top() const { return m_top; }
+ Edge right() const { return m_right; }
+ Edge bottom() const { return m_bottom; }
+
+ void inflateX(float dx) { m_left.moveZ(dx); m_right.moveZ(dx); }
+ void inflateY(float dy) { m_top.moveZ(dy); m_bottom.moveZ(dy); }
+ void inflate(float d) { inflateX(d); inflateY(d); }
+ void inflateAntiAliasingDistance() { inflate(kAntiAliasingInflateDistance); }
+
+ FloatQuad floatQuad() const;
+
+ void toFloatArray(float[12]) const;
+
+private:
+ Edge m_left;
+ Edge m_top;
+ Edge m_right;
+ Edge m_bottom;
+};
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCLayerQuadTest.cpp b/cc/CCLayerQuadTest.cpp
new file mode 100644
index 0000000..eb4a1f0
--- /dev/null
+++ b/cc/CCLayerQuadTest.cpp
@@ -0,0 +1,45 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerQuad.h"
+
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+
+namespace {
+
+TEST(CCLayerQuadTest, FloatQuadConversion)
+{
+ FloatPoint p1(-0.5, -0.5);
+ FloatPoint p2( 0.5, -0.5);
+ FloatPoint p3( 0.5, 0.5);
+ FloatPoint p4(-0.5, 0.5);
+
+ FloatQuad quadCW(p1, p2, p3, p4);
+ CCLayerQuad layerQuadCW(quadCW);
+ EXPECT_TRUE(layerQuadCW.floatQuad() == quadCW);
+
+ FloatQuad quadCCW(p1, p4, p3, p2);
+ CCLayerQuad layerQuadCCW(quadCCW);
+ EXPECT_TRUE(layerQuadCCW.floatQuad() == quadCCW);
+}
+
+TEST(CCLayerQuadTest, Inflate)
+{
+ FloatPoint p1(-0.5, -0.5);
+ FloatPoint p2( 0.5, -0.5);
+ FloatPoint p3( 0.5, 0.5);
+ FloatPoint p4(-0.5, 0.5);
+
+ FloatQuad quad(p1, p2, p3, p4);
+ CCLayerQuad layerQuad(quad);
+ quad.scale(2, 2);
+ layerQuad.inflate(0.5);
+ EXPECT_TRUE(layerQuad.floatQuad() == quad);
+}
+
+} // namespace
diff --git a/cc/CCLayerSorter.cpp b/cc/CCLayerSorter.cpp
new file mode 100644
index 0000000..bf2818b
--- /dev/null
+++ b/cc/CCLayerSorter.cpp
@@ -0,0 +1,422 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerSorter.h"
+
+#include "CCMathUtil.h"
+#include "CCRenderSurface.h"
+#include <limits.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/Deque.h>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+#define LOG_CHANNEL_PREFIX Log
+#define SHOW_DEBUG_LOG 0
+
+#if !defined( NDEBUG )
+#if SHOW_DEBUG_LOG
+static WTFLogChannel LogCCLayerSorter = { 0x00000000, "", WTFLogChannelOn };
+#else
+static WTFLogChannel LogCCLayerSorter = { 0x00000000, "", WTFLogChannelOff };
+#endif
+#endif
+
+namespace WebCore {
+
+inline static float perpProduct(const FloatSize& u, const FloatSize& v)
+{
+ return u.width() * v.height() - u.height() * v.width();
+}
+
+// Tests if two edges defined by their endpoints (a,b) and (c,d) intersect. Returns true and the
+// point of intersection if they do and false otherwise.
+static bool edgeEdgeTest(const FloatPoint& a, const FloatPoint& b, const FloatPoint& c, const FloatPoint& d, FloatPoint& r)
+{
+ FloatSize u = b - a;
+ FloatSize v = d - c;
+ FloatSize w = a - c;
+
+ float denom = perpProduct(u, v);
+
+ // If denom == 0 then the edges are parallel. While they could be overlapping
+ // we don't bother to check here as the we'll find their intersections from the
+ // corner to quad tests.
+ if (!denom)
+ return false;
+
+ float s = perpProduct(v, w) / denom;
+ if (s < 0 || s > 1)
+ return false;
+
+ float t = perpProduct(u, w) / denom;
+ if (t < 0 || t > 1)
+ return false;
+
+ u.scale(s);
+ r = a + u;
+ return true;
+}
+
+// Checks whether layer "a" draws on top of layer "b". The weight value returned is an indication of
+// the maximum z-depth difference between the layers or zero if the layers are found to be intesecting
+// (some features are in front and some are behind).
+CCLayerSorter::ABCompareResult CCLayerSorter::checkOverlap(LayerShape* a, LayerShape* b, float zThreshold, float& weight)
+{
+ weight = 0;
+
+ // Early out if the projected bounds don't overlap.
+ if (!a->projectedBounds.intersects(b->projectedBounds))
+ return None;
+
+ FloatPoint aPoints[4] = {a->projectedQuad.p1(), a->projectedQuad.p2(), a->projectedQuad.p3(), a->projectedQuad.p4() };
+ FloatPoint bPoints[4] = {b->projectedQuad.p1(), b->projectedQuad.p2(), b->projectedQuad.p3(), b->projectedQuad.p4() };
+
+ // Make a list of points that inside both layer quad projections.
+ Vector<FloatPoint> overlapPoints;
+
+ // Check all four corners of one layer against the other layer's quad.
+ for (int i = 0; i < 4; ++i) {
+ if (a->projectedQuad.containsPoint(bPoints[i]))
+ overlapPoints.append(bPoints[i]);
+ if (b->projectedQuad.containsPoint(aPoints[i]))
+ overlapPoints.append(aPoints[i]);
+ }
+
+ // Check all the edges of one layer for intersection with the other layer's edges.
+ FloatPoint r;
+ for (int ea = 0; ea < 4; ++ea)
+ for (int eb = 0; eb < 4; ++eb)
+ if (edgeEdgeTest(aPoints[ea], aPoints[(ea + 1) % 4],
+ bPoints[eb], bPoints[(eb + 1) % 4],
+ r))
+ overlapPoints.append(r);
+
+ if (!overlapPoints.size())
+ return None;
+
+ // Check the corresponding layer depth value for all overlap points to determine
+ // which layer is in front.
+ float maxPositive = 0;
+ float maxNegative = 0;
+ for (unsigned o = 0; o < overlapPoints.size(); o++) {
+ float za = a->layerZFromProjectedPoint(overlapPoints[o]);
+ float zb = b->layerZFromProjectedPoint(overlapPoints[o]);
+
+ float diff = za - zb;
+ if (diff > maxPositive)
+ maxPositive = diff;
+ if (diff < maxNegative)
+ maxNegative = diff;
+ }
+
+ float maxDiff = (fabsf(maxPositive) > fabsf(maxNegative) ? maxPositive : maxNegative);
+
+ // If the results are inconsistent (and the z difference substantial to rule out
+ // numerical errors) then the layers are intersecting. We will still return an
+ // order based on the maximum depth difference but with an edge weight of zero
+ // these layers will get priority if a graph cycle is present and needs to be broken.
+ if (maxPositive > zThreshold && maxNegative < -zThreshold)
+ weight = 0;
+ else
+ weight = fabsf(maxDiff);
+
+ // Maintain relative order if the layers have the same depth at all intersection points.
+ if (maxDiff <= 0)
+ return ABeforeB;
+
+ return BBeforeA;
+}
+
+CCLayerSorter::LayerShape::LayerShape(float width, float height, const WebTransformationMatrix& drawTransform)
+{
+ FloatQuad layerQuad(FloatRect(0, 0, width, height));
+
+ // Compute the projection of the layer quad onto the z = 0 plane.
+
+ FloatPoint clippedQuad[8];
+ int numVerticesInClippedQuad;
+ CCMathUtil::mapClippedQuad(drawTransform, layerQuad, clippedQuad, numVerticesInClippedQuad);
+
+ if (numVerticesInClippedQuad < 3) {
+ projectedBounds = FloatRect();
+ return;
+ }
+
+ projectedBounds = CCMathUtil::computeEnclosingRectOfVertices(clippedQuad, numVerticesInClippedQuad);
+
+ // NOTE: it will require very significant refactoring and overhead to deal with
+ // generalized polygons or multiple quads per layer here. For the sake of layer
+ // sorting it is equally correct to take a subsection of the polygon that can be made
+ // into a quad. This will only be incorrect in the case of intersecting layers, which
+ // are not supported yet anyway.
+ projectedQuad.setP1(clippedQuad[0]);
+ projectedQuad.setP2(clippedQuad[1]);
+ projectedQuad.setP3(clippedQuad[2]);
+ if (numVerticesInClippedQuad >= 4)
+ projectedQuad.setP4(clippedQuad[3]);
+ else
+ projectedQuad.setP4(clippedQuad[2]); // this will be a degenerate quad that is actually a triangle.
+
+ // Compute the normal of the layer's plane.
+ bool clipped = false;
+ FloatPoint3D c1 = CCMathUtil::mapPoint(drawTransform, FloatPoint3D(0, 0, 0), clipped);
+ FloatPoint3D c2 = CCMathUtil::mapPoint(drawTransform, FloatPoint3D(0, 1, 0), clipped);
+ FloatPoint3D c3 = CCMathUtil::mapPoint(drawTransform, FloatPoint3D(1, 0, 0), clipped);
+ // FIXME: Deal with clipping.
+ FloatPoint3D c12 = c2 - c1;
+ FloatPoint3D c13 = c3 - c1;
+ layerNormal = c13.cross(c12);
+
+ transformOrigin = c1;
+}
+
+// Returns the Z coordinate of a point on the layer that projects
+// to point p which lies on the z = 0 plane. It does it by computing the
+// intersection of a line starting from p along the Z axis and the plane
+// of the layer.
+float CCLayerSorter::LayerShape::layerZFromProjectedPoint(const FloatPoint& p) const
+{
+ const FloatPoint3D zAxis(0, 0, 1);
+ FloatPoint3D w = FloatPoint3D(p) - transformOrigin;
+
+ float d = layerNormal.dot(zAxis);
+ float n = -layerNormal.dot(w);
+
+ // Check if layer is parallel to the z = 0 axis which will make it
+ // invisible and hence returning zero is fine.
+ if (!d)
+ return 0;
+
+ // The intersection point would be given by:
+ // p + (n / d) * u but since we are only interested in the
+ // z coordinate and p's z coord is zero, all we need is the value of n/d.
+ return n / d;
+}
+
+void CCLayerSorter::createGraphNodes(LayerList::iterator first, LayerList::iterator last)
+{
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Creating graph nodes:\n");
+#endif
+ float minZ = FLT_MAX;
+ float maxZ = -FLT_MAX;
+ for (LayerList::const_iterator it = first; it < last; it++) {
+ m_nodes.append(GraphNode(*it));
+ GraphNode& node = m_nodes.at(m_nodes.size() - 1);
+ CCRenderSurface* renderSurface = node.layer->renderSurface();
+ if (!node.layer->drawsContent() && !renderSurface)
+ continue;
+
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Layer %d (%d x %d)\n", node.layer->id(), node.layer->bounds().width(), node.layer->bounds().height());
+#endif
+
+ WebTransformationMatrix drawTransform;
+ float layerWidth, layerHeight;
+ if (renderSurface) {
+ drawTransform = renderSurface->drawTransform();
+ layerWidth = renderSurface->contentRect().width();
+ layerHeight = renderSurface->contentRect().height();
+ } else {
+ drawTransform = node.layer->drawTransform();
+ layerWidth = node.layer->contentBounds().width();
+ layerHeight = node.layer->contentBounds().height();
+ }
+
+ node.shape = LayerShape(layerWidth, layerHeight, drawTransform);
+
+ maxZ = max(maxZ, node.shape.transformOrigin.z());
+ minZ = min(minZ, node.shape.transformOrigin.z());
+ }
+
+ m_zRange = fabsf(maxZ - minZ);
+}
+
+void CCLayerSorter::createGraphEdges()
+{
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Edges:\n");
+#endif
+ // Fraction of the total zRange below which z differences
+ // are not considered reliable.
+ const float zThresholdFactor = 0.01;
+ float zThreshold = m_zRange * zThresholdFactor;
+
+ for (unsigned na = 0; na < m_nodes.size(); na++) {
+ GraphNode& nodeA = m_nodes[na];
+ if (!nodeA.layer->drawsContent() && !nodeA.layer->renderSurface())
+ continue;
+ for (unsigned nb = na + 1; nb < m_nodes.size(); nb++) {
+ GraphNode& nodeB = m_nodes[nb];
+ if (!nodeB.layer->drawsContent() && !nodeB.layer->renderSurface())
+ continue;
+ float weight = 0;
+ ABCompareResult overlapResult = checkOverlap(&nodeA.shape, &nodeB.shape, zThreshold, weight);
+ GraphNode* startNode = 0;
+ GraphNode* endNode = 0;
+ if (overlapResult == ABeforeB) {
+ startNode = &nodeA;
+ endNode = &nodeB;
+ } else if (overlapResult == BBeforeA) {
+ startNode = &nodeB;
+ endNode = &nodeA;
+ }
+
+ if (startNode) {
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "%d -> %d\n", startNode->layer->id(), endNode->layer->id());
+#endif
+ m_edges.append(GraphEdge(startNode, endNode, weight));
+ }
+ }
+ }
+
+ for (unsigned i = 0; i < m_edges.size(); i++) {
+ GraphEdge& edge = m_edges[i];
+ m_activeEdges.add(&edge, &edge);
+ edge.from->outgoing.append(&edge);
+ edge.to->incoming.append(&edge);
+ edge.to->incomingEdgeWeight += edge.weight;
+ }
+}
+
+// Finds and removes an edge from the list by doing a swap with the
+// last element of the list.
+void CCLayerSorter::removeEdgeFromList(GraphEdge* edge, Vector<GraphEdge*>& list)
+{
+ size_t edgeIndex = list.find(edge);
+ ASSERT(edgeIndex != notFound);
+ if (list.size() == 1) {
+ ASSERT(!edgeIndex);
+ list.clear();
+ return;
+ }
+ if (edgeIndex != list.size() - 1)
+ list[edgeIndex] = list[list.size() - 1];
+
+ list.removeLast();
+}
+
+// Sorts the given list of layers such that they can be painted in a back-to-front
+// order. Sorting produces correct results for non-intersecting layers that don't have
+// cyclical order dependencies. Cycles and intersections are broken (somewhat) aribtrarily.
+// Sorting of layers is done via a topological sort of a directed graph whose nodes are
+// the layers themselves. An edge from node A to node B signifies that layer A needs to
+// be drawn before layer B. If A and B have no dependency between each other, then we
+// preserve the ordering of those layers as they were in the original list.
+//
+// The draw order between two layers is determined by projecting the two triangles making
+// up each layer quad to the Z = 0 plane, finding points of intersection between the triangles
+// and backprojecting those points to the plane of the layer to determine the corresponding Z
+// coordinate. The layer with the lower Z coordinate (farther from the eye) needs to be rendered
+// first.
+//
+// If the layer projections don't intersect, then no edges (dependencies) are created
+// between them in the graph. HOWEVER, in this case we still need to preserve the ordering
+// of the original list of layers, since that list should already have proper z-index
+// ordering of layers.
+//
+void CCLayerSorter::sort(LayerList::iterator first, LayerList::iterator last)
+{
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Sorting start ----\n");
+#endif
+ createGraphNodes(first, last);
+
+ createGraphEdges();
+
+ Vector<GraphNode*> sortedList;
+ Deque<GraphNode*> noIncomingEdgeNodeList;
+
+ // Find all the nodes that don't have incoming edges.
+ for (NodeList::iterator la = m_nodes.begin(); la < m_nodes.end(); la++) {
+ if (!la->incoming.size())
+ noIncomingEdgeNodeList.append(la);
+ }
+
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Sorted list: ");
+#endif
+ while (m_activeEdges.size() || noIncomingEdgeNodeList.size()) {
+ while (noIncomingEdgeNodeList.size()) {
+
+ // It is necessary to preserve the existing ordering of layers, when there are
+ // no explicit dependencies (because this existing ordering has correct
+ // z-index/layout ordering). To preserve this ordering, we process Nodes in
+ // the same order that they were added to the list.
+ GraphNode* fromNode = noIncomingEdgeNodeList.takeFirst();
+
+ // Add it to the final list.
+ sortedList.append(fromNode);
+
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "%d, ", fromNode->layer->id());
+#endif
+
+ // Remove all its outgoing edges from the graph.
+ for (unsigned i = 0; i < fromNode->outgoing.size(); i++) {
+ GraphEdge* outgoingEdge = fromNode->outgoing[i];
+
+ m_activeEdges.remove(outgoingEdge);
+ removeEdgeFromList(outgoingEdge, outgoingEdge->to->incoming);
+ outgoingEdge->to->incomingEdgeWeight -= outgoingEdge->weight;
+
+ if (!outgoingEdge->to->incoming.size())
+ noIncomingEdgeNodeList.append(outgoingEdge->to);
+ }
+ fromNode->outgoing.clear();
+ }
+
+ if (!m_activeEdges.size())
+ break;
+
+ // If there are still active edges but the list of nodes without incoming edges
+ // is empty then we have run into a cycle. Break the cycle by finding the node
+ // with the smallest overall incoming edge weight and use it. This will favor
+ // nodes that have zero-weight incoming edges i.e. layers that are being
+ // occluded by a layer that intersects them.
+ float minIncomingEdgeWeight = FLT_MAX;
+ GraphNode* nextNode = 0;
+ for (unsigned i = 0; i < m_nodes.size(); i++) {
+ if (m_nodes[i].incoming.size() && m_nodes[i].incomingEdgeWeight < minIncomingEdgeWeight) {
+ minIncomingEdgeWeight = m_nodes[i].incomingEdgeWeight;
+ nextNode = &m_nodes[i];
+ }
+ }
+ ASSERT(nextNode);
+ // Remove all its incoming edges.
+ for (unsigned e = 0; e < nextNode->incoming.size(); e++) {
+ GraphEdge* incomingEdge = nextNode->incoming[e];
+
+ m_activeEdges.remove(incomingEdge);
+ removeEdgeFromList(incomingEdge, incomingEdge->from->outgoing);
+ }
+ nextNode->incoming.clear();
+ nextNode->incomingEdgeWeight = 0;
+ noIncomingEdgeNodeList.append(nextNode);
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Breaking cycle by cleaning up incoming edges from %d (weight = %f)\n", nextNode->layer->id(), minIncomingEdgeWeight);
+#endif
+ }
+
+ // Note: The original elements of the list are in no danger of having their ref count go to zero
+ // here as they are all nodes of the layer hierarchy and are kept alive by their parent nodes.
+ int count = 0;
+ for (LayerList::iterator it = first; it < last; it++)
+ *it = sortedList[count++]->layer;
+
+#if !defined( NDEBUG )
+ LOG(CCLayerSorter, "Sorting end ----\n");
+#endif
+
+ m_nodes.clear();
+ m_edges.clear();
+ m_activeEdges.clear();
+}
+
+}
diff --git a/cc/CCLayerSorter.h b/cc/CCLayerSorter.h
new file mode 100644
index 0000000..0fb64a6
--- /dev/null
+++ b/cc/CCLayerSorter.h
@@ -0,0 +1,88 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerSorter_h
+#define CCLayerSorter_h
+
+#include "CCLayerImpl.h"
+#include "FloatPoint3D.h"
+#include "FloatQuad.h"
+#include "FloatRect.h"
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+
+namespace WebKit {
+class WebTransformationMatrix;
+}
+
+namespace WebCore {
+
+class CCLayerSorter {
+ WTF_MAKE_NONCOPYABLE(CCLayerSorter);
+public:
+ CCLayerSorter() : m_zRange(0) { }
+
+ typedef Vector<CCLayerImpl*> LayerList;
+
+ void sort(LayerList::iterator first, LayerList::iterator last);
+
+ // Holds various useful properties derived from a layer's 3D outline.
+ struct LayerShape {
+ LayerShape() { }
+ LayerShape(float width, float height, const WebKit::WebTransformationMatrix& drawTransform);
+
+ float layerZFromProjectedPoint(const FloatPoint&) const;
+
+ FloatPoint3D layerNormal;
+ FloatPoint3D transformOrigin;
+ FloatQuad projectedQuad;
+ FloatRect projectedBounds;
+ };
+
+ enum ABCompareResult {
+ ABeforeB,
+ BBeforeA,
+ None
+ };
+
+ static ABCompareResult checkOverlap(LayerShape*, LayerShape*, float zThreshold, float& weight);
+
+private:
+ struct GraphEdge;
+
+ struct GraphNode {
+ explicit GraphNode(CCLayerImpl* cclayer) : layer(cclayer), incomingEdgeWeight(0) { }
+
+ CCLayerImpl* layer;
+ LayerShape shape;
+ Vector<GraphEdge*> incoming;
+ Vector<GraphEdge*> outgoing;
+ float incomingEdgeWeight;
+ };
+
+ struct GraphEdge {
+ GraphEdge(GraphNode* fromNode, GraphNode* toNode, float weight) : from(fromNode), to(toNode), weight(weight) { };
+
+ GraphNode* from;
+ GraphNode* to;
+ float weight;
+ };
+
+ typedef Vector<GraphNode> NodeList;
+ typedef Vector<GraphEdge> EdgeList;
+ NodeList m_nodes;
+ EdgeList m_edges;
+ float m_zRange;
+
+ typedef HashMap<GraphEdge*, GraphEdge*> EdgeMap;
+ EdgeMap m_activeEdges;
+
+ void createGraphNodes(LayerList::iterator first, LayerList::iterator last);
+ void createGraphEdges();
+ void removeEdgeFromList(GraphEdge*, Vector<GraphEdge*>&);
+};
+
+}
+#endif
diff --git a/cc/CCLayerSorterTest.cpp b/cc/CCLayerSorterTest.cpp
new file mode 100644
index 0000000..672f652
--- /dev/null
+++ b/cc/CCLayerSorterTest.cpp
@@ -0,0 +1,267 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerSorter.h"
+
+#include "CCLayerImpl.h"
+#include "CCMathUtil.h"
+#include "CCSingleThreadProxy.h"
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+// Note: In the following overlap tests, the "camera" is looking down the negative Z axis,
+// meaning that layers with smaller z values (more negative) are further from the camera
+// and therefore must be drawn before layers with higher z values.
+
+TEST(CCLayerSorterTest, BasicOverlap)
+{
+ CCLayerSorter::ABCompareResult overlapResult;
+ const float zThreshold = 0.1f;
+ float weight = 0;
+
+ // Trivial test, with one layer directly obscuring the other.
+ WebTransformationMatrix neg4Translate;
+ neg4Translate.translate3d(0, 0, -4);
+ CCLayerSorter::LayerShape front(2, 2, neg4Translate);
+
+ WebTransformationMatrix neg5Translate;
+ neg5Translate.translate3d(0, 0, -5);
+ CCLayerSorter::LayerShape back(2, 2, neg5Translate);
+
+ overlapResult = CCLayerSorter::checkOverlap(&front, &back, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult);
+ EXPECT_EQ(1, weight);
+
+ overlapResult = CCLayerSorter::checkOverlap(&back, &front, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+ EXPECT_EQ(1, weight);
+
+ // One layer translated off to the right. No overlap should be detected.
+ WebTransformationMatrix rightTranslate;
+ rightTranslate.translate3d(10, 0, -5);
+ CCLayerSorter::LayerShape backRight(2, 2, rightTranslate);
+ overlapResult = CCLayerSorter::checkOverlap(&front, &backRight, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::None, overlapResult);
+
+ // When comparing a layer with itself, z difference is always 0.
+ overlapResult = CCLayerSorter::checkOverlap(&front, &front, zThreshold, weight);
+ EXPECT_EQ(0, weight);
+}
+
+TEST(CCLayerSorterTest, RightAngleOverlap)
+{
+ CCLayerSorter::ABCompareResult overlapResult;
+ const float zThreshold = 0.1f;
+ float weight = 0;
+
+ WebTransformationMatrix perspectiveMatrix;
+ perspectiveMatrix.applyPerspective(1000);
+
+ // Two layers forming a right angle with a perspective viewing transform.
+ WebTransformationMatrix leftFaceMatrix;
+ leftFaceMatrix.rotate3d(0, 1, 0, -90);
+ leftFaceMatrix.translateRight3d(-1, 0, -5);
+ leftFaceMatrix.translate(-1, -1);
+ CCLayerSorter::LayerShape leftFace(2, 2, perspectiveMatrix * leftFaceMatrix);
+ WebTransformationMatrix frontFaceMatrix;
+ frontFaceMatrix.translate3d(0, 0, -4);
+ frontFaceMatrix.translate(-1, -1);
+ CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix);
+
+ overlapResult = CCLayerSorter::checkOverlap(&frontFace, &leftFace, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult);
+}
+
+TEST(CCLayerSorterTest, IntersectingLayerOverlap)
+{
+ CCLayerSorter::ABCompareResult overlapResult;
+ const float zThreshold = 0.1f;
+ float weight = 0;
+
+ WebTransformationMatrix perspectiveMatrix;
+ perspectiveMatrix.applyPerspective(1000);
+
+ // Intersecting layers. An explicit order will be returned based on relative z
+ // values at the overlapping features but the weight returned should be zero.
+ WebTransformationMatrix frontFaceMatrix;
+ frontFaceMatrix.translate3d(0, 0, -4);
+ frontFaceMatrix.translate(-1, -1);
+ CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix);
+
+ WebTransformationMatrix throughMatrix;
+ throughMatrix.rotate3d(0, 1, 0, 45);
+ throughMatrix.translateRight3d(0, 0, -4);
+ throughMatrix.translate(-1, -1);
+ CCLayerSorter::LayerShape rotatedFace(2, 2, perspectiveMatrix * throughMatrix);
+ overlapResult = CCLayerSorter::checkOverlap(&frontFace, &rotatedFace, zThreshold, weight);
+ EXPECT_NE(CCLayerSorter::None, overlapResult);
+ EXPECT_EQ(0, weight);
+}
+
+TEST(CCLayerSorterTest, LayersAtAngleOverlap)
+{
+ CCLayerSorter::ABCompareResult overlapResult;
+ const float zThreshold = 0.1f;
+ float weight = 0;
+
+ // Trickier test with layers at an angle.
+ //
+ // -x . . . . 0 . . . . +x
+ // -z /
+ // : /----B----
+ // 0 C
+ // : ----A----/
+ // +z /
+ //
+ // C is in front of A and behind B (not what you'd expect by comparing centers).
+ // A and B don't overlap, so they're incomparable.
+
+ WebTransformationMatrix transformA;
+ transformA.translate3d(-6, 0, 1);
+ transformA.translate(-4, -10);
+ CCLayerSorter::LayerShape layerA(8, 20, transformA);
+
+ WebTransformationMatrix transformB;
+ transformB.translate3d(6, 0, -1);
+ transformB.translate(-4, -10);
+ CCLayerSorter::LayerShape layerB(8, 20, transformB);
+
+ WebTransformationMatrix transformC;
+ transformC.rotate3d(0, 1, 0, 40);
+ transformC.translate(-4, -10);
+ CCLayerSorter::LayerShape layerC(8, 20, transformC);
+
+ overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerC, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+ overlapResult = CCLayerSorter::checkOverlap(&layerC, &layerB, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+ overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::None, overlapResult);
+}
+
+TEST(CCLayerSorterTest, LayersUnderPathologicalPerspectiveTransform)
+{
+ CCLayerSorter::ABCompareResult overlapResult;
+ const float zThreshold = 0.1f;
+ float weight = 0;
+
+ // On perspective projection, if w becomes negative, the re-projected point will be
+ // invalid and un-usable. Correct code needs to clip away portions of the geometry
+ // where w < 0. If the code uses the invalid value, it will think that a layer has
+ // different bounds than it really does, which can cause things to sort incorrectly.
+
+ WebTransformationMatrix perspectiveMatrix;
+ perspectiveMatrix.applyPerspective(1);
+
+ WebTransformationMatrix transformA;
+ transformA.translate3d(-15, 0, -2);
+ transformA.translate(-5, -5);
+ CCLayerSorter::LayerShape layerA(10, 10, perspectiveMatrix * transformA);
+
+ // With this sequence of transforms, when layer B is correctly clipped, it will be
+ // visible on the left half of the projection plane, in front of layerA. When it is
+ // not clipped, its bounds will actually incorrectly appear much smaller and the
+ // correct sorting dependency will not be found.
+ WebTransformationMatrix transformB;
+ transformB.translate3d(0, 0, 0.7);
+ transformB.rotate3d(0, 45, 0);
+ transformB.translate(-5, -5);
+ CCLayerSorter::LayerShape layerB(10, 10, perspectiveMatrix * transformB);
+
+ // Sanity check that the test case actually covers the intended scenario, where part
+ // of layer B go behind the w = 0 plane.
+ FloatQuad testQuad = FloatQuad(FloatRect(FloatPoint(-0.5, -0.5), FloatSize(1, 1)));
+ bool clipped = false;
+ CCMathUtil::mapQuad(perspectiveMatrix * transformB, testQuad, clipped);
+ ASSERT_TRUE(clipped);
+
+ overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight);
+ EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+}
+
+TEST(CCLayerSorterTest, verifyExistingOrderingPreservedWhenNoZDiff)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ // If there is no reason to re-sort the layers (i.e. no 3d z difference), then the
+ // existing ordering provided on input should be retained. This test covers the fix in
+ // https://bugs.webkit.org/show_bug.cgi?id=75046. Before this fix, ordering was
+ // accidentally reversed, causing bugs in z-index ordering on websites when
+ // preserves3D triggered the CCLayerSorter.
+
+ // Input list of layers: [1, 2, 3, 4, 5].
+ // Expected output: [3, 4, 1, 2, 5].
+ // - 1, 2, and 5 do not have a 3d z difference, and therefore their relative ordering should be retained.
+ // - 3 and 4 do not have a 3d z difference, and therefore their relative ordering should be retained.
+ // - 3 and 4 should be re-sorted so they are in front of 1, 2, and 5.
+
+ OwnPtr<CCLayerImpl> layer1 = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> layer2 = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> layer3 = CCLayerImpl::create(3);
+ OwnPtr<CCLayerImpl> layer4 = CCLayerImpl::create(4);
+ OwnPtr<CCLayerImpl> layer5 = CCLayerImpl::create(5);
+
+ WebTransformationMatrix BehindMatrix;
+ BehindMatrix.translate3d(0, 0, 2);
+ WebTransformationMatrix FrontMatrix;
+ FrontMatrix.translate3d(0, 0, 1);
+
+ layer1->setBounds(IntSize(10, 10));
+ layer1->setContentBounds(IntSize(10, 10));
+ layer1->setDrawTransform(BehindMatrix);
+ layer1->setDrawsContent(true);
+
+ layer2->setBounds(IntSize(20, 20));
+ layer2->setContentBounds(IntSize(20, 20));
+ layer2->setDrawTransform(BehindMatrix);
+ layer2->setDrawsContent(true);
+
+ layer3->setBounds(IntSize(30, 30));
+ layer3->setContentBounds(IntSize(30, 30));
+ layer3->setDrawTransform(FrontMatrix);
+ layer3->setDrawsContent(true);
+
+ layer4->setBounds(IntSize(40, 40));
+ layer4->setContentBounds(IntSize(40, 40));
+ layer4->setDrawTransform(FrontMatrix);
+ layer4->setDrawsContent(true);
+
+ layer5->setBounds(IntSize(50, 50));
+ layer5->setContentBounds(IntSize(50, 50));
+ layer5->setDrawTransform(BehindMatrix);
+ layer5->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> layerList;
+ layerList.append(layer1.get());
+ layerList.append(layer2.get());
+ layerList.append(layer3.get());
+ layerList.append(layer4.get());
+ layerList.append(layer5.get());
+
+ ASSERT_EQ(static_cast<size_t>(5), layerList.size());
+ EXPECT_EQ(1, layerList[0]->id());
+ EXPECT_EQ(2, layerList[1]->id());
+ EXPECT_EQ(3, layerList[2]->id());
+ EXPECT_EQ(4, layerList[3]->id());
+ EXPECT_EQ(5, layerList[4]->id());
+
+ CCLayerSorter layerSorter;
+ layerSorter.sort(layerList.begin(), layerList.end());
+
+ ASSERT_EQ(static_cast<size_t>(5), layerList.size());
+ EXPECT_EQ(3, layerList[0]->id());
+ EXPECT_EQ(4, layerList[1]->id());
+ EXPECT_EQ(1, layerList[2]->id());
+ EXPECT_EQ(2, layerList[3]->id());
+ EXPECT_EQ(5, layerList[4]->id());
+}
+
+} // namespace
diff --git a/cc/CCLayerTilingData.cpp b/cc/CCLayerTilingData.cpp
new file mode 100644
index 0000000..04ccda5
--- /dev/null
+++ b/cc/CCLayerTilingData.cpp
@@ -0,0 +1,149 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerTilingData.h"
+
+using namespace std;
+
+namespace WebCore {
+
+PassOwnPtr<CCLayerTilingData> CCLayerTilingData::create(const IntSize& tileSize, BorderTexelOption border)
+{
+ return adoptPtr(new CCLayerTilingData(tileSize, border));
+}
+
+CCLayerTilingData::CCLayerTilingData(const IntSize& tileSize, BorderTexelOption border)
+ : m_tilingData(tileSize, IntSize(), border == HasBorderTexels)
+{
+ setTileSize(tileSize);
+}
+
+void CCLayerTilingData::setTileSize(const IntSize& size)
+{
+ if (tileSize() == size)
+ return;
+
+ reset();
+
+ m_tilingData.setMaxTextureSize(size);
+}
+
+const IntSize& CCLayerTilingData::tileSize() const
+{
+ return m_tilingData.maxTextureSize();
+}
+
+void CCLayerTilingData::setBorderTexelOption(BorderTexelOption borderTexelOption)
+{
+ bool borderTexels = borderTexelOption == HasBorderTexels;
+ if (hasBorderTexels() == borderTexels)
+ return;
+
+ reset();
+ m_tilingData.setHasBorderTexels(borderTexels);
+}
+
+const CCLayerTilingData& CCLayerTilingData::operator=(const CCLayerTilingData& tiler)
+{
+ m_tilingData = tiler.m_tilingData;
+
+ return *this;
+}
+
+void CCLayerTilingData::addTile(PassOwnPtr<Tile> tile, int i, int j)
+{
+ ASSERT(!tileAt(i, j));
+ tile->moveTo(i, j);
+ m_tiles.add(make_pair(i, j), tile);
+}
+
+PassOwnPtr<CCLayerTilingData::Tile> CCLayerTilingData::takeTile(int i, int j)
+{
+ return m_tiles.take(make_pair(i, j));
+}
+
+CCLayerTilingData::Tile* CCLayerTilingData::tileAt(int i, int j) const
+{
+ return m_tiles.get(make_pair(i, j));
+}
+
+void CCLayerTilingData::reset()
+{
+ m_tiles.clear();
+}
+
+void CCLayerTilingData::contentRectToTileIndices(const IntRect& contentRect, int& left, int& top, int& right, int& bottom) const
+{
+ // An empty rect doesn't result in an empty set of tiles, so don't pass an empty rect.
+ // FIXME: Possibly we should fill a vector of tiles instead,
+ // since the normal use of this function is to enumerate some tiles.
+ ASSERT(!contentRect.isEmpty());
+
+ left = m_tilingData.tileXIndexFromSrcCoord(contentRect.x());
+ top = m_tilingData.tileYIndexFromSrcCoord(contentRect.y());
+ right = m_tilingData.tileXIndexFromSrcCoord(contentRect.maxX() - 1);
+ bottom = m_tilingData.tileYIndexFromSrcCoord(contentRect.maxY() - 1);
+}
+
+IntRect CCLayerTilingData::tileRect(const Tile* tile) const
+{
+ IntRect tileRect = m_tilingData.tileBoundsWithBorder(tile->i(), tile->j());
+ tileRect.setSize(tileSize());
+ return tileRect;
+}
+
+Region CCLayerTilingData::opaqueRegionInContentRect(const IntRect& contentRect) const
+{
+ if (contentRect.isEmpty())
+ return Region();
+
+ Region opaqueRegion;
+ int left, top, right, bottom;
+ contentRectToTileIndices(contentRect, left, top, right, bottom);
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ Tile* tile = tileAt(i, j);
+ if (!tile)
+ continue;
+
+ IntRect tileOpaqueRect = intersection(contentRect, tile->opaqueRect());
+ opaqueRegion.unite(tileOpaqueRect);
+ }
+ }
+ return opaqueRegion;
+}
+
+void CCLayerTilingData::setBounds(const IntSize& size)
+{
+ m_tilingData.setTotalSize(size);
+ if (size.isEmpty()) {
+ m_tiles.clear();
+ return;
+ }
+
+ // Any tiles completely outside our new bounds are invalid and should be dropped.
+ int left, top, right, bottom;
+ contentRectToTileIndices(IntRect(IntPoint(), size), left, top, right, bottom);
+ Vector<TileMapKey> invalidTileKeys;
+ for (TileMap::const_iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) {
+ if (it->first.first > right || it->first.second > bottom)
+ invalidTileKeys.append(it->first);
+ }
+ for (size_t i = 0; i < invalidTileKeys.size(); ++i)
+ m_tiles.remove(invalidTileKeys[i]);
+}
+
+IntSize CCLayerTilingData::bounds() const
+{
+ return m_tilingData.totalSize();
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCLayerTilingData.h b/cc/CCLayerTilingData.h
new file mode 100644
index 0000000..391639a
--- /dev/null
+++ b/cc/CCLayerTilingData.h
@@ -0,0 +1,98 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCLayerTilingData_h
+#define CCLayerTilingData_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "IntRect.h"
+#include "Region.h"
+#include "TilingData.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashTraits.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCLayerTilingData {
+public:
+ enum BorderTexelOption { HasBorderTexels, NoBorderTexels };
+
+ static PassOwnPtr<CCLayerTilingData> create(const IntSize& tileSize, BorderTexelOption);
+
+ bool hasEmptyBounds() const { return m_tilingData.hasEmptyBounds(); }
+ int numTilesX() const { return m_tilingData.numTilesX(); }
+ int numTilesY() const { return m_tilingData.numTilesY(); }
+ IntRect tileBounds(int i, int j) const { return m_tilingData.tileBounds(i, j); }
+ IntPoint textureOffset(int xIndex, int yIndex) const { return m_tilingData.textureOffset(xIndex, yIndex); }
+
+ // Change the tile size. This may invalidate all the existing tiles.
+ void setTileSize(const IntSize&);
+ const IntSize& tileSize() const;
+ // Change the border texel setting. This may invalidate all existing tiles.
+ void setBorderTexelOption(BorderTexelOption);
+ bool hasBorderTexels() const { return m_tilingData.borderTexels(); }
+
+ bool isEmpty() const { return hasEmptyBounds() || !tiles().size(); }
+
+ const CCLayerTilingData& operator=(const CCLayerTilingData&);
+
+ class Tile {
+ WTF_MAKE_NONCOPYABLE(Tile);
+ public:
+ Tile() : m_i(-1), m_j(-1) { }
+ virtual ~Tile() { }
+
+ int i() const { return m_i; }
+ int j() const { return m_j; }
+ void moveTo(int i, int j) { m_i = i; m_j = j; }
+
+ const IntRect& opaqueRect() const { return m_opaqueRect; }
+ void setOpaqueRect(const IntRect& opaqueRect) { m_opaqueRect = opaqueRect; }
+ private:
+ int m_i;
+ int m_j;
+ IntRect m_opaqueRect;
+ };
+ // Default hash key traits for integers disallow 0 and -1 as a key, so
+ // use a custom hash trait which disallows -1 and -2 instead.
+ typedef std::pair<int, int> TileMapKey;
+ struct TileMapKeyTraits : HashTraits<TileMapKey> {
+ static const bool emptyValueIsZero = false;
+ static const bool needsDestruction = false;
+ static TileMapKey emptyValue() { return std::make_pair(-1, -1); }
+ static void constructDeletedValue(TileMapKey& slot) { slot = std::make_pair(-2, -2); }
+ static bool isDeletedValue(TileMapKey value) { return value.first == -2 && value.second == -2; }
+ };
+ typedef HashMap<TileMapKey, OwnPtr<Tile>, DefaultHash<TileMapKey>::Hash, TileMapKeyTraits> TileMap;
+
+ void addTile(PassOwnPtr<Tile>, int, int);
+ PassOwnPtr<Tile> takeTile(int, int);
+ Tile* tileAt(int, int) const;
+ const TileMap& tiles() const { return m_tiles; }
+
+ void setBounds(const IntSize&);
+ IntSize bounds() const;
+
+ void contentRectToTileIndices(const IntRect&, int &left, int &top, int &right, int &bottom) const;
+ IntRect tileRect(const Tile*) const;
+
+ Region opaqueRegionInContentRect(const IntRect&) const;
+
+ void reset();
+
+protected:
+ CCLayerTilingData(const IntSize& tileSize, BorderTexelOption);
+
+ TileMap m_tiles;
+ TilingData m_tilingData;
+};
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCLayerTreeHost.cpp b/cc/CCLayerTreeHost.cpp
new file mode 100644
index 0000000..aa8d441
--- /dev/null
+++ b/cc/CCLayerTreeHost.cpp
@@ -0,0 +1,752 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTreeHost.h"
+
+#include "CCFontAtlas.h"
+#include "CCGraphicsContext.h"
+#include "CCHeadsUpDisplayLayerImpl.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerIterator.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCOcclusionTracker.h"
+#include "CCOverdrawMetrics.h"
+#include "CCSettings.h"
+#include "CCSingleThreadProxy.h"
+#include "CCThreadProxy.h"
+#include "HeadsUpDisplayLayerChromium.h"
+#include "LayerChromium.h"
+#include "Region.h"
+#include "TraceEvent.h"
+#include "TreeSynchronizer.h"
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+static int numLayerTreeInstances;
+}
+
+namespace WebCore {
+
+bool CCLayerTreeHost::s_needsFilterContext = false;
+
+bool CCLayerTreeHost::anyLayerTreeHostInstanceExists()
+{
+ return numLayerTreeInstances > 0;
+}
+
+PassOwnPtr<CCLayerTreeHost> CCLayerTreeHost::create(CCLayerTreeHostClient* client, const CCLayerTreeSettings& settings)
+{
+ OwnPtr<CCLayerTreeHost> layerTreeHost = adoptPtr(new CCLayerTreeHost(client, settings));
+ if (!layerTreeHost->initialize())
+ return nullptr;
+ return layerTreeHost.release();
+}
+
+CCLayerTreeHost::CCLayerTreeHost(CCLayerTreeHostClient* client, const CCLayerTreeSettings& settings)
+ : m_compositorIdentifier(-1)
+ , m_animating(false)
+ , m_needsAnimateLayers(false)
+ , m_client(client)
+ , m_commitNumber(0)
+ , m_renderingStats()
+ , m_rendererInitialized(false)
+ , m_contextLost(false)
+ , m_numTimesRecreateShouldFail(0)
+ , m_numFailedRecreateAttempts(0)
+ , m_settings(settings)
+ , m_deviceScaleFactor(1)
+ , m_visible(true)
+ , m_pageScaleFactor(1)
+ , m_minPageScaleFactor(1)
+ , m_maxPageScaleFactor(1)
+ , m_triggerIdleUpdates(true)
+ , m_backgroundColor(SK_ColorWHITE)
+ , m_hasTransparentBackground(false)
+ , m_partialTextureUpdateRequests(0)
+{
+ ASSERT(CCProxy::isMainThread());
+ numLayerTreeInstances++;
+}
+
+bool CCLayerTreeHost::initialize()
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHost::initialize");
+
+ if (CCProxy::hasImplThread())
+ m_proxy = CCThreadProxy::create(this);
+ else
+ m_proxy = CCSingleThreadProxy::create(this);
+ m_proxy->start();
+
+ if (!m_proxy->initializeContext())
+ return false;
+
+ m_compositorIdentifier = m_proxy->compositorIdentifier();
+ return true;
+}
+
+CCLayerTreeHost::~CCLayerTreeHost()
+{
+ if (m_rootLayer)
+ m_rootLayer->setLayerTreeHost(0);
+ ASSERT(CCProxy::isMainThread());
+ TRACE_EVENT0("cc", "CCLayerTreeHost::~CCLayerTreeHost");
+ ASSERT(m_proxy);
+ m_proxy->stop();
+ m_proxy.clear();
+ numLayerTreeInstances--;
+ RateLimiterMap::iterator it = m_rateLimiters.begin();
+ if (it != m_rateLimiters.end())
+ it->second->stop();
+}
+
+void CCLayerTreeHost::setSurfaceReady()
+{
+ m_proxy->setSurfaceReady();
+}
+
+void CCLayerTreeHost::initializeRenderer()
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHost::initializeRenderer");
+ if (!m_proxy->initializeRenderer()) {
+ // Uh oh, better tell the client that we can't do anything with this context.
+ m_client->didRecreateOutputSurface(false);
+ return;
+ }
+
+ // Update m_settings based on capabilities that we got back from the renderer.
+ m_settings.acceleratePainting = m_proxy->rendererCapabilities().usingAcceleratedPainting;
+
+ // Update m_settings based on partial update capability.
+ m_settings.maxPartialTextureUpdates = min(m_settings.maxPartialTextureUpdates, m_proxy->maxPartialTextureUpdates());
+
+ m_contentsTextureManager = CCPrioritizedTextureManager::create(0, m_proxy->rendererCapabilities().maxTextureSize, CCRenderer::ContentPool);
+ m_surfaceMemoryPlaceholder = m_contentsTextureManager->createTexture(IntSize(), GraphicsContext3D::RGBA);
+
+ m_rendererInitialized = true;
+
+ m_settings.defaultTileSize = IntSize(min(m_settings.defaultTileSize.width(), m_proxy->rendererCapabilities().maxTextureSize),
+ min(m_settings.defaultTileSize.height(), m_proxy->rendererCapabilities().maxTextureSize));
+ m_settings.maxUntiledLayerSize = IntSize(min(m_settings.maxUntiledLayerSize.width(), m_proxy->rendererCapabilities().maxTextureSize),
+ min(m_settings.maxUntiledLayerSize.height(), m_proxy->rendererCapabilities().maxTextureSize));
+}
+
+CCLayerTreeHost::RecreateResult CCLayerTreeHost::recreateContext()
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHost::recreateContext");
+ ASSERT(m_contextLost);
+
+ bool recreated = false;
+ if (!m_numTimesRecreateShouldFail)
+ recreated = m_proxy->recreateContext();
+ else
+ m_numTimesRecreateShouldFail--;
+
+ if (recreated) {
+ m_client->didRecreateOutputSurface(true);
+ m_contextLost = false;
+ return RecreateSucceeded;
+ }
+
+ // Tolerate a certain number of recreation failures to work around races
+ // in the context-lost machinery.
+ m_numFailedRecreateAttempts++;
+ if (m_numFailedRecreateAttempts < 5) {
+ // FIXME: The single thread does not self-schedule context
+ // recreation. So force another recreation attempt to happen by requesting
+ // another commit.
+ if (!CCProxy::hasImplThread())
+ setNeedsCommit();
+ return RecreateFailedButTryAgain;
+ }
+
+ // We have tried too many times to recreate the context. Tell the host to fall
+ // back to software rendering.
+ m_client->didRecreateOutputSurface(false);
+ return RecreateFailedAndGaveUp;
+}
+
+void CCLayerTreeHost::deleteContentsTexturesOnImplThread(CCResourceProvider* resourceProvider)
+{
+ ASSERT(CCProxy::isImplThread());
+ if (m_rendererInitialized)
+ m_contentsTextureManager->clearAllMemory(resourceProvider);
+}
+
+void CCLayerTreeHost::acquireLayerTextures()
+{
+ ASSERT(CCProxy::isMainThread());
+ m_proxy->acquireLayerTextures();
+}
+
+void CCLayerTreeHost::updateAnimations(double monotonicFrameBeginTime)
+{
+ m_animating = true;
+ m_client->animate(monotonicFrameBeginTime);
+ animateLayers(monotonicFrameBeginTime);
+ m_animating = false;
+
+ m_renderingStats.numAnimationFrames++;
+}
+
+void CCLayerTreeHost::layout()
+{
+ m_client->layout();
+}
+
+void CCLayerTreeHost::beginCommitOnImplThread(CCLayerTreeHostImpl* hostImpl)
+{
+ ASSERT(CCProxy::isImplThread());
+ TRACE_EVENT0("cc", "CCLayerTreeHost::commitTo");
+
+ m_contentsTextureManager->reduceMemory(hostImpl->resourceProvider());
+}
+
+// This function commits the CCLayerTreeHost to an impl tree. When modifying
+// this function, keep in mind that the function *runs* on the impl thread! Any
+// code that is logically a main thread operation, e.g. deletion of a LayerChromium,
+// should be delayed until the CCLayerTreeHost::commitComplete, which will run
+// after the commit, but on the main thread.
+void CCLayerTreeHost::finishCommitOnImplThread(CCLayerTreeHostImpl* hostImpl)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ hostImpl->setRootLayer(TreeSynchronizer::synchronizeTrees(rootLayer(), hostImpl->detachLayerTree(), hostImpl));
+
+ if (!m_hudLayer)
+ hostImpl->setHudLayer(0);
+ else
+ hostImpl->setHudLayer(static_cast<CCHeadsUpDisplayLayerImpl*>(CCLayerTreeHostCommon::findLayerInSubtree(hostImpl->rootLayer(), m_hudLayer->id())));
+
+ // We may have added an animation during the tree sync. This will cause both layer tree hosts
+ // to visit their controllers.
+ if (rootLayer() && m_needsAnimateLayers)
+ hostImpl->setNeedsAnimateLayers();
+
+ hostImpl->setSourceFrameNumber(commitNumber());
+ hostImpl->setViewportSize(layoutViewportSize(), deviceViewportSize());
+ hostImpl->setDeviceScaleFactor(deviceScaleFactor());
+ hostImpl->setPageScaleFactorAndLimits(m_pageScaleFactor, m_minPageScaleFactor, m_maxPageScaleFactor);
+ hostImpl->setBackgroundColor(m_backgroundColor);
+ hostImpl->setHasTransparentBackground(m_hasTransparentBackground);
+
+ m_commitNumber++;
+}
+
+void CCLayerTreeHost::setFontAtlas(PassOwnPtr<CCFontAtlas> fontAtlas)
+{
+ m_fontAtlas = fontAtlas;
+ setNeedsCommit();
+}
+
+void CCLayerTreeHost::willCommit()
+{
+ m_client->willCommit();
+ if (m_rootLayer && m_settings.showDebugInfo()) {
+ if (!m_hudLayer)
+ m_hudLayer = HeadsUpDisplayLayerChromium::create();
+
+ if (m_fontAtlas)
+ m_hudLayer->setFontAtlas(m_fontAtlas.release());
+
+ if (m_hudLayer->parent() != m_rootLayer.get()) {
+ m_hudLayer->removeFromParent();
+ m_rootLayer->addChild(m_hudLayer);
+ }
+ }
+}
+
+void CCLayerTreeHost::commitComplete()
+{
+ m_deleteTextureAfterCommitList.clear();
+ m_client->didCommit();
+}
+
+PassOwnPtr<CCGraphicsContext> CCLayerTreeHost::createContext()
+{
+ return m_client->createOutputSurface();
+}
+
+PassOwnPtr<CCLayerTreeHostImpl> CCLayerTreeHost::createLayerTreeHostImpl(CCLayerTreeHostImplClient* client)
+{
+ return CCLayerTreeHostImpl::create(m_settings, client);
+}
+
+void CCLayerTreeHost::didLoseContext()
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHost::didLoseContext");
+ ASSERT(CCProxy::isMainThread());
+ m_contextLost = true;
+ m_numFailedRecreateAttempts = 0;
+ setNeedsCommit();
+}
+
+bool CCLayerTreeHost::compositeAndReadback(void *pixels, const IntRect& rect)
+{
+ m_triggerIdleUpdates = false;
+ bool ret = m_proxy->compositeAndReadback(pixels, rect);
+ m_triggerIdleUpdates = true;
+ return ret;
+}
+
+void CCLayerTreeHost::finishAllRendering()
+{
+ if (!m_rendererInitialized)
+ return;
+ m_proxy->finishAllRendering();
+}
+
+void CCLayerTreeHost::renderingStats(CCRenderingStats& stats) const
+{
+ stats = m_renderingStats;
+ m_proxy->implSideRenderingStats(stats);
+}
+
+const RendererCapabilities& CCLayerTreeHost::rendererCapabilities() const
+{
+ return m_proxy->rendererCapabilities();
+}
+
+void CCLayerTreeHost::setNeedsAnimate()
+{
+ ASSERT(CCProxy::hasImplThread());
+ m_proxy->setNeedsAnimate();
+}
+
+void CCLayerTreeHost::setNeedsCommit()
+{
+ m_proxy->setNeedsCommit();
+}
+
+void CCLayerTreeHost::setNeedsRedraw()
+{
+ m_proxy->setNeedsRedraw();
+ if (!CCThreadProxy::implThread())
+ m_client->scheduleComposite();
+}
+
+bool CCLayerTreeHost::commitRequested() const
+{
+ return m_proxy->commitRequested();
+}
+
+void CCLayerTreeHost::setAnimationEvents(PassOwnPtr<CCAnimationEventsVector> events, double wallClockTime)
+{
+ ASSERT(CCThreadProxy::isMainThread());
+ setAnimationEventsRecursive(*events, m_rootLayer.get(), wallClockTime);
+}
+
+void CCLayerTreeHost::didAddAnimation()
+{
+ m_needsAnimateLayers = true;
+ m_proxy->didAddAnimation();
+}
+
+void CCLayerTreeHost::setRootLayer(PassRefPtr<LayerChromium> rootLayer)
+{
+ if (m_rootLayer == rootLayer)
+ return;
+
+ if (m_rootLayer)
+ m_rootLayer->setLayerTreeHost(0);
+ m_rootLayer = rootLayer;
+ if (m_rootLayer)
+ m_rootLayer->setLayerTreeHost(this);
+ setNeedsCommit();
+}
+
+void CCLayerTreeHost::setViewportSize(const IntSize& layoutViewportSize, const IntSize& deviceViewportSize)
+{
+ if (layoutViewportSize == m_layoutViewportSize && deviceViewportSize == m_deviceViewportSize)
+ return;
+
+ m_layoutViewportSize = layoutViewportSize;
+ m_deviceViewportSize = deviceViewportSize;
+
+ setNeedsCommit();
+}
+
+void CCLayerTreeHost::setPageScaleFactorAndLimits(float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor)
+{
+ if (pageScaleFactor == m_pageScaleFactor && minPageScaleFactor == m_minPageScaleFactor && maxPageScaleFactor == m_maxPageScaleFactor)
+ return;
+
+ m_pageScaleFactor = pageScaleFactor;
+ m_minPageScaleFactor = minPageScaleFactor;
+ m_maxPageScaleFactor = maxPageScaleFactor;
+ setNeedsCommit();
+}
+
+void CCLayerTreeHost::setVisible(bool visible)
+{
+ if (m_visible == visible)
+ return;
+ m_visible = visible;
+ m_proxy->setVisible(visible);
+}
+
+void CCLayerTreeHost::evictAllContentTextures()
+{
+ ASSERT(CCProxy::isMainThread());
+ ASSERT(m_contentsTextureManager.get());
+ m_contentsTextureManager->allBackingTexturesWereDeleted();
+}
+
+void CCLayerTreeHost::startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double durationSec)
+{
+ m_proxy->startPageScaleAnimation(targetPosition, useAnchor, scale, durationSec);
+}
+
+void CCLayerTreeHost::loseContext(int numTimes)
+{
+ TRACE_EVENT1("cc", "CCLayerTreeHost::loseCompositorContext", "numTimes", numTimes);
+ m_numTimesRecreateShouldFail = numTimes - 1;
+ m_proxy->loseContext();
+}
+
+CCPrioritizedTextureManager* CCLayerTreeHost::contentsTextureManager() const
+{
+ return m_contentsTextureManager.get();
+}
+
+void CCLayerTreeHost::composite()
+{
+ ASSERT(!CCThreadProxy::implThread());
+ static_cast<CCSingleThreadProxy*>(m_proxy.get())->compositeImmediately();
+}
+
+void CCLayerTreeHost::scheduleComposite()
+{
+ m_client->scheduleComposite();
+}
+
+bool CCLayerTreeHost::initializeRendererIfNeeded()
+{
+ if (!m_rendererInitialized) {
+ initializeRenderer();
+ // If we couldn't initialize, then bail since we're returning to software mode.
+ if (!m_rendererInitialized)
+ return false;
+ }
+ if (m_contextLost) {
+ if (recreateContext() != RecreateSucceeded)
+ return false;
+ }
+ return true;
+}
+
+
+void CCLayerTreeHost::updateLayers(CCTextureUpdateQueue& queue, size_t memoryAllocationLimitBytes)
+{
+ ASSERT(m_rendererInitialized);
+ ASSERT(memoryAllocationLimitBytes);
+
+ if (!rootLayer())
+ return;
+
+ if (layoutViewportSize().isEmpty())
+ return;
+
+ m_contentsTextureManager->setMaxMemoryLimitBytes(memoryAllocationLimitBytes);
+
+ updateLayers(rootLayer(), queue);
+}
+
+void CCLayerTreeHost::updateLayers(LayerChromium* rootLayer, CCTextureUpdateQueue& queue)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHost::updateLayers");
+
+ LayerList updateList;
+
+ {
+ TRACE_EVENT0("cc", "CCLayerTreeHost::updateLayers::calcDrawEtc");
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer, deviceViewportSize(), m_deviceScaleFactor, rendererCapabilities().maxTextureSize, updateList);
+ CCLayerTreeHostCommon::calculateVisibleRects(updateList);
+ }
+
+ // Reset partial texture update requests.
+ m_partialTextureUpdateRequests = 0;
+
+ bool needMoreUpdates = paintLayerContents(updateList, queue);
+ if (m_triggerIdleUpdates && needMoreUpdates)
+ setNeedsCommit();
+
+ for (size_t i = 0; i < updateList.size(); ++i)
+ updateList[i]->clearRenderSurface();
+}
+
+void CCLayerTreeHost::setPrioritiesForSurfaces(size_t surfaceMemoryBytes)
+{
+ // Surfaces have a place holder for their memory since they are managed
+ // independantly but should still be tracked and reduce other memory usage.
+ m_surfaceMemoryPlaceholder->setTextureManager(m_contentsTextureManager.get());
+ m_surfaceMemoryPlaceholder->setRequestPriority(CCPriorityCalculator::renderSurfacePriority());
+ m_surfaceMemoryPlaceholder->setToSelfManagedMemoryPlaceholder(surfaceMemoryBytes);
+}
+
+void CCLayerTreeHost::setPrioritiesForLayers(const LayerList& updateList)
+{
+ // Use BackToFront since it's cheap and this isn't order-dependent.
+ typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::BackToFront> CCLayerIteratorType;
+
+ CCPriorityCalculator calculator;
+ CCLayerIteratorType end = CCLayerIteratorType::end(&updateList);
+ for (CCLayerIteratorType it = CCLayerIteratorType::begin(&updateList); it != end; ++it) {
+ if (it.representsItself())
+ it->setTexturePriorities(calculator);
+ else if (it.representsTargetRenderSurface()) {
+ if (it->maskLayer())
+ it->maskLayer()->setTexturePriorities(calculator);
+ if (it->replicaLayer() && it->replicaLayer()->maskLayer())
+ it->replicaLayer()->maskLayer()->setTexturePriorities(calculator);
+ }
+ }
+}
+
+void CCLayerTreeHost::prioritizeTextures(const LayerList& renderSurfaceLayerList, CCOverdrawMetrics& metrics)
+{
+ m_contentsTextureManager->clearPriorities();
+
+ size_t memoryForRenderSurfacesMetric = calculateMemoryForRenderSurfaces(renderSurfaceLayerList);
+
+ setPrioritiesForLayers(renderSurfaceLayerList);
+ setPrioritiesForSurfaces(memoryForRenderSurfacesMetric);
+
+ metrics.didUseContentsTextureMemoryBytes(m_contentsTextureManager->memoryAboveCutoffBytes());
+ metrics.didUseRenderSurfaceTextureMemoryBytes(memoryForRenderSurfacesMetric);
+
+ m_contentsTextureManager->prioritizeTextures();
+}
+
+size_t CCLayerTreeHost::calculateMemoryForRenderSurfaces(const LayerList& updateList)
+{
+ size_t readbackBytes = 0;
+ size_t maxBackgroundTextureBytes = 0;
+ size_t contentsTextureBytes = 0;
+
+ // Start iteration at 1 to skip the root surface as it does not have a texture cost.
+ for (size_t i = 1; i < updateList.size(); ++i) {
+ LayerChromium* renderSurfaceLayer = updateList[i].get();
+ RenderSurfaceChromium* renderSurface = renderSurfaceLayer->renderSurface();
+
+ size_t bytes = CCTexture::memorySizeBytes(renderSurface->contentRect().size(), GraphicsContext3D::RGBA);
+ contentsTextureBytes += bytes;
+
+ if (renderSurfaceLayer->backgroundFilters().isEmpty())
+ continue;
+
+ if (bytes > maxBackgroundTextureBytes)
+ maxBackgroundTextureBytes = bytes;
+ if (!readbackBytes)
+ readbackBytes = CCTexture::memorySizeBytes(m_deviceViewportSize, GraphicsContext3D::RGBA);
+ }
+ return readbackBytes + maxBackgroundTextureBytes + contentsTextureBytes;
+}
+
+bool CCLayerTreeHost::paintMasksForRenderSurface(LayerChromium* renderSurfaceLayer, CCTextureUpdateQueue& queue)
+{
+ // Note: Masks and replicas only exist for layers that own render surfaces. If we reach this point
+ // in code, we already know that at least something will be drawn into this render surface, so the
+ // mask and replica should be painted.
+
+ bool needMoreUpdates = false;
+ LayerChromium* maskLayer = renderSurfaceLayer->maskLayer();
+ if (maskLayer) {
+ maskLayer->update(queue, 0, m_renderingStats);
+ needMoreUpdates |= maskLayer->needMoreUpdates();
+ }
+
+ LayerChromium* replicaMaskLayer = renderSurfaceLayer->replicaLayer() ? renderSurfaceLayer->replicaLayer()->maskLayer() : 0;
+ if (replicaMaskLayer) {
+ replicaMaskLayer->update(queue, 0, m_renderingStats);
+ needMoreUpdates |= replicaMaskLayer->needMoreUpdates();
+ }
+ return needMoreUpdates;
+}
+
+bool CCLayerTreeHost::paintLayerContents(const LayerList& renderSurfaceLayerList, CCTextureUpdateQueue& queue)
+{
+ // Use FrontToBack to allow for testing occlusion and performing culling during the tree walk.
+ typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
+
+ bool needMoreUpdates = false;
+ bool recordMetricsForFrame = true; // FIXME: In the future, disable this when about:tracing is off.
+ CCOcclusionTracker occlusionTracker(m_rootLayer->renderSurface()->contentRect(), recordMetricsForFrame);
+ occlusionTracker.setMinimumTrackingSize(m_settings.minimumOcclusionTrackingSize);
+
+ prioritizeTextures(renderSurfaceLayerList, occlusionTracker.overdrawMetrics());
+
+ CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
+ for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
+ occlusionTracker.enterLayer(it);
+
+ if (it.representsTargetRenderSurface()) {
+ ASSERT(it->renderSurface()->drawOpacity() || it->renderSurface()->drawOpacityIsAnimating());
+ needMoreUpdates |= paintMasksForRenderSurface(*it, queue);
+ } else if (it.representsItself()) {
+ ASSERT(!it->bounds().isEmpty());
+ it->update(queue, &occlusionTracker, m_renderingStats);
+ needMoreUpdates |= it->needMoreUpdates();
+ }
+
+ occlusionTracker.leaveLayer(it);
+ }
+
+ occlusionTracker.overdrawMetrics().recordMetrics(this);
+
+ return needMoreUpdates;
+}
+
+static LayerChromium* findFirstScrollableLayer(LayerChromium* layer)
+{
+ if (!layer)
+ return 0;
+
+ if (layer->scrollable())
+ return layer;
+
+ for (size_t i = 0; i < layer->children().size(); ++i) {
+ LayerChromium* found = findFirstScrollableLayer(layer->children()[i].get());
+ if (found)
+ return found;
+ }
+
+ return 0;
+}
+
+void CCLayerTreeHost::applyScrollAndScale(const CCScrollAndScaleSet& info)
+{
+ if (!m_rootLayer)
+ return;
+
+ LayerChromium* rootScrollLayer = findFirstScrollableLayer(m_rootLayer.get());
+ IntSize rootScrollDelta;
+
+ for (size_t i = 0; i < info.scrolls.size(); ++i) {
+ LayerChromium* layer = CCLayerTreeHostCommon::findLayerInSubtree(m_rootLayer.get(), info.scrolls[i].layerId);
+ if (!layer)
+ continue;
+ if (layer == rootScrollLayer)
+ rootScrollDelta += info.scrolls[i].scrollDelta;
+ else
+ layer->scrollBy(info.scrolls[i].scrollDelta);
+ }
+ if (!rootScrollDelta.isZero() || info.pageScaleDelta != 1)
+ m_client->applyScrollAndScale(rootScrollDelta, info.pageScaleDelta);
+}
+
+void CCLayerTreeHost::startRateLimiter(WebKit::WebGraphicsContext3D* context)
+{
+ if (m_animating)
+ return;
+
+ ASSERT(context);
+ RateLimiterMap::iterator it = m_rateLimiters.find(context);
+ if (it != m_rateLimiters.end())
+ it->second->start();
+ else {
+ RefPtr<RateLimiter> rateLimiter = RateLimiter::create(context, this);
+ m_rateLimiters.set(context, rateLimiter);
+ rateLimiter->start();
+ }
+}
+
+void CCLayerTreeHost::stopRateLimiter(WebKit::WebGraphicsContext3D* context)
+{
+ RateLimiterMap::iterator it = m_rateLimiters.find(context);
+ if (it != m_rateLimiters.end()) {
+ it->second->stop();
+ m_rateLimiters.remove(it);
+ }
+}
+
+void CCLayerTreeHost::rateLimit()
+{
+ // Force a no-op command on the compositor context, so that any ratelimiting commands will wait for the compositing
+ // context, and therefore for the SwapBuffers.
+ m_proxy->forceSerializeOnSwapBuffers();
+}
+
+bool CCLayerTreeHost::bufferedUpdates()
+{
+ return m_settings.maxPartialTextureUpdates != numeric_limits<size_t>::max();
+}
+
+bool CCLayerTreeHost::requestPartialTextureUpdate()
+{
+ if (m_partialTextureUpdateRequests >= m_settings.maxPartialTextureUpdates)
+ return false;
+
+ m_partialTextureUpdateRequests++;
+ return true;
+}
+
+void CCLayerTreeHost::deleteTextureAfterCommit(PassOwnPtr<CCPrioritizedTexture> texture)
+{
+ m_deleteTextureAfterCommitList.append(texture);
+}
+
+void CCLayerTreeHost::setDeviceScaleFactor(float deviceScaleFactor)
+{
+ if (deviceScaleFactor == m_deviceScaleFactor)
+ return;
+ m_deviceScaleFactor = deviceScaleFactor;
+
+ setNeedsCommit();
+}
+
+void CCLayerTreeHost::animateLayers(double monotonicTime)
+{
+ if (!CCSettings::acceleratedAnimationEnabled() || !m_needsAnimateLayers)
+ return;
+
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::animateLayers");
+ m_needsAnimateLayers = animateLayersRecursive(m_rootLayer.get(), monotonicTime);
+}
+
+bool CCLayerTreeHost::animateLayersRecursive(LayerChromium* current, double monotonicTime)
+{
+ if (!current)
+ return false;
+
+ bool subtreeNeedsAnimateLayers = false;
+ CCLayerAnimationController* currentController = current->layerAnimationController();
+ currentController->animate(monotonicTime, 0);
+
+ // If the current controller still has an active animation, we must continue animating layers.
+ if (currentController->hasActiveAnimation())
+ subtreeNeedsAnimateLayers = true;
+
+ for (size_t i = 0; i < current->children().size(); ++i) {
+ if (animateLayersRecursive(current->children()[i].get(), monotonicTime))
+ subtreeNeedsAnimateLayers = true;
+ }
+
+ return subtreeNeedsAnimateLayers;
+}
+
+void CCLayerTreeHost::setAnimationEventsRecursive(const CCAnimationEventsVector& events, LayerChromium* layer, double wallClockTime)
+{
+ if (!layer)
+ return;
+
+ for (size_t eventIndex = 0; eventIndex < events.size(); ++eventIndex) {
+ if (layer->id() == events[eventIndex].layerId) {
+ if (events[eventIndex].type == CCAnimationEvent::Started)
+ layer->notifyAnimationStarted(events[eventIndex], wallClockTime);
+ else
+ layer->notifyAnimationFinished(wallClockTime);
+ }
+ }
+
+ for (size_t childIndex = 0; childIndex < layer->children().size(); ++childIndex)
+ setAnimationEventsRecursive(events, layer->children()[childIndex].get(), wallClockTime);
+}
+
+} // namespace WebCore
diff --git a/cc/CCLayerTreeHost.h b/cc/CCLayerTreeHost.h
new file mode 100644
index 0000000..11e1f9b
--- /dev/null
+++ b/cc/CCLayerTreeHost.h
@@ -0,0 +1,318 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerTreeHost_h
+#define CCLayerTreeHost_h
+
+#include "CCAnimationEvents.h"
+#include "CCGraphicsContext.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCOcclusionTracker.h"
+#include "CCPrioritizedTextureManager.h"
+#include "CCProxy.h"
+#include "CCRenderingStats.h"
+#include "IntRect.h"
+#include "RateLimiter.h"
+#include "SkColor.h"
+#include <limits>
+#include <wtf/HashMap.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class CCFontAtlas;
+class CCLayerChromium;
+class CCLayerTreeHostImpl;
+class CCLayerTreeHostImplClient;
+class CCPrioritizedTextureManager;
+class CCTextureUpdateQueue;
+class HeadsUpDisplayLayerChromium;
+class Region;
+struct CCScrollAndScaleSet;
+
+class CCLayerTreeHostClient {
+public:
+ virtual void willBeginFrame() = 0;
+ // Marks finishing compositing-related tasks on the main thread. In threaded mode, this corresponds to didCommit().
+ virtual void didBeginFrame() = 0;
+ virtual void animate(double frameBeginTime) = 0;
+ virtual void layout() = 0;
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) = 0;
+ virtual PassOwnPtr<WebKit::WebCompositorOutputSurface> createOutputSurface() = 0;
+ virtual void didRecreateOutputSurface(bool success) = 0;
+ virtual void willCommit() = 0;
+ virtual void didCommit() = 0;
+ virtual void didCommitAndDrawFrame() = 0;
+ virtual void didCompleteSwapBuffers() = 0;
+
+ // Used only in the single-threaded path.
+ virtual void scheduleComposite() = 0;
+
+protected:
+ virtual ~CCLayerTreeHostClient() { }
+};
+
+struct CCLayerTreeSettings {
+ CCLayerTreeSettings()
+ : acceleratePainting(false)
+ , showFPSCounter(false)
+ , showPlatformLayerTree(false)
+ , showPaintRects(false)
+ , showPropertyChangedRects(false)
+ , showSurfaceDamageRects(false)
+ , showScreenSpaceRects(false)
+ , showReplicaScreenSpaceRects(false)
+ , showOccludingRects(false)
+ , renderVSyncEnabled(true)
+ , refreshRate(0)
+ , maxPartialTextureUpdates(std::numeric_limits<size_t>::max())
+ , defaultTileSize(IntSize(256, 256))
+ , maxUntiledLayerSize(IntSize(512, 512))
+ , minimumOcclusionTrackingSize(IntSize(160, 160))
+ { }
+
+ bool acceleratePainting;
+ bool showFPSCounter;
+ bool showPlatformLayerTree;
+ bool showPaintRects;
+ bool showPropertyChangedRects;
+ bool showSurfaceDamageRects;
+ bool showScreenSpaceRects;
+ bool showReplicaScreenSpaceRects;
+ bool showOccludingRects;
+ bool renderVSyncEnabled;
+ double refreshRate;
+ size_t maxPartialTextureUpdates;
+ IntSize defaultTileSize;
+ IntSize maxUntiledLayerSize;
+ IntSize minimumOcclusionTrackingSize;
+
+ bool showDebugInfo() const { return showPlatformLayerTree || showFPSCounter || showDebugRects(); }
+ bool showDebugRects() const { return showPaintRects || showPropertyChangedRects || showSurfaceDamageRects || showScreenSpaceRects || showReplicaScreenSpaceRects || showOccludingRects; }
+};
+
+// Provides information on an Impl's rendering capabilities back to the CCLayerTreeHost
+struct RendererCapabilities {
+ RendererCapabilities()
+ : bestTextureFormat(0)
+ , contextHasCachedFrontBuffer(false)
+ , usingPartialSwap(false)
+ , usingAcceleratedPainting(false)
+ , usingSetVisibility(false)
+ , usingSwapCompleteCallback(false)
+ , usingGpuMemoryManager(false)
+ , usingDiscardFramebuffer(false)
+ , usingEglImage(false)
+ , maxTextureSize(0) { }
+
+ GC3Denum bestTextureFormat;
+ bool contextHasCachedFrontBuffer;
+ bool usingPartialSwap;
+ bool usingAcceleratedPainting;
+ bool usingSetVisibility;
+ bool usingSwapCompleteCallback;
+ bool usingGpuMemoryManager;
+ bool usingDiscardFramebuffer;
+ bool usingEglImage;
+ int maxTextureSize;
+};
+
+class CCLayerTreeHost : public RateLimiterClient {
+ WTF_MAKE_NONCOPYABLE(CCLayerTreeHost);
+public:
+ static PassOwnPtr<CCLayerTreeHost> create(CCLayerTreeHostClient*, const CCLayerTreeSettings&);
+ virtual ~CCLayerTreeHost();
+
+ void setSurfaceReady();
+
+ // Returns true if any CCLayerTreeHost is alive.
+ static bool anyLayerTreeHostInstanceExists();
+
+ static bool needsFilterContext() { return s_needsFilterContext; }
+ static void setNeedsFilterContext(bool needsFilterContext) { s_needsFilterContext = needsFilterContext; }
+ bool needsSharedContext() const { return needsFilterContext() || settings().acceleratePainting; }
+
+ // CCLayerTreeHost interface to CCProxy.
+ void willBeginFrame() { m_client->willBeginFrame(); }
+ void didBeginFrame() { m_client->didBeginFrame(); }
+ void updateAnimations(double monotonicFrameBeginTime);
+ void layout();
+ void beginCommitOnImplThread(CCLayerTreeHostImpl*);
+ void finishCommitOnImplThread(CCLayerTreeHostImpl*);
+ void willCommit();
+ void commitComplete();
+ PassOwnPtr<CCGraphicsContext> createContext();
+ virtual PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHostImpl(CCLayerTreeHostImplClient*);
+ void didLoseContext();
+ enum RecreateResult {
+ RecreateSucceeded,
+ RecreateFailedButTryAgain,
+ RecreateFailedAndGaveUp,
+ };
+ RecreateResult recreateContext();
+ void didCommitAndDrawFrame() { m_client->didCommitAndDrawFrame(); }
+ void didCompleteSwapBuffers() { m_client->didCompleteSwapBuffers(); }
+ void deleteContentsTexturesOnImplThread(CCResourceProvider*);
+ virtual void acquireLayerTextures();
+ // Returns false if we should abort this frame due to initialization failure.
+ bool initializeRendererIfNeeded();
+ void updateLayers(CCTextureUpdateQueue&, size_t contentsMemoryLimitBytes);
+
+ CCLayerTreeHostClient* client() { return m_client; }
+
+ int compositorIdentifier() const { return m_compositorIdentifier; }
+
+ // Only used when compositing on the main thread.
+ void composite();
+ void scheduleComposite();
+
+ // Composites and attempts to read back the result into the provided
+ // buffer. If it wasn't possible, e.g. due to context lost, will return
+ // false.
+ bool compositeAndReadback(void *pixels, const IntRect&);
+
+ void finishAllRendering();
+
+ int commitNumber() const { return m_commitNumber; }
+
+ void renderingStats(CCRenderingStats&) const;
+
+ const RendererCapabilities& rendererCapabilities() const;
+
+ // Test only hook
+ void loseContext(int numTimes);
+
+ void setNeedsAnimate();
+ // virtual for testing
+ virtual void setNeedsCommit();
+ void setNeedsRedraw();
+ bool commitRequested() const;
+
+ void setAnimationEvents(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime);
+ virtual void didAddAnimation();
+
+ LayerChromium* rootLayer() { return m_rootLayer.get(); }
+ const LayerChromium* rootLayer() const { return m_rootLayer.get(); }
+ void setRootLayer(PassRefPtr<LayerChromium>);
+
+ const CCLayerTreeSettings& settings() const { return m_settings; }
+
+ void setViewportSize(const IntSize& layoutViewportSize, const IntSize& deviceViewportSize);
+
+ const IntSize& layoutViewportSize() const { return m_layoutViewportSize; }
+ const IntSize& deviceViewportSize() const { return m_deviceViewportSize; }
+
+ void setPageScaleFactorAndLimits(float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor);
+
+ void setBackgroundColor(SkColor color) { m_backgroundColor = color; }
+
+ void setHasTransparentBackground(bool transparent) { m_hasTransparentBackground = transparent; }
+
+ CCPrioritizedTextureManager* contentsTextureManager() const;
+
+ // This will cause contents texture manager to evict all textures, but
+ // without deleting them. This happens after all content textures have
+ // already been deleted on impl, after getting a 0 allocation limit.
+ // Set during a commit, but before updateLayers.
+ void evictAllContentTextures();
+
+ bool visible() const { return m_visible; }
+ void setVisible(bool);
+
+ void startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double durationSec);
+
+ void applyScrollAndScale(const CCScrollAndScaleSet&);
+
+ void startRateLimiter(WebKit::WebGraphicsContext3D*);
+ void stopRateLimiter(WebKit::WebGraphicsContext3D*);
+
+ // RateLimitClient implementation
+ virtual void rateLimit() OVERRIDE;
+
+ bool bufferedUpdates();
+ bool requestPartialTextureUpdate();
+ void deleteTextureAfterCommit(PassOwnPtr<CCPrioritizedTexture>);
+
+ void setDeviceScaleFactor(float);
+ float deviceScaleFactor() const { return m_deviceScaleFactor; }
+
+ void setFontAtlas(PassOwnPtr<CCFontAtlas>);
+
+protected:
+ CCLayerTreeHost(CCLayerTreeHostClient*, const CCLayerTreeSettings&);
+ bool initialize();
+
+private:
+ typedef Vector<RefPtr<LayerChromium> > LayerList;
+ typedef Vector<OwnPtr<CCPrioritizedTexture> > TextureList;
+
+ void initializeRenderer();
+
+ void update(LayerChromium*, CCTextureUpdateQueue&, const CCOcclusionTracker*);
+ bool paintLayerContents(const LayerList&, CCTextureUpdateQueue&);
+ bool paintMasksForRenderSurface(LayerChromium*, CCTextureUpdateQueue&);
+
+ void updateLayers(LayerChromium*, CCTextureUpdateQueue&);
+
+ void prioritizeTextures(const LayerList&, CCOverdrawMetrics&);
+ void setPrioritiesForSurfaces(size_t surfaceMemoryBytes);
+ void setPrioritiesForLayers(const LayerList&);
+ size_t calculateMemoryForRenderSurfaces(const LayerList& updateList);
+
+ void animateLayers(double monotonicTime);
+ bool animateLayersRecursive(LayerChromium* current, double monotonicTime);
+ void setAnimationEventsRecursive(const CCAnimationEventsVector&, LayerChromium*, double wallClockTime);
+
+ int m_compositorIdentifier;
+
+ bool m_animating;
+ bool m_needsAnimateLayers;
+
+ CCLayerTreeHostClient* m_client;
+
+ int m_commitNumber;
+ CCRenderingStats m_renderingStats;
+
+ OwnPtr<CCProxy> m_proxy;
+ bool m_rendererInitialized;
+ bool m_contextLost;
+ int m_numTimesRecreateShouldFail;
+ int m_numFailedRecreateAttempts;
+
+ RefPtr<LayerChromium> m_rootLayer;
+ RefPtr<HeadsUpDisplayLayerChromium> m_hudLayer;
+ OwnPtr<CCFontAtlas> m_fontAtlas;
+
+ OwnPtr<CCPrioritizedTextureManager> m_contentsTextureManager;
+ OwnPtr<CCPrioritizedTexture> m_surfaceMemoryPlaceholder;
+
+ CCLayerTreeSettings m_settings;
+
+ IntSize m_layoutViewportSize;
+ IntSize m_deviceViewportSize;
+ float m_deviceScaleFactor;
+
+ bool m_visible;
+
+ typedef HashMap<WebKit::WebGraphicsContext3D*, RefPtr<RateLimiter> > RateLimiterMap;
+ RateLimiterMap m_rateLimiters;
+
+ float m_pageScaleFactor;
+ float m_minPageScaleFactor, m_maxPageScaleFactor;
+ bool m_triggerIdleUpdates;
+
+ SkColor m_backgroundColor;
+ bool m_hasTransparentBackground;
+
+ TextureList m_deleteTextureAfterCommitList;
+ size_t m_partialTextureUpdateRequests;
+
+ static bool s_needsFilterContext;
+};
+
+}
+
+#endif
diff --git a/cc/CCLayerTreeHostCommon.cpp b/cc/CCLayerTreeHostCommon.cpp
new file mode 100644
index 0000000..146f796
--- /dev/null
+++ b/cc/CCLayerTreeHostCommon.cpp
@@ -0,0 +1,888 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#include "CCLayerTreeHostCommon.h"
+
+#include "CCLayerImpl.h"
+#include "CCLayerIterator.h"
+#include "CCLayerSorter.h"
+#include "CCMathUtil.h"
+#include "CCRenderSurface.h"
+#include "FloatQuad.h"
+#include "IntRect.h"
+#include "LayerChromium.h"
+#include "RenderSurfaceChromium.h"
+#include <public/WebTransformationMatrix.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+IntRect CCLayerTreeHostCommon::calculateVisibleRect(const IntRect& targetSurfaceRect, const IntRect& layerBoundRect, const WebTransformationMatrix& transform)
+{
+ // Is this layer fully contained within the target surface?
+ IntRect layerInSurfaceSpace = CCMathUtil::mapClippedRect(transform, layerBoundRect);
+ if (targetSurfaceRect.contains(layerInSurfaceSpace))
+ return layerBoundRect;
+
+ // If the layer doesn't fill up the entire surface, then find the part of
+ // the surface rect where the layer could be visible. This avoids trying to
+ // project surface rect points that are behind the projection point.
+ IntRect minimalSurfaceRect = targetSurfaceRect;
+ minimalSurfaceRect.intersect(layerInSurfaceSpace);
+
+ // Project the corners of the target surface rect into the layer space.
+ // This bounding rectangle may be larger than it needs to be (being
+ // axis-aligned), but is a reasonable filter on the space to consider.
+ // Non-invertible transforms will create an empty rect here.
+ const WebTransformationMatrix surfaceToLayer = transform.inverse();
+ IntRect layerRect = enclosingIntRect(CCMathUtil::projectClippedRect(surfaceToLayer, FloatRect(minimalSurfaceRect)));
+ layerRect.intersect(layerBoundRect);
+ return layerRect;
+}
+
+template<typename LayerType>
+static inline bool layerIsInExisting3DRenderingContext(LayerType* layer)
+{
+ // According to current W3C spec on CSS transforms, a layer is part of an established
+ // 3d rendering context if its parent has transform-style of preserves-3d.
+ return layer->parent() && layer->parent()->preserves3D();
+}
+
+template<typename LayerType>
+static bool layerIsRootOfNewRenderingContext(LayerType* layer)
+{
+ // According to current W3C spec on CSS transforms (Section 6.1), a layer is the
+ // beginning of 3d rendering context if its parent does not have transform-style:
+ // preserve-3d, but this layer itself does.
+ if (layer->parent())
+ return !layer->parent()->preserves3D() && layer->preserves3D();
+
+ return layer->preserves3D();
+}
+
+template<typename LayerType>
+static bool isLayerBackFaceVisible(LayerType* layer)
+{
+ // The current W3C spec on CSS transforms says that backface visibility should be
+ // determined differently depending on whether the layer is in a "3d rendering
+ // context" or not. For Chromium code, we can determine whether we are in a 3d
+ // rendering context by checking if the parent preserves 3d.
+
+ if (layerIsInExisting3DRenderingContext(layer))
+ return layer->drawTransform().isBackFaceVisible();
+
+ // In this case, either the layer establishes a new 3d rendering context, or is not in
+ // a 3d rendering context at all.
+ return layer->transform().isBackFaceVisible();
+}
+
+template<typename LayerType>
+static bool isSurfaceBackFaceVisible(LayerType* layer, const WebTransformationMatrix& drawTransform)
+{
+ if (layerIsInExisting3DRenderingContext(layer))
+ return drawTransform.isBackFaceVisible();
+
+ if (layerIsRootOfNewRenderingContext(layer))
+ return layer->transform().isBackFaceVisible();
+
+ // If the renderSurface is not part of a new or existing rendering context, then the
+ // layers that contribute to this surface will decide back-face visibility for themselves.
+ return false;
+}
+
+template<typename LayerType>
+static inline bool layerClipsSubtree(LayerType* layer)
+{
+ return layer->masksToBounds() || layer->maskLayer();
+}
+
+template<typename LayerType>
+static IntRect calculateVisibleContentRect(LayerType* layer)
+{
+ ASSERT(layer->renderTarget());
+
+ IntRect targetSurfaceRect = layer->renderTarget()->renderSurface()->contentRect();
+
+ targetSurfaceRect.intersect(layer->drawableContentRect());
+
+ if (targetSurfaceRect.isEmpty() || layer->contentBounds().isEmpty())
+ return IntRect();
+
+ const IntRect contentRect = IntRect(IntPoint(), layer->contentBounds());
+ IntRect visibleContentRect = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, contentRect, layer->drawTransform());
+ return visibleContentRect;
+}
+
+static bool isScaleOrTranslation(const WebTransformationMatrix& m)
+{
+ return !m.m12() && !m.m13() && !m.m14()
+ && !m.m21() && !m.m23() && !m.m24()
+ && !m.m31() && !m.m32() && !m.m43()
+ && m.m44();
+}
+
+static inline bool transformToParentIsKnown(CCLayerImpl*)
+{
+ return true;
+}
+
+static inline bool transformToParentIsKnown(LayerChromium* layer)
+{
+ return !layer->transformIsAnimating();
+}
+
+static inline bool transformToScreenIsKnown(CCLayerImpl*)
+{
+ return true;
+}
+
+static inline bool transformToScreenIsKnown(LayerChromium* layer)
+{
+ return !layer->screenSpaceTransformIsAnimating();
+}
+
+template<typename LayerType>
+static bool layerShouldBeSkipped(LayerType* layer)
+{
+ // Layers can be skipped if any of these conditions are met.
+ // - does not draw content.
+ // - is transparent
+ // - has empty bounds
+ // - the layer is not double-sided, but its back face is visible.
+ //
+ // Some additional conditions need to be computed at a later point after the recursion is finished.
+ // - the intersection of render surface content and layer clipRect is empty
+ // - the visibleContentRect is empty
+ //
+ // Note, if the layer should not have been drawn due to being fully transparent,
+ // we would have skipped the entire subtree and never made it into this function,
+ // so it is safe to omit this check here.
+
+ if (!layer->drawsContent() || layer->bounds().isEmpty())
+ return true;
+
+ LayerType* backfaceTestLayer = layer;
+ if (layer->useParentBackfaceVisibility()) {
+ ASSERT(layer->parent());
+ ASSERT(!layer->parent()->useParentBackfaceVisibility());
+ backfaceTestLayer = layer->parent();
+ }
+
+ // The layer should not be drawn if (1) it is not double-sided and (2) the back of the layer is known to be facing the screen.
+ if (!backfaceTestLayer->doubleSided() && transformToScreenIsKnown(backfaceTestLayer) && isLayerBackFaceVisible(backfaceTestLayer))
+ return true;
+
+ return false;
+}
+
+static inline bool subtreeShouldBeSkipped(CCLayerImpl* layer)
+{
+ // The opacity of a layer always applies to its children (either implicitly
+ // via a render surface or explicitly if the parent preserves 3D), so the
+ // entire subtree can be skipped if this layer is fully transparent.
+ return !layer->opacity();
+}
+
+static inline bool subtreeShouldBeSkipped(LayerChromium* layer)
+{
+ // If the opacity is being animated then the opacity on the main thread is unreliable
+ // (since the impl thread may be using a different opacity), so it should not be trusted.
+ // In particular, it should not cause the subtree to be skipped.
+ return !layer->opacity() && !layer->opacityIsAnimating();
+}
+
+template<typename LayerType>
+static bool subtreeShouldRenderToSeparateSurface(LayerType* layer, bool axisAlignedWithRespectToParent)
+{
+ // The root layer has a special render surface that is set up externally, so
+ // it shouldn't be treated as a surface in this code.
+ if (!layer->parent())
+ return false;
+
+ // Cache this value, because otherwise it walks the entire subtree several times.
+ bool descendantDrawsContent = layer->descendantDrawsContent();
+
+ //
+ // A layer and its descendants should render onto a new RenderSurface if any of these rules hold:
+ //
+
+ // If we force it.
+ if (layer->forceRenderSurface())
+ return true;
+
+ // If the layer uses a mask.
+ if (layer->maskLayer())
+ return true;
+
+ // If the layer has a reflection.
+ if (layer->replicaLayer())
+ return true;
+
+ // If the layer uses a CSS filter.
+ if (!layer->filters().isEmpty() || !layer->backgroundFilters().isEmpty())
+ return true;
+
+ // If the layer flattens its subtree (i.e. the layer doesn't preserve-3d), but it is
+ // treated as a 3D object by its parent (i.e. parent does preserve-3d).
+ if (layerIsInExisting3DRenderingContext(layer) && !layer->preserves3D() && descendantDrawsContent)
+ return true;
+
+ // If the layer clips its descendants but it is not axis-aligned with respect to its parent.
+ if (layerClipsSubtree(layer) && !axisAlignedWithRespectToParent && descendantDrawsContent)
+ return true;
+
+ // If the layer has opacity != 1 and does not have a preserves-3d transform style.
+ if (layer->opacity() != 1 && !layer->preserves3D() && descendantDrawsContent)
+ return true;
+
+ return false;
+}
+
+WebTransformationMatrix computeScrollCompensationForThisLayer(CCLayerImpl* scrollingLayer, const WebTransformationMatrix& parentMatrix)
+{
+ // For every layer that has non-zero scrollDelta, we have to compute a transform that can undo the
+ // scrollDelta translation. In particular, we want this matrix to premultiply a fixed-position layer's
+ // parentMatrix, so we design this transform in three steps as follows. The steps described here apply
+ // from right-to-left, so Step 1 would be the right-most matrix:
+ //
+ // Step 1. transform from target surface space to the exact space where scrollDelta is actually applied.
+ // -- this is inverse of the matrix in step 3
+ // Step 2. undo the scrollDelta
+ // -- this is just a translation by scrollDelta.
+ // Step 3. transform back to target surface space.
+ // -- this transform is the "partialLayerOriginTransform" = (parentMatrix * scale(layer->pageScaleDelta()));
+ //
+ // These steps create a matrix that both start and end in targetSurfaceSpace. So this matrix can
+ // pre-multiply any fixed-position layer's drawTransform to undo the scrollDeltas -- as long as
+ // that fixed position layer is fixed onto the same renderTarget as this scrollingLayer.
+ //
+
+ WebTransformationMatrix partialLayerOriginTransform = parentMatrix;
+ partialLayerOriginTransform.scale(scrollingLayer->pageScaleDelta());
+
+ WebTransformationMatrix scrollCompensationForThisLayer = partialLayerOriginTransform; // Step 3
+ scrollCompensationForThisLayer.translate(scrollingLayer->scrollDelta().width(), scrollingLayer->scrollDelta().height()); // Step 2
+ scrollCompensationForThisLayer.multiply(partialLayerOriginTransform.inverse()); // Step 1
+ return scrollCompensationForThisLayer;
+}
+
+WebTransformationMatrix computeScrollCompensationMatrixForChildren(LayerChromium* currentLayer, const WebTransformationMatrix& currentParentMatrix, const WebTransformationMatrix& currentScrollCompensation)
+{
+ // The main thread (i.e. LayerChromium) does not need to worry about scroll compensation.
+ // So we can just return an identity matrix here.
+ return WebTransformationMatrix();
+}
+
+WebTransformationMatrix computeScrollCompensationMatrixForChildren(CCLayerImpl* layer, const WebTransformationMatrix& parentMatrix, const WebTransformationMatrix& currentScrollCompensationMatrix)
+{
+ // "Total scroll compensation" is the transform needed to cancel out all scrollDelta translations that
+ // occurred since the nearest container layer, even if there are renderSurfaces in-between.
+ //
+ // There are some edge cases to be aware of, that are not explicit in the code:
+ // - A layer that is both a fixed-position and container should not be its own container, instead, that means
+ // it is fixed to an ancestor, and is a container for any fixed-position descendants.
+ // - A layer that is a fixed-position container and has a renderSurface should behave the same as a container
+ // without a renderSurface, the renderSurface is irrelevant in that case.
+ // - A layer that does not have an explicit container is simply fixed to the viewport
+ // (i.e. the root renderSurface, and it would still compensate for root layer's scrollDelta).
+ // - If the fixed-position layer has its own renderSurface, then the renderSurface is
+ // the one who gets fixed.
+ //
+ // This function needs to be called AFTER layers create their own renderSurfaces.
+ //
+
+ // Avoid the overheads (including stack allocation and matrix initialization/copy) if we know that the scroll compensation doesn't need to be reset or adjusted.
+ if (!layer->isContainerForFixedPositionLayers() && layer->scrollDelta().isZero() && !layer->renderSurface())
+ return currentScrollCompensationMatrix;
+
+ // Start as identity matrix.
+ WebTransformationMatrix nextScrollCompensationMatrix;
+
+ // If this layer is not a container, then it inherits the existing scroll compensations.
+ if (!layer->isContainerForFixedPositionLayers())
+ nextScrollCompensationMatrix = currentScrollCompensationMatrix;
+
+ // If the current layer has a non-zero scrollDelta, then we should compute its local scrollCompensation
+ // and accumulate it to the nextScrollCompensationMatrix.
+ if (!layer->scrollDelta().isZero()) {
+ WebTransformationMatrix scrollCompensationForThisLayer = computeScrollCompensationForThisLayer(layer, parentMatrix);
+ nextScrollCompensationMatrix.multiply(scrollCompensationForThisLayer);
+ }
+
+ // If the layer created its own renderSurface, we have to adjust nextScrollCompensationMatrix.
+ // The adjustment allows us to continue using the scrollCompensation on the next surface.
+ // Step 1 (right-most in the math): transform from the new surface to the original ancestor surface
+ // Step 2: apply the scroll compensation
+ // Step 3: transform back to the new surface.
+ if (layer->renderSurface() && !nextScrollCompensationMatrix.isIdentity())
+ nextScrollCompensationMatrix = layer->renderSurface()->drawTransform().inverse() * nextScrollCompensationMatrix * layer->renderSurface()->drawTransform();
+
+ return nextScrollCompensationMatrix;
+}
+
+// Should be called just before the recursive calculateDrawTransformsInternal().
+template<typename LayerType, typename LayerList>
+void setupRootLayerAndSurfaceForRecursion(LayerType* rootLayer, LayerList& renderSurfaceLayerList, const IntSize& deviceViewportSize)
+{
+ if (!rootLayer->renderSurface())
+ rootLayer->createRenderSurface();
+
+ rootLayer->renderSurface()->setContentRect(IntRect(IntPoint::zero(), deviceViewportSize));
+ rootLayer->renderSurface()->clearLayerList();
+
+ ASSERT(renderSurfaceLayerList.isEmpty());
+ renderSurfaceLayerList.append(rootLayer);
+}
+
+// Recursively walks the layer tree starting at the given node and computes all the
+// necessary transformations, clipRects, render surfaces, etc.
+template<typename LayerType, typename LayerList, typename RenderSurfaceType, typename LayerSorter>
+static void calculateDrawTransformsInternal(LayerType* layer, LayerType* rootLayer, const WebTransformationMatrix& parentMatrix,
+ const WebTransformationMatrix& fullHierarchyMatrix, const WebTransformationMatrix& currentScrollCompensationMatrix,
+ const IntRect& clipRectFromAncestor, bool ancestorClipsSubtree,
+ RenderSurfaceType* nearestAncestorThatMovesPixels, LayerList& renderSurfaceLayerList, LayerList& layerList,
+ LayerSorter* layerSorter, int maxTextureSize, float deviceScaleFactor, IntRect& drawableContentRectOfSubtree)
+{
+ // This function computes the new matrix transformations recursively for this
+ // layer and all its descendants. It also computes the appropriate render surfaces.
+ // Some important points to remember:
+ //
+ // 0. Here, transforms are notated in Matrix x Vector order, and in words we describe what
+ // the transform does from left to right.
+ //
+ // 1. In our terminology, the "layer origin" refers to the top-left corner of a layer, and the
+ // positive Y-axis points downwards. This interpretation is valid because the orthographic
+ // projection applied at draw time flips the Y axis appropriately.
+ //
+ // 2. The anchor point, when given as a FloatPoint object, is specified in "unit layer space",
+ // where the bounds of the layer map to [0, 1]. However, as a WebTransformationMatrix object,
+ // the transform to the anchor point is specified in "pixel layer space", where the bounds
+ // of the layer map to [bounds.width(), bounds.height()].
+ //
+ // 3. Definition of various transforms used:
+ // M[parent] is the parent matrix, with respect to the nearest render surface, passed down recursively.
+ // M[root] is the full hierarchy, with respect to the root, passed down recursively.
+ // Tr[origin] is the translation matrix from the parent's origin to this layer's origin.
+ // Tr[origin2anchor] is the translation from the layer's origin to its anchor point
+ // Tr[origin2center] is the translation from the layer's origin to its center
+ // M[layer] is the layer's matrix (applied at the anchor point)
+ // M[sublayer] is the layer's sublayer transform (applied at the layer's center)
+ // Tr[anchor2center] is the translation offset from the anchor point and the center of the layer
+ // S[content2layer] is the ratio of a layer's contentBounds() to its bounds().
+ //
+ // Some shortcuts and substitutions are used in the code to reduce matrix multiplications:
+ // Tr[anchor2center] = Tr[origin2anchor].inverse() * Tr[origin2center]
+ //
+ // Some composite transforms can help in understanding the sequence of transforms:
+ // compositeLayerTransform = Tr[origin2anchor] * M[layer] * Tr[origin2anchor].inverse()
+ // compositeSublayerTransform = Tr[origin2center] * M[sublayer] * Tr[origin2center].inverse()
+ //
+ // In words, the layer transform is applied about the anchor point, and the sublayer transform is
+ // applied about the center of the layer.
+ //
+ // 4. When a layer (or render surface) is drawn, it is drawn into a "target render surface". Therefore the draw
+ // transform does not necessarily transform from screen space to local layer space. Instead, the draw transform
+ // is the transform between the "target render surface space" and local layer space. Note that render surfaces,
+ // except for the root, also draw themselves into a different target render surface, and so their draw
+ // transform and origin transforms are also described with respect to the target.
+ //
+ // Using these definitions, then:
+ //
+ // The draw transform for the layer is:
+ // M[draw] = M[parent] * Tr[origin] * compositeLayerTransform * S[content2layer]
+ // = M[parent] * Tr[layer->position()] * M[layer] * Tr[anchor2origin] * S[content2layer]
+ //
+ // Interpreting the math left-to-right, this transforms from the layer's render surface to the origin of the layer in content space.
+ //
+ // The screen space transform is:
+ // M[screenspace] = M[root] * Tr[origin] * compositeLayerTransform * S[content2layer]
+ // = M[root] * Tr[layer->position()] * M[layer] * Tr[origin2anchor].inverse() * S[content2layer]
+ //
+ // Interpreting the math left-to-right, this transforms from the root render surface's content space to the local layer's origin in layer space.
+ //
+ // The transform hierarchy that is passed on to children (i.e. the child's parentMatrix) is:
+ // M[parent]_for_child = M[parent] * Tr[origin] * compositeLayerTransform * compositeSublayerTransform
+ // = M[parent] * Tr[layer->position()] * M[layer] * Tr[anchor2center] * M[sublayer] * Tr[origin2center].inverse()
+ // = M[draw] * M[sublayer] * Tr[origin2center].inverse()
+ //
+ // and a similar matrix for the full hierarchy with respect to the root.
+ //
+ // Finally, note that the final matrix used by the shader for the layer is P * M[draw] * S . This final product
+ // is computed in drawTexturedQuad(), where:
+ // P is the projection matrix
+ // S is the scale adjustment (to scale up to the layer size)
+ //
+ // When a render surface has a replica layer, that layer's transform is used to draw a second copy of the surface.
+ // Transforms named here are relative to the surface, unless they specify they are relative to the replica layer.
+ //
+ // We will denote a scale by device scale S[deviceScale]
+ //
+ // The render surface draw transform to its target surface origin is:
+ // M[surfaceDraw] = M[owningLayer->Draw]
+ //
+ // The render surface origin transform to its the root (screen space) origin is:
+ // M[surface2root] = M[owningLayer->screenspace] * S[deviceScale].inverse()
+ //
+ // The replica draw transform to its target surface origin is:
+ // M[replicaDraw] = S[deviceScale] * M[surfaceDraw] * Tr[replica->position() + replica->anchor()] * Tr[replica] * Tr[origin2anchor].inverse() * S[contentsScale].inverse()
+ //
+ // The replica draw transform to the root (screen space) origin is:
+ // M[replica2root] = M[surface2root] * Tr[replica->position()] * Tr[replica] * Tr[origin2anchor].inverse()
+ //
+
+ // If we early-exit anywhere in this function, the drawableContentRect of this subtree should be considered empty.
+ drawableContentRectOfSubtree = IntRect();
+
+ if (subtreeShouldBeSkipped(layer))
+ return;
+
+ IntRect clipRectForSubtree;
+ bool subtreeShouldBeClipped = false;
+
+ float drawOpacity = layer->opacity();
+ bool drawOpacityIsAnimating = layer->opacityIsAnimating();
+ if (layer->parent() && layer->parent()->preserves3D()) {
+ drawOpacity *= layer->parent()->drawOpacity();
+ drawOpacityIsAnimating |= layer->parent()->drawOpacityIsAnimating();
+ }
+
+ IntSize bounds = layer->bounds();
+ FloatPoint anchorPoint = layer->anchorPoint();
+ FloatPoint position = layer->position() - layer->scrollDelta();
+
+ // Offset between anchor point and the center of the quad.
+ float centerOffsetX = (0.5 - anchorPoint.x()) * bounds.width();
+ float centerOffsetY = (0.5 - anchorPoint.y()) * bounds.height();
+
+ WebTransformationMatrix layerLocalTransform;
+ // LT = S[pageScaleDelta]
+ layerLocalTransform.scale(layer->pageScaleDelta());
+ // LT = S[pageScaleDelta] * Tr[origin] * Tr[origin2anchor]
+ layerLocalTransform.translate3d(position.x() + anchorPoint.x() * bounds.width(), position.y() + anchorPoint.y() * bounds.height(), layer->anchorPointZ());
+ // LT = S[pageScaleDelta] * Tr[origin] * Tr[origin2anchor] * M[layer]
+ layerLocalTransform.multiply(layer->transform());
+ // LT = S[pageScaleDelta] * Tr[origin] * Tr[origin2anchor] * M[layer] * Tr[anchor2center]
+ layerLocalTransform.translate3d(centerOffsetX, centerOffsetY, -layer->anchorPointZ());
+
+ WebTransformationMatrix combinedTransform = parentMatrix;
+ combinedTransform.multiply(layerLocalTransform);
+
+ if (layer->fixedToContainerLayer()) {
+ // Special case: this layer is a composited fixed-position layer; we need to
+ // explicitly compensate for all ancestors' nonzero scrollDeltas to keep this layer
+ // fixed correctly.
+ combinedTransform = currentScrollCompensationMatrix * combinedTransform;
+ }
+
+ // The drawTransform that gets computed below is effectively the layer's drawTransform, unless
+ // the layer itself creates a renderSurface. In that case, the renderSurface re-parents the transforms.
+ WebTransformationMatrix drawTransform = combinedTransform;
+ // M[draw] = M[parent] * LT * Tr[anchor2center] * Tr[center2origin]
+ drawTransform.translate(-layer->bounds().width() / 2.0, -layer->bounds().height() / 2.0);
+ if (!layer->contentBounds().isEmpty() && !layer->bounds().isEmpty()) {
+ // M[draw] = M[parent] * LT * Tr[anchor2origin] * S[layer2content]
+ drawTransform.scaleNonUniform(layer->bounds().width() / static_cast<double>(layer->contentBounds().width()),
+ layer->bounds().height() / static_cast<double>(layer->contentBounds().height()));
+ }
+
+ // layerScreenSpaceTransform represents the transform between root layer's "screen space" and local content space.
+ WebTransformationMatrix layerScreenSpaceTransform = fullHierarchyMatrix;
+ if (!layer->preserves3D())
+ CCMathUtil::flattenTransformTo2d(layerScreenSpaceTransform);
+ layerScreenSpaceTransform.multiply(drawTransform);
+ layer->setScreenSpaceTransform(layerScreenSpaceTransform);
+
+ bool animatingTransformToTarget = layer->transformIsAnimating();
+ bool animatingTransformToScreen = animatingTransformToTarget;
+ if (layer->parent()) {
+ animatingTransformToTarget |= layer->parent()->drawTransformIsAnimating();
+ animatingTransformToScreen |= layer->parent()->screenSpaceTransformIsAnimating();
+ }
+
+ FloatRect contentRect(FloatPoint(), layer->contentBounds());
+
+ // fullHierarchyMatrix is the matrix that transforms objects between screen space (except projection matrix) and the most recent RenderSurface's space.
+ // nextHierarchyMatrix will only change if this layer uses a new RenderSurface, otherwise remains the same.
+ WebTransformationMatrix nextHierarchyMatrix = fullHierarchyMatrix;
+ WebTransformationMatrix sublayerMatrix;
+
+ if (subtreeShouldRenderToSeparateSurface(layer, isScaleOrTranslation(combinedTransform))) {
+ // Check back-face visibility before continuing with this surface and its subtree
+ if (!layer->doubleSided() && transformToParentIsKnown(layer) && isSurfaceBackFaceVisible(layer, combinedTransform))
+ return;
+
+ if (!layer->renderSurface())
+ layer->createRenderSurface();
+
+ RenderSurfaceType* renderSurface = layer->renderSurface();
+ renderSurface->clearLayerList();
+
+ // The origin of the new surface is the upper left corner of the layer.
+ renderSurface->setDrawTransform(drawTransform);
+ WebTransformationMatrix layerDrawTransform;
+ layerDrawTransform.scale(deviceScaleFactor);
+ if (!layer->contentBounds().isEmpty() && !layer->bounds().isEmpty()) {
+ layerDrawTransform.scaleNonUniform(layer->bounds().width() / static_cast<double>(layer->contentBounds().width()),
+ layer->bounds().height() / static_cast<double>(layer->contentBounds().height()));
+ }
+ layer->setDrawTransform(layerDrawTransform);
+
+ // The sublayer matrix transforms centered layer rects into target
+ // surface content space.
+ sublayerMatrix.makeIdentity();
+ sublayerMatrix.scale(deviceScaleFactor);
+ sublayerMatrix.translate(0.5 * bounds.width(), 0.5 * bounds.height());
+
+ // The opacity value is moved from the layer to its surface, so that the entire subtree properly inherits opacity.
+ renderSurface->setDrawOpacity(drawOpacity);
+ renderSurface->setDrawOpacityIsAnimating(drawOpacityIsAnimating);
+ layer->setDrawOpacity(1);
+ layer->setDrawOpacityIsAnimating(false);
+
+ renderSurface->setTargetSurfaceTransformsAreAnimating(animatingTransformToTarget);
+ renderSurface->setScreenSpaceTransformsAreAnimating(animatingTransformToScreen);
+ animatingTransformToTarget = false;
+ layer->setDrawTransformIsAnimating(animatingTransformToTarget);
+ layer->setScreenSpaceTransformIsAnimating(animatingTransformToScreen);
+
+ // Update the aggregate hierarchy matrix to include the transform of the
+ // newly created RenderSurface.
+ nextHierarchyMatrix.multiply(renderSurface->drawTransform());
+
+ // The new renderSurface here will correctly clip the entire subtree. So, we do
+ // not need to continue propagating the clipping state further down the tree. This
+ // way, we can avoid transforming clipRects from ancestor target surface space to
+ // current target surface space that could cause more w < 0 headaches.
+ subtreeShouldBeClipped = false;
+
+ if (layer->maskLayer())
+ layer->maskLayer()->setRenderTarget(layer);
+
+ if (layer->replicaLayer() && layer->replicaLayer()->maskLayer())
+ layer->replicaLayer()->maskLayer()->setRenderTarget(layer);
+
+ if (layer->filters().hasFilterThatMovesPixels())
+ nearestAncestorThatMovesPixels = renderSurface;
+
+ renderSurface->setNearestAncestorThatMovesPixels(nearestAncestorThatMovesPixels);
+
+ renderSurfaceLayerList.append(layer);
+ } else {
+ layer->setDrawTransform(drawTransform);
+ layer->setDrawTransformIsAnimating(animatingTransformToTarget);
+ layer->setScreenSpaceTransformIsAnimating(animatingTransformToScreen);
+ sublayerMatrix = combinedTransform;
+
+ layer->setDrawOpacity(drawOpacity);
+ layer->setDrawOpacityIsAnimating(drawOpacityIsAnimating);
+
+ if (layer != rootLayer) {
+ ASSERT(layer->parent());
+ layer->clearRenderSurface();
+
+ // Layers without renderSurfaces directly inherit the ancestor's clip status.
+ subtreeShouldBeClipped = ancestorClipsSubtree;
+ if (ancestorClipsSubtree)
+ clipRectForSubtree = clipRectFromAncestor;
+
+ // Layers that are not their own renderTarget will render into the target of their nearest ancestor.
+ layer->setRenderTarget(layer->parent()->renderTarget());
+ } else {
+ // FIXME: This root layer special case code should eventually go away. But before that is truly possible,
+ // tests (or code) related to CCOcclusionTracker need to be adjusted so that they do not require
+ // the rootLayer to clip; the root layer's RenderSurface would already clip and should be enough.
+ ASSERT(!layer->parent());
+ ASSERT(layer->renderSurface());
+ ASSERT(ancestorClipsSubtree);
+ layer->renderSurface()->setClipRect(clipRectFromAncestor);
+ subtreeShouldBeClipped = true;
+ clipRectForSubtree = clipRectFromAncestor;
+ }
+ }
+
+ IntRect rectInTargetSpace = enclosingIntRect(CCMathUtil::mapClippedRect(layer->drawTransform(), contentRect));
+
+ if (layerClipsSubtree(layer)) {
+ subtreeShouldBeClipped = true;
+ if (ancestorClipsSubtree && !layer->renderSurface()) {
+ clipRectForSubtree = clipRectFromAncestor;
+ clipRectForSubtree.intersect(rectInTargetSpace);
+ } else
+ clipRectForSubtree = rectInTargetSpace;
+ }
+
+ // Flatten to 2D if the layer doesn't preserve 3D.
+ if (!layer->preserves3D())
+ CCMathUtil::flattenTransformTo2d(sublayerMatrix);
+
+ // Apply the sublayer transform at the center of the layer.
+ sublayerMatrix.multiply(layer->sublayerTransform());
+
+ // The coordinate system given to children is located at the layer's origin, not the center.
+ sublayerMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0);
+
+ LayerList& descendants = (layer->renderSurface() ? layer->renderSurface()->layerList() : layerList);
+
+ // Any layers that are appended after this point are in the layer's subtree and should be included in the sorting process.
+ unsigned sortingStartIndex = descendants.size();
+
+ if (!layerShouldBeSkipped(layer))
+ descendants.append(layer);
+
+ WebTransformationMatrix nextScrollCompensationMatrix = computeScrollCompensationMatrixForChildren(layer, parentMatrix, currentScrollCompensationMatrix);;
+
+ IntRect accumulatedDrawableContentRectOfChildren;
+ for (size_t i = 0; i < layer->children().size(); ++i) {
+ LayerType* child = layer->children()[i].get();
+ IntRect drawableContentRectOfChildSubtree;
+ calculateDrawTransformsInternal<LayerType, LayerList, RenderSurfaceType, LayerSorter>(child, rootLayer, sublayerMatrix, nextHierarchyMatrix, nextScrollCompensationMatrix,
+ clipRectForSubtree, subtreeShouldBeClipped, nearestAncestorThatMovesPixels,
+ renderSurfaceLayerList, descendants, layerSorter, maxTextureSize, deviceScaleFactor, drawableContentRectOfChildSubtree);
+ if (!drawableContentRectOfChildSubtree.isEmpty()) {
+ accumulatedDrawableContentRectOfChildren.unite(drawableContentRectOfChildSubtree);
+ if (child->renderSurface())
+ descendants.append(child);
+ }
+ }
+
+ // Compute the total drawableContentRect for this subtree (the rect is in targetSurface space)
+ IntRect localDrawableContentRectOfSubtree = accumulatedDrawableContentRectOfChildren;
+ if (layer->drawsContent())
+ localDrawableContentRectOfSubtree.unite(rectInTargetSpace);
+ if (subtreeShouldBeClipped)
+ localDrawableContentRectOfSubtree.intersect(clipRectForSubtree);
+
+ // Compute the layer's drawable content rect (the rect is in targetSurface space)
+ IntRect drawableContentRectOfLayer = rectInTargetSpace;
+ if (subtreeShouldBeClipped)
+ drawableContentRectOfLayer.intersect(clipRectForSubtree);
+ layer->setDrawableContentRect(drawableContentRectOfLayer);
+
+ // Compute the remaining properties for the render surface, if the layer has one.
+ if (layer->renderSurface() && layer != rootLayer) {
+ RenderSurfaceType* renderSurface = layer->renderSurface();
+ IntRect clippedContentRect = localDrawableContentRectOfSubtree;
+
+ // The render surface clipRect is expressed in the space where this surface draws, i.e. the same space as clipRectFromAncestor.
+ if (ancestorClipsSubtree)
+ renderSurface->setClipRect(clipRectFromAncestor);
+ else
+ renderSurface->setClipRect(IntRect());
+
+ // Don't clip if the layer is reflected as the reflection shouldn't be
+ // clipped. If the layer is animating, then the surface's transform to
+ // its target is not known on the main thread, and we should not use it
+ // to clip.
+ if (!layer->replicaLayer() && transformToParentIsKnown(layer)) {
+ // Note, it is correct to use ancestorClipsSubtree here, because we are looking at this layer's renderSurface, not the layer itself.
+ if (ancestorClipsSubtree && !clippedContentRect.isEmpty()) {
+ IntRect surfaceClipRect = CCLayerTreeHostCommon::calculateVisibleRect(renderSurface->clipRect(), clippedContentRect, renderSurface->drawTransform());
+ clippedContentRect.intersect(surfaceClipRect);
+ }
+ }
+
+ // The RenderSurface backing texture cannot exceed the maximum supported
+ // texture size.
+ clippedContentRect.setWidth(std::min(clippedContentRect.width(), maxTextureSize));
+ clippedContentRect.setHeight(std::min(clippedContentRect.height(), maxTextureSize));
+
+ if (clippedContentRect.isEmpty())
+ renderSurface->clearLayerList();
+
+ renderSurface->setContentRect(clippedContentRect);
+ renderSurface->setScreenSpaceTransform(layer->screenSpaceTransform());
+
+ if (layer->replicaLayer()) {
+ WebTransformationMatrix surfaceOriginToReplicaOriginTransform;
+ surfaceOriginToReplicaOriginTransform.scale(deviceScaleFactor);
+ surfaceOriginToReplicaOriginTransform.translate(layer->replicaLayer()->position().x() + layer->replicaLayer()->anchorPoint().x() * bounds.width(),
+ layer->replicaLayer()->position().y() + layer->replicaLayer()->anchorPoint().y() * bounds.height());
+ surfaceOriginToReplicaOriginTransform.multiply(layer->replicaLayer()->transform());
+ surfaceOriginToReplicaOriginTransform.translate(-layer->replicaLayer()->anchorPoint().x() * bounds.width(), -layer->replicaLayer()->anchorPoint().y() * bounds.height());
+ surfaceOriginToReplicaOriginTransform.scale(1 / deviceScaleFactor);
+
+ // Compute the replica's "originTransform" that maps from the replica's origin space to the target surface origin space.
+ WebTransformationMatrix replicaOriginTransform = layer->renderSurface()->drawTransform() * surfaceOriginToReplicaOriginTransform;
+ renderSurface->setReplicaDrawTransform(replicaOriginTransform);
+
+ // Compute the replica's "screenSpaceTransform" that maps from the replica's origin space to the screen's origin space.
+ WebTransformationMatrix replicaScreenSpaceTransform = layer->renderSurface()->screenSpaceTransform() * surfaceOriginToReplicaOriginTransform;
+ renderSurface->setReplicaScreenSpaceTransform(replicaScreenSpaceTransform);
+ }
+
+ // If a render surface has no layer list, then it and none of its children needed to get drawn.
+ if (!layer->renderSurface()->layerList().size()) {
+ // FIXME: Originally we asserted that this layer was already at the end of the
+ // list, and only needed to remove that layer. For now, we remove the
+ // entire subtree of surfaces to fix a crash bug. The root cause is
+ // https://bugs.webkit.org/show_bug.cgi?id=74147 and we should be able
+ // to put the original assert after fixing that.
+ while (renderSurfaceLayerList.last() != layer) {
+ renderSurfaceLayerList.last()->clearRenderSurface();
+ renderSurfaceLayerList.removeLast();
+ }
+ ASSERT(renderSurfaceLayerList.last() == layer);
+ renderSurfaceLayerList.removeLast();
+ layer->clearRenderSurface();
+ return;
+ }
+ }
+
+ // If neither this layer nor any of its children were added, early out.
+ if (sortingStartIndex == descendants.size())
+ return;
+
+ // If preserves-3d then sort all the descendants in 3D so that they can be
+ // drawn from back to front. If the preserves-3d property is also set on the parent then
+ // skip the sorting as the parent will sort all the descendants anyway.
+ if (descendants.size() && layer->preserves3D() && (!layer->parent() || !layer->parent()->preserves3D()))
+ sortLayers(&descendants.at(sortingStartIndex), descendants.end(), layerSorter);
+
+ if (layer->renderSurface())
+ drawableContentRectOfSubtree = enclosingIntRect(layer->renderSurface()->drawableContentRect());
+ else
+ drawableContentRectOfSubtree = localDrawableContentRectOfSubtree;
+
+ return;
+}
+
+// FIXME: Instead of using the following function to set visibility rects on a second
+// tree pass, revise calculateVisibleContentRect() so that this can be done in a single
+// pass inside calculateDrawTransformsInternal<>().
+template<typename LayerType, typename LayerList, typename RenderSurfaceType>
+static void calculateVisibleRectsInternal(const LayerList& renderSurfaceLayerList)
+{
+ // Use BackToFront since it's cheap and this isn't order-dependent.
+ typedef CCLayerIterator<LayerType, LayerList, RenderSurfaceType, CCLayerIteratorActions::BackToFront> CCLayerIteratorType;
+
+ CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
+ for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
+ if (it.representsTargetRenderSurface()) {
+ LayerType* maskLayer = it->maskLayer();
+ if (maskLayer)
+ maskLayer->setVisibleContentRect(IntRect(IntPoint(), it->contentBounds()));
+ LayerType* replicaMaskLayer = it->replicaLayer() ? it->replicaLayer()->maskLayer() : 0;
+ if (replicaMaskLayer)
+ replicaMaskLayer->setVisibleContentRect(IntRect(IntPoint(), it->contentBounds()));
+ } else if (it.representsItself()) {
+ IntRect visibleContentRect = calculateVisibleContentRect(*it);
+ it->setVisibleContentRect(visibleContentRect);
+ }
+ }
+}
+
+void CCLayerTreeHostCommon::calculateDrawTransforms(LayerChromium* rootLayer, const IntSize& deviceViewportSize, float deviceScaleFactor, int maxTextureSize, Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList)
+{
+ IntRect totalDrawableContentRect;
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix deviceScaleTransform;
+ deviceScaleTransform.scale(deviceScaleFactor);
+
+ setupRootLayerAndSurfaceForRecursion<LayerChromium, Vector<RefPtr<LayerChromium> > >(rootLayer, renderSurfaceLayerList, deviceViewportSize);
+
+ WebCore::calculateDrawTransformsInternal<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, void>(rootLayer, rootLayer, deviceScaleTransform, identityMatrix, identityMatrix,
+ rootLayer->renderSurface()->contentRect(), true, 0, renderSurfaceLayerList,
+ rootLayer->renderSurface()->layerList(), 0, maxTextureSize, deviceScaleFactor, totalDrawableContentRect);
+}
+
+void CCLayerTreeHostCommon::calculateDrawTransforms(CCLayerImpl* rootLayer, const IntSize& deviceViewportSize, float deviceScaleFactor, CCLayerSorter* layerSorter, int maxTextureSize, Vector<CCLayerImpl*>& renderSurfaceLayerList)
+{
+ IntRect totalDrawableContentRect;
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix deviceScaleTransform;
+ deviceScaleTransform.scale(deviceScaleFactor);
+
+ setupRootLayerAndSurfaceForRecursion<CCLayerImpl, Vector<CCLayerImpl*> >(rootLayer, renderSurfaceLayerList, deviceViewportSize);
+
+ WebCore::calculateDrawTransformsInternal<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerSorter>(rootLayer, rootLayer, deviceScaleTransform, identityMatrix, identityMatrix,
+ rootLayer->renderSurface()->contentRect(), true, 0, renderSurfaceLayerList,
+ rootLayer->renderSurface()->layerList(), layerSorter, maxTextureSize, deviceScaleFactor, totalDrawableContentRect);
+}
+
+void CCLayerTreeHostCommon::calculateVisibleRects(Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList)
+{
+ calculateVisibleRectsInternal<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium>(renderSurfaceLayerList);
+}
+
+void CCLayerTreeHostCommon::calculateVisibleRects(Vector<CCLayerImpl*>& renderSurfaceLayerList)
+{
+ calculateVisibleRectsInternal<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface>(renderSurfaceLayerList);
+}
+
+static bool pointHitsRect(const IntPoint& viewportPoint, const WebTransformationMatrix& localSpaceToScreenSpaceTransform, FloatRect localSpaceRect)
+{
+ // If the transform is not invertible, then assume that this point doesn't hit this rect.
+ if (!localSpaceToScreenSpaceTransform.isInvertible())
+ return false;
+
+ // Transform the hit test point from screen space to the local space of the given rect.
+ bool clipped = false;
+ FloatPoint hitTestPointInLocalSpace = CCMathUtil::projectPoint(localSpaceToScreenSpaceTransform.inverse(), FloatPoint(viewportPoint), clipped);
+
+ // If projectPoint could not project to a valid value, then we assume that this point doesn't hit this rect.
+ if (clipped)
+ return false;
+
+ return localSpaceRect.contains(hitTestPointInLocalSpace);
+}
+
+static bool pointIsClippedBySurfaceOrClipRect(const IntPoint& viewportPoint, CCLayerImpl* layer)
+{
+ CCLayerImpl* currentLayer = layer;
+
+ // Walk up the layer tree and hit-test any renderSurfaces and any layer clipRects that are active.
+ while (currentLayer) {
+ if (currentLayer->renderSurface() && !pointHitsRect(viewportPoint, currentLayer->renderSurface()->screenSpaceTransform(), currentLayer->renderSurface()->contentRect()))
+ return true;
+
+ // Note that drawableContentRects are actually in targetSurface space, so the transform we
+ // have to provide is the target surface's screenSpaceTransform.
+ CCLayerImpl* renderTarget = currentLayer->renderTarget();
+ if (layerClipsSubtree(currentLayer) && !pointHitsRect(viewportPoint, renderTarget->renderSurface()->screenSpaceTransform(), currentLayer->drawableContentRect()))
+ return true;
+
+ currentLayer = currentLayer->parent();
+ }
+
+ // If we have finished walking all ancestors without having already exited, then the point is not clipped by any ancestors.
+ return false;
+}
+
+CCLayerImpl* CCLayerTreeHostCommon::findLayerThatIsHitByPoint(const IntPoint& viewportPoint, Vector<CCLayerImpl*>& renderSurfaceLayerList)
+{
+ CCLayerImpl* foundLayer = 0;
+
+ typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
+ CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
+
+ for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
+ // We don't want to consider renderSurfaces for hit testing.
+ if (!it.representsItself())
+ continue;
+
+ CCLayerImpl* currentLayer = (*it);
+
+ FloatRect contentRect(FloatPoint::zero(), currentLayer->contentBounds());
+ if (!pointHitsRect(viewportPoint, currentLayer->screenSpaceTransform(), contentRect))
+ continue;
+
+ // At this point, we think the point does hit the layer, but we need to walk up
+ // the parents to ensure that the layer was not clipped in such a way that the
+ // hit point actually should not hit the layer.
+ if (pointIsClippedBySurfaceOrClipRect(viewportPoint, currentLayer))
+ continue;
+
+ foundLayer = currentLayer;
+ break;
+ }
+
+ // This can potentially return 0, which means the viewportPoint did not successfully hit test any layers, not even the root layer.
+ return foundLayer;
+}
+
+} // namespace WebCore
diff --git a/cc/CCLayerTreeHostCommon.h b/cc/CCLayerTreeHostCommon.h
new file mode 100644
index 0000000..b0c56b0
--- /dev/null
+++ b/cc/CCLayerTreeHostCommon.h
@@ -0,0 +1,85 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerTreeHostCommon_h
+#define CCLayerTreeHostCommon_h
+
+#include "IntRect.h"
+#include "IntSize.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCLayerImpl;
+class CCLayerSorter;
+class LayerChromium;
+
+class CCLayerTreeHostCommon {
+public:
+ static IntRect calculateVisibleRect(const IntRect& targetSurfaceRect, const IntRect& layerBoundRect, const WebKit::WebTransformationMatrix&);
+
+ static void calculateDrawTransforms(LayerChromium* rootLayer, const IntSize& deviceViewportSize, float deviceScaleFactor, int maxTextureSize, Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList);
+ static void calculateDrawTransforms(CCLayerImpl* rootLayer, const IntSize& deviceViewportSize, float deviceScaleFactor, CCLayerSorter*, int maxTextureSize, Vector<CCLayerImpl*>& renderSurfaceLayerList);
+
+ static void calculateVisibleRects(Vector<CCLayerImpl*>& renderSurfaceLayerList);
+ static void calculateVisibleRects(Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList);
+
+ // Performs hit testing for a given renderSurfaceLayerList.
+ static CCLayerImpl* findLayerThatIsHitByPoint(const IntPoint& viewportPoint, Vector<CCLayerImpl*>& renderSurfaceLayerList);
+
+ template<typename LayerType> static bool renderSurfaceContributesToTarget(LayerType*, int targetSurfaceLayerID);
+
+ // Returns a layer with the given id if one exists in the subtree starting
+ // from the given root layer (including mask and replica layers).
+ template<typename LayerType> static LayerType* findLayerInSubtree(LayerType* rootLayer, int layerId);
+
+ struct ScrollUpdateInfo {
+ int layerId;
+ IntSize scrollDelta;
+ };
+};
+
+struct CCScrollAndScaleSet {
+ Vector<CCLayerTreeHostCommon::ScrollUpdateInfo> scrolls;
+ float pageScaleDelta;
+};
+
+template<typename LayerType>
+bool CCLayerTreeHostCommon::renderSurfaceContributesToTarget(LayerType* layer, int targetSurfaceLayerID)
+{
+ // A layer will either contribute its own content, or its render surface's content, to
+ // the target surface. The layer contributes its surface's content when both the
+ // following are true:
+ // (1) The layer actually has a renderSurface, and
+ // (2) The layer's renderSurface is not the same as the targetSurface.
+ //
+ // Otherwise, the layer just contributes itself to the target surface.
+
+ return layer->renderSurface() && layer->id() != targetSurfaceLayerID;
+}
+
+template<typename LayerType>
+LayerType* CCLayerTreeHostCommon::findLayerInSubtree(LayerType* rootLayer, int layerId)
+{
+ if (rootLayer->id() == layerId)
+ return rootLayer;
+
+ if (rootLayer->maskLayer() && rootLayer->maskLayer()->id() == layerId)
+ return rootLayer->maskLayer();
+
+ if (rootLayer->replicaLayer() && rootLayer->replicaLayer()->id() == layerId)
+ return rootLayer->replicaLayer();
+
+ for (size_t i = 0; i < rootLayer->children().size(); ++i) {
+ if (LayerType* found = findLayerInSubtree(rootLayer->children()[i].get(), layerId))
+ return found;
+ }
+ return 0;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCLayerTreeHostCommonTest.cpp b/cc/CCLayerTreeHostCommonTest.cpp
new file mode 100644
index 0000000..2fef05d
--- /dev/null
+++ b/cc/CCLayerTreeHostCommonTest.cpp
@@ -0,0 +1,3247 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTreeHostCommon.h"
+
+#include "CCAnimationTestCommon.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerImpl.h"
+#include "CCLayerSorter.h"
+#include "CCLayerTreeTestCommon.h"
+#include "CCMathUtil.h"
+#include "CCProxy.h"
+#include "CCSingleThreadProxy.h"
+#include "CCThread.h"
+#include "ContentLayerChromium.h"
+#include "LayerChromium.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using namespace WebKitTests;
+using WebKit::WebTransformationMatrix;
+
+void WebKitTests::ExpectTransformationMatrixEq(WebTransformationMatrix expected,
+ WebTransformationMatrix actual)
+{
+ EXPECT_FLOAT_EQ((expected).m11(), (actual).m11());
+ EXPECT_FLOAT_EQ((expected).m12(), (actual).m12());
+ EXPECT_FLOAT_EQ((expected).m13(), (actual).m13());
+ EXPECT_FLOAT_EQ((expected).m14(), (actual).m14());
+ EXPECT_FLOAT_EQ((expected).m21(), (actual).m21());
+ EXPECT_FLOAT_EQ((expected).m22(), (actual).m22());
+ EXPECT_FLOAT_EQ((expected).m23(), (actual).m23());
+ EXPECT_FLOAT_EQ((expected).m24(), (actual).m24());
+ EXPECT_FLOAT_EQ((expected).m31(), (actual).m31());
+ EXPECT_FLOAT_EQ((expected).m32(), (actual).m32());
+ EXPECT_FLOAT_EQ((expected).m33(), (actual).m33());
+ EXPECT_FLOAT_EQ((expected).m34(), (actual).m34());
+ EXPECT_FLOAT_EQ((expected).m41(), (actual).m41());
+ EXPECT_FLOAT_EQ((expected).m42(), (actual).m42());
+ EXPECT_FLOAT_EQ((expected).m43(), (actual).m43());
+ EXPECT_FLOAT_EQ((expected).m44(), (actual).m44());
+}
+
+namespace {
+
+template<typename LayerType>
+void setLayerPropertiesForTesting(LayerType* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D)
+{
+ layer->setTransform(transform);
+ layer->setSublayerTransform(sublayerTransform);
+ layer->setAnchorPoint(anchor);
+ layer->setPosition(position);
+ layer->setBounds(bounds);
+ layer->setPreserves3D(preserves3D);
+}
+
+void setLayerPropertiesForTesting(LayerChromium* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D)
+{
+ setLayerPropertiesForTesting<LayerChromium>(layer, transform, sublayerTransform, anchor, position, bounds, preserves3D);
+}
+
+void setLayerPropertiesForTesting(CCLayerImpl* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D)
+{
+ setLayerPropertiesForTesting<CCLayerImpl>(layer, transform, sublayerTransform, anchor, position, bounds, preserves3D);
+ layer->setContentBounds(bounds);
+}
+
+void executeCalculateDrawTransformsAndVisibility(LayerChromium* rootLayer)
+{
+ WebTransformationMatrix identityMatrix;
+ Vector<RefPtr<LayerChromium> > dummyRenderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+
+ // We are probably not testing what is intended if the rootLayer bounds are empty.
+ ASSERT(!rootLayer->bounds().isEmpty());
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer, rootLayer->bounds(), 1, dummyMaxTextureSize, dummyRenderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(dummyRenderSurfaceLayerList);
+}
+
+void executeCalculateDrawTransformsAndVisibility(CCLayerImpl* rootLayer)
+{
+ // Note: this version skips layer sorting.
+
+ WebTransformationMatrix identityMatrix;
+ Vector<CCLayerImpl*> dummyRenderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+
+ // We are probably not testing what is intended if the rootLayer bounds are empty.
+ ASSERT(!rootLayer->bounds().isEmpty());
+ CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer, rootLayer->bounds(), 1, 0, dummyMaxTextureSize, dummyRenderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(dummyRenderSurfaceLayerList);
+}
+
+WebTransformationMatrix remove3DComponentOfMatrix(const WebTransformationMatrix& mat)
+{
+ WebTransformationMatrix ret = mat;
+ ret.setM13(0);
+ ret.setM23(0);
+ ret.setM31(0);
+ ret.setM32(0);
+ ret.setM33(1);
+ ret.setM34(0);
+ ret.setM43(0);
+ return ret;
+}
+
+PassOwnPtr<CCLayerImpl> createTreeForFixedPositionTests()
+{
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3);
+ OwnPtr<CCLayerImpl> greatGrandChild = CCLayerImpl::create(4);
+
+ WebTransformationMatrix IdentityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false);
+ setLayerPropertiesForTesting(child.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false);
+ setLayerPropertiesForTesting(grandChild.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false);
+ setLayerPropertiesForTesting(greatGrandChild.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false);
+
+ grandChild->addChild(greatGrandChild.release());
+ child->addChild(grandChild.release());
+ root->addChild(child.release());
+
+ return root.release();
+}
+
+class LayerChromiumWithForcedDrawsContent : public LayerChromium {
+public:
+ LayerChromiumWithForcedDrawsContent()
+ : LayerChromium()
+ {
+ }
+
+ virtual bool drawsContent() const OVERRIDE { return true; }
+};
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForNoOpLayer)
+{
+ // Sanity check: For layers positioned at zero, with zero size,
+ // and with identity transforms, then the drawTransform,
+ // screenSpaceTransform, and the hierarchy passed on to children
+ // layers should also be identity transforms.
+
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ parent->addChild(child);
+ child->addChild(grandChild);
+
+ WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(0, 0), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(0, 0), false);
+
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, grandChild->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleLayer)
+{
+ WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> layer = LayerChromium::create();
+
+ // Case 1: setting the sublayer transform should not affect this layer's draw transform or screen-space transform.
+ WebTransformationMatrix arbitraryTranslation;
+ arbitraryTranslation.translate(10, 20);
+ setLayerPropertiesForTesting(layer.get(), identityMatrix, arbitraryTranslation, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ WebTransformationMatrix expectedDrawTransform = identityMatrix;
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedDrawTransform, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, layer->screenSpaceTransform());
+
+ // Case 2: Setting the bounds of the layer should not affect either the draw transform or the screenspace transform.
+ WebTransformationMatrix translationToCenter;
+ translationToCenter.translate(5, 6);
+ setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, layer->screenSpaceTransform());
+
+ // Case 3: The anchor point by itself (without a layer transform) should have no effect on the transforms.
+ setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, layer->screenSpaceTransform());
+
+ // Case 4: A change in actual position affects both the draw transform and screen space transform.
+ WebTransformationMatrix positionTransform;
+ positionTransform.translate(0, 1.2);
+ setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 1.2f), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(positionTransform, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(positionTransform, layer->screenSpaceTransform());
+
+ // Case 5: In the correct sequence of transforms, the layer transform should pre-multiply the translationToCenter. This is easily tested by
+ // using a scale transform, because scale and translation are not commutative.
+ WebTransformationMatrix layerTransform;
+ layerTransform.scale3d(2, 2, 1);
+ setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(layerTransform, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(layerTransform, layer->screenSpaceTransform());
+
+ // Case 6: The layer transform should occur with respect to the anchor point.
+ WebTransformationMatrix translationToAnchor;
+ translationToAnchor.translate(5, 0);
+ WebTransformationMatrix expectedResult = translationToAnchor * layerTransform * translationToAnchor.inverse();
+ setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0.5, 0), FloatPoint(0, 0), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedResult, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedResult, layer->screenSpaceTransform());
+
+ // Case 7: Verify that position pre-multiplies the layer transform.
+ // The current implementation of calculateDrawTransforms does this implicitly, but it is
+ // still worth testing to detect accidental regressions.
+ expectedResult = positionTransform * translationToAnchor * layerTransform * translationToAnchor.inverse();
+ setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0.5, 0), FloatPoint(0, 1.2f), IntSize(10, 12), false);
+ executeCalculateDrawTransformsAndVisibility(layer.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedResult, layer->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedResult, layer->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForSimpleHierarchy)
+{
+ WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ parent->addChild(child);
+ child->addChild(grandChild);
+
+ // Case 1: parent's anchorPoint should not affect child or grandChild.
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(76, 78), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, grandChild->screenSpaceTransform());
+
+ // Case 2: parent's position affects child and grandChild.
+ WebTransformationMatrix parentPositionTransform;
+ parentPositionTransform.translate(0, 1.2);
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 1.2f), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(76, 78), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentPositionTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentPositionTransform, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentPositionTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentPositionTransform, grandChild->screenSpaceTransform());
+
+ // Case 3: parent's local transform affects child and grandchild
+ WebTransformationMatrix parentLayerTransform;
+ parentLayerTransform.scale3d(2, 2, 1);
+ WebTransformationMatrix parentTranslationToAnchor;
+ parentTranslationToAnchor.translate(2.5, 3);
+ WebTransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse();
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, identityMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(76, 78), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, grandChild->screenSpaceTransform());
+
+ // Case 4: parent's sublayerMatrix affects child and grandchild
+ // scaling is used here again so that the correct sequence of transforms is properly tested.
+ // Note that preserves3D is false, but the sublayer matrix should retain its 3D properties when given to child.
+ // But then, the child also does not preserve3D. When it gives its hierarchy to the grandChild, it should be flattened to 2D.
+ WebTransformationMatrix parentSublayerMatrix;
+ parentSublayerMatrix.scale3d(10, 10, 3.3);
+ WebTransformationMatrix parentTranslationToCenter;
+ parentTranslationToCenter.translate(5, 6);
+ // Sublayer matrix is applied to the center of the parent layer.
+ parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse()
+ * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse();
+ WebTransformationMatrix flattenedCompositeTransform = remove3DComponentOfMatrix(parentCompositeTransform);
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(76, 78), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(flattenedCompositeTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(flattenedCompositeTransform, grandChild->screenSpaceTransform());
+
+ // Case 5: same as Case 4, except that child does preserve 3D, so the grandChild should receive the non-flattened composite transform.
+ //
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), true);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(76, 78), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, grandChild->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleRenderSurface)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(child);
+ child->addChild(grandChild);
+
+ // Child is set up so that a new render surface should be created.
+ child->setOpacity(0.5);
+
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix parentLayerTransform;
+ parentLayerTransform.scale3d(1, 0.9, 1);
+ WebTransformationMatrix parentTranslationToAnchor;
+ parentTranslationToAnchor.translate(25, 30);
+ WebTransformationMatrix parentSublayerMatrix;
+ parentSublayerMatrix.scale3d(0.9, 1, 3.3);
+ WebTransformationMatrix parentTranslationToCenter;
+ parentTranslationToCenter.translate(50, 60);
+ WebTransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse()
+ * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse();
+
+ // Child's render surface should not exist yet.
+ ASSERT_FALSE(child->renderSurface());
+
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(100, 120), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(8, 10), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Render surface should have been created now.
+ ASSERT_TRUE(child->renderSurface());
+ ASSERT_EQ(child, child->renderTarget());
+
+ // The child layer's draw transform should refer to its new render surface.
+ // The screen-space transform, however, should still refer to the root.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->screenSpaceTransform());
+
+ // Because the grandChild is the only drawable content, the child's renderSurface will tighten its bounds to the grandChild.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->renderTarget()->renderSurface()->drawTransform());
+
+ // The screen space is the same as the target since the child surface draws into the root.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->renderTarget()->renderSurface()->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForReplica)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> childReplica = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(child);
+ child->addChild(grandChild);
+ child->setReplicaLayer(childReplica.get());
+
+ // Child is set up so that a new render surface should be created.
+ child->setOpacity(0.5);
+
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix parentLayerTransform;
+ parentLayerTransform.scale3d(2, 2, 1);
+ WebTransformationMatrix parentTranslationToAnchor;
+ parentTranslationToAnchor.translate(2.5, 3);
+ WebTransformationMatrix parentSublayerMatrix;
+ parentSublayerMatrix.scale3d(10, 10, 3.3);
+ WebTransformationMatrix parentTranslationToCenter;
+ parentTranslationToCenter.translate(5, 6);
+ WebTransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse()
+ * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse();
+ WebTransformationMatrix childTranslationToCenter;
+ childTranslationToCenter.translate(8, 9);
+ WebTransformationMatrix replicaLayerTransform;
+ replicaLayerTransform.scale3d(3, 3, 1);
+ WebTransformationMatrix replicaCompositeTransform = parentCompositeTransform * replicaLayerTransform;
+
+ // Child's render surface should not exist yet.
+ ASSERT_FALSE(child->renderSurface());
+
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25, 0.25), FloatPoint(0, 0), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(-0.5, -0.5), IntSize(1, 1), false);
+ setLayerPropertiesForTesting(childReplica.get(), replicaLayerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(0, 0), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Render surface should have been created now.
+ ASSERT_TRUE(child->renderSurface());
+ ASSERT_EQ(child, child->renderTarget());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->renderTarget()->renderSurface()->replicaDrawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->renderTarget()->renderSurface()->replicaScreenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
+{
+ // This test creates a more complex tree and verifies it all at once. This covers the following cases:
+ // - layers that are described w.r.t. a render surface: should have draw transforms described w.r.t. that surface
+ // - A render surface described w.r.t. an ancestor render surface: should have a draw transform described w.r.t. that ancestor surface
+ // - Replicas of a render surface are described w.r.t. the replica's transform around its anchor, along with the surface itself.
+ // - Sanity check on recursion: verify transforms of layers described w.r.t. a render surface that is described w.r.t. an ancestor render surface.
+ // - verifying that each layer has a reference to the correct renderSurface and renderTarget values.
+
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface2 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRoot = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS1 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS2 = LayerChromium::create();
+ RefPtr<LayerChromium> replicaOfRS1 = LayerChromium::create();
+ RefPtr<LayerChromium> replicaOfRS2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(renderSurface1);
+ parent->addChild(childOfRoot);
+ renderSurface1->addChild(childOfRS1);
+ renderSurface1->addChild(renderSurface2);
+ renderSurface2->addChild(childOfRS2);
+ childOfRoot->addChild(grandChildOfRoot);
+ childOfRS1->addChild(grandChildOfRS1);
+ childOfRS2->addChild(grandChildOfRS2);
+ renderSurface1->setReplicaLayer(replicaOfRS1.get());
+ renderSurface2->setReplicaLayer(replicaOfRS2.get());
+
+ // In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface.
+ renderSurface1->setOpacity(0.5);
+ renderSurface2->setOpacity(0.33f);
+
+ // All layers in the tree are initialized with an anchor at .25 and a size of (10,10).
+ // matrix "A" is the composite layer transform used in all layers, centered about the anchor point
+ // matrix "B" is the sublayer transform used in all layers, centered about the center position of the layer.
+ // matrix "R" is the composite replica transform used in all replica layers.
+ //
+ // x component tests that layerTransform and sublayerTransform are done in the right order (translation and scale are noncommutative).
+ // y component has a translation by 1 for every ancestor, which indicates the "depth" of the layer in the hierarchy.
+ WebTransformationMatrix translationToAnchor;
+ translationToAnchor.translate(2.5, 0);
+ WebTransformationMatrix translationToCenter;
+ translationToCenter.translate(5, 5);
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(1, 1);
+ WebTransformationMatrix sublayerTransform;
+ sublayerTransform.scale3d(10, 1, 1);
+ WebTransformationMatrix replicaLayerTransform;
+ replicaLayerTransform.scale3d(-2, 5, 1);
+
+ WebTransformationMatrix A = translationToAnchor * layerTransform * translationToAnchor.inverse();
+ WebTransformationMatrix B = translationToCenter * sublayerTransform * translationToCenter.inverse();
+ WebTransformationMatrix R = A * translationToAnchor * replicaLayerTransform * translationToAnchor.inverse();
+ WebTransformationMatrix identityMatrix;
+
+ setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(replicaOfRS1.get(), replicaLayerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(), false);
+ setLayerPropertiesForTesting(replicaOfRS2.get(), replicaLayerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(0, 0), IntSize(), false);
+
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Only layers that are associated with render surfaces should have an actual renderSurface() value.
+ //
+ ASSERT_TRUE(parent->renderSurface());
+ ASSERT_FALSE(childOfRoot->renderSurface());
+ ASSERT_FALSE(grandChildOfRoot->renderSurface());
+
+ ASSERT_TRUE(renderSurface1->renderSurface());
+ ASSERT_FALSE(childOfRS1->renderSurface());
+ ASSERT_FALSE(grandChildOfRS1->renderSurface());
+
+ ASSERT_TRUE(renderSurface2->renderSurface());
+ ASSERT_FALSE(childOfRS2->renderSurface());
+ ASSERT_FALSE(grandChildOfRS2->renderSurface());
+
+ // Verify all renderTarget accessors
+ //
+ EXPECT_EQ(parent, parent->renderTarget());
+ EXPECT_EQ(parent, childOfRoot->renderTarget());
+ EXPECT_EQ(parent, grandChildOfRoot->renderTarget());
+
+ EXPECT_EQ(renderSurface1, renderSurface1->renderTarget());
+ EXPECT_EQ(renderSurface1, childOfRS1->renderTarget());
+ EXPECT_EQ(renderSurface1, grandChildOfRS1->renderTarget());
+
+ EXPECT_EQ(renderSurface2, renderSurface2->renderTarget());
+ EXPECT_EQ(renderSurface2, childOfRS2->renderTarget());
+ EXPECT_EQ(renderSurface2, grandChildOfRS2->renderTarget());
+
+ // Verify layer draw transforms
+ // note that draw transforms are described with respect to the nearest ancestor render surface
+ // but screen space transforms are described with respect to the root.
+ //
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A, parent->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, childOfRoot->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, grandChildOfRoot->drawTransform());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, renderSurface1->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, childOfRS1->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * A * B * A, grandChildOfRS1->drawTransform());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, renderSurface2->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, childOfRS2->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * A * B * A, grandChildOfRS2->drawTransform());
+
+ // Verify layer screen-space transforms
+ //
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A, parent->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, childOfRoot->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, grandChildOfRoot->screenSpaceTransform());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, childOfRS1->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A * B * A, grandChildOfRS1->screenSpaceTransform());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, renderSurface2->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A * B * A, childOfRS2->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A * B * A * B * A, grandChildOfRS2->screenSpaceTransform());
+
+ // Verify render surface transforms.
+ //
+ // Draw transform of render surface 1 is described with respect to root.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaDrawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaScreenSpaceTransform());
+ // Draw transform of render surface 2 is described with respect to render surface 2.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, renderSurface2->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * R, renderSurface2->renderSurface()->replicaDrawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, renderSurface2->renderSurface()->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * R, renderSurface2->renderSurface()->replicaScreenSpaceTransform());
+
+ // Sanity check. If these fail there is probably a bug in the test itself.
+ // It is expected that we correctly set up transforms so that the y-component of the screen-space transform
+ // encodes the "depth" of the layer in the tree.
+ EXPECT_FLOAT_EQ(1, parent->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(2, childOfRoot->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3, grandChildOfRoot->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(2, renderSurface1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3, childOfRS1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4, grandChildOfRS1->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(3, renderSurface2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4, childOfRS2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(5, grandChildOfRS2->screenSpaceTransform().m42());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForFlatteningLayer)
+{
+ // For layers that flatten their subtree, there should be an orthographic projection
+ // (for x and y values) in the middle of the transform sequence. Note that the way the
+ // code is currently implemented, it is not expected to use a canonical orthographic
+ // projection.
+
+ RefPtr<LayerChromium> root = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ WebTransformationMatrix rotationAboutYAxis;
+ rotationAboutYAxis.rotate3d(0, 30, 0);
+
+ const WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child.get(), rotationAboutYAxis, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild.get(), rotationAboutYAxis, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+
+ root->addChild(child);
+ child->addChild(grandChild);
+ child->setForceRenderSurface(true);
+
+ // No layers in this test should preserve 3d.
+ ASSERT_FALSE(root->preserves3D());
+ ASSERT_FALSE(child->preserves3D());
+ ASSERT_FALSE(grandChild->preserves3D());
+
+ WebTransformationMatrix expectedChildDrawTransform = rotationAboutYAxis;
+ WebTransformationMatrix expectedChildScreenSpaceTransform = rotationAboutYAxis;
+ WebTransformationMatrix expectedGrandChildDrawTransform = rotationAboutYAxis; // draws onto child's renderSurface
+ WebTransformationMatrix expectedGrandChildScreenSpaceTransform = rotationAboutYAxis.to2dTransform() * rotationAboutYAxis;
+
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // The child's drawTransform should have been taken by its surface.
+ ASSERT_TRUE(child->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildDrawTransform, child->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildScreenSpaceTransform, child->renderSurface()->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildScreenSpaceTransform, child->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildDrawTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildScreenSpaceTransform, grandChild->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForDegenerateIntermediateLayer)
+{
+ // A layer that is empty in one axis, but not the other, was accidentally skipping a necessary translation.
+ // Without that translation, the coordinate space of the layer's drawTransform is incorrect.
+ //
+ // Normally this isn't a problem, because the layer wouldn't be drawn anyway, but if that layer becomes a renderSurface, then
+ // its drawTransform is implicitly inherited by the rest of the subtree, which then is positioned incorrectly as a result.
+
+ RefPtr<LayerChromium> root = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ // The child height is zero, but has non-zero width that should be accounted for while computing drawTransforms.
+ const WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 0), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+
+ root->addChild(child);
+ child->addChild(grandChild);
+ child->setForceRenderSurface(true);
+
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ ASSERT_TRUE(child->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->renderSurface()->drawTransform()); // This is the real test, the rest are sanity checks.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(identityMatrix, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForRenderSurfaceWithClippedLayer)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ const WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface1.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint(30, 30), IntSize(10, 10), false);
+
+ parent->addChild(renderSurface1);
+ renderSurface1->addChild(child);
+ renderSurface1->setForceRenderSurface(true);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // The child layer's content is entirely outside the parent's clip rect, so the intermediate
+ // render surface should not be listed here, even if it was forced to be created. Render surfaces without children or visible
+ // content are unexpected at draw time (e.g. we might try to create a content texture of size 0).
+ ASSERT_TRUE(parent->renderSurface());
+ ASSERT_FALSE(renderSurface1->renderSurface());
+ EXPECT_EQ(1U, renderSurfaceLayerList.size());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForTransparentChild)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ const WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(renderSurface1.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+
+ parent->addChild(renderSurface1);
+ renderSurface1->addChild(child);
+ renderSurface1->setForceRenderSurface(true);
+ renderSurface1->setOpacity(0);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Since the layer is transparent, renderSurface1->renderSurface() should not have gotten added anywhere.
+ // Also, the drawable content rect should not have been extended by the children.
+ ASSERT_TRUE(parent->renderSurface());
+ EXPECT_EQ(0U, parent->renderSurface()->layerList().size());
+ EXPECT_EQ(1U, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(IntRect(), parent->drawableContentRect());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyForceRenderSurface)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ renderSurface1->setForceRenderSurface(true);
+
+ const WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface1.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
+
+ parent->addChild(renderSurface1);
+ renderSurface1->addChild(child);
+
+ // Sanity check before the actual test
+ EXPECT_FALSE(parent->renderSurface());
+ EXPECT_FALSE(renderSurface1->renderSurface());
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // The root layer always creates a renderSurface
+ EXPECT_TRUE(parent->renderSurface());
+ EXPECT_TRUE(renderSurface1->renderSurface());
+ EXPECT_EQ(2U, renderSurfaceLayerList.size());
+
+ renderSurfaceLayerList.clear();
+ renderSurface1->setForceRenderSurface(false);
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ EXPECT_TRUE(parent->renderSurface());
+ EXPECT_FALSE(renderSurface1->renderSurface());
+ EXPECT_EQ(1U, renderSurfaceLayerList.size());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDirectContainer)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // is the direct parent of the fixed-position layer.
+
+ DebugScopedSetImplThread scopedImplThread;
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setFixedToContainerLayer(true);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ WebTransformationMatrix expectedGrandChildTransform = expectedChildTransform;
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 10
+ child->setScrollDelta(IntSize(10, 10));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -10);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithTransformedDirectContainer)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // is the direct parent of the fixed-position layer, but that container is transformed.
+ // In this case, the fixed position element inherits the container's transform,
+ // but the scrollDelta that has to be undone should not be affected by that transform.
+ //
+ // Transforms are in general non-commutative; using something like a non-uniform scale
+ // helps to verify that translations and non-uniform scales are applied in the correct
+ // order.
+
+ DebugScopedSetImplThread scopedImplThread;
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ // This scale will cause child and grandChild to be effectively 200 x 800 with respect to the renderTarget.
+ WebTransformationMatrix nonUniformScale;
+ nonUniformScale.scaleNonUniform(2, 8);
+ child->setTransform(nonUniformScale);
+
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setFixedToContainerLayer(true);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ expectedChildTransform.multiply(nonUniformScale);
+
+ WebTransformationMatrix expectedGrandChildTransform = expectedChildTransform;
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 20
+ child->setScrollDelta(IntSize(10, 20));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // The child should be affected by scrollDelta, but the fixed position grandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -20); // scrollDelta
+ expectedChildTransform.multiply(nonUniformScale);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDistantContainer)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // is NOT the direct parent of the fixed-position layer.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ CCLayerImpl* greatGrandChild = grandChild->children()[0].get();
+
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setPosition(FloatPoint(8, 6));
+ greatGrandChild->setFixedToContainerLayer(true);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ WebTransformationMatrix expectedGrandChildTransform;
+ expectedGrandChildTransform.translate(8, 6);
+
+ WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform;
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 10
+ child->setScrollDelta(IntSize(10, 10));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -10);
+ expectedGrandChildTransform.makeIdentity();
+ expectedGrandChildTransform.translate(-2, -4);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDistantContainerAndTransforms)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // is NOT the direct parent of the fixed-position layer, and the hierarchy has various
+ // transforms that have to be processed in the correct order.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ CCLayerImpl* greatGrandChild = grandChild->children()[0].get();
+
+ WebTransformationMatrix rotationAboutZ;
+ rotationAboutZ.rotate3d(0, 0, 90);
+
+ child->setIsContainerForFixedPositionLayers(true);
+ child->setTransform(rotationAboutZ);
+ grandChild->setPosition(FloatPoint(8, 6));
+ grandChild->setTransform(rotationAboutZ);
+ greatGrandChild->setFixedToContainerLayer(true); // greatGrandChild is positioned upside-down with respect to the renderTarget.
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ expectedChildTransform.multiply(rotationAboutZ);
+
+ WebTransformationMatrix expectedGrandChildTransform;
+ expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited
+ expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform.
+ expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform
+
+ WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform;
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 20
+ child->setScrollDelta(IntSize(10, 20));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -20); // scrollDelta
+ expectedChildTransform.multiply(rotationAboutZ);
+
+ expectedGrandChildTransform.makeIdentity();
+ expectedGrandChildTransform.translate(-10, -20); // child's scrollDelta is inherited
+ expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited
+ expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform.
+ expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithMultipleScrollDeltas)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // has multiple ancestors that have nonzero scrollDelta before reaching the space where the layer is fixed.
+ // In this test, each scrollDelta occurs in a different space because of each layer's local transform.
+ // This test checks for correct scroll compensation when the fixed-position container
+ // is NOT the direct parent of the fixed-position layer, and the hierarchy has various
+ // transforms that have to be processed in the correct order.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ CCLayerImpl* greatGrandChild = grandChild->children()[0].get();
+
+ WebTransformationMatrix rotationAboutZ;
+ rotationAboutZ.rotate3d(0, 0, 90);
+
+ child->setIsContainerForFixedPositionLayers(true);
+ child->setTransform(rotationAboutZ);
+ grandChild->setPosition(FloatPoint(8, 6));
+ grandChild->setTransform(rotationAboutZ);
+ greatGrandChild->setFixedToContainerLayer(true); // greatGrandChild is positioned upside-down with respect to the renderTarget.
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ expectedChildTransform.multiply(rotationAboutZ);
+
+ WebTransformationMatrix expectedGrandChildTransform;
+ expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited
+ expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform.
+ expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform
+
+ WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform;
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 20
+ child->setScrollDelta(IntSize(10, 0));
+ grandChild->setScrollDelta(IntSize(5, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, 0); // scrollDelta
+ expectedChildTransform.multiply(rotationAboutZ);
+
+ expectedGrandChildTransform.makeIdentity();
+ expectedGrandChildTransform.translate(-10, 0); // child's scrollDelta is inherited
+ expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited
+ expectedGrandChildTransform.translate(-5, 0); // grandChild's scrollDelta
+ expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform.
+ expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithIntermediateSurfaceAndTransforms)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // contributes to a different renderSurface than the fixed-position layer. In this
+ // case, the surface drawTransforms also have to be accounted for when checking the
+ // scrollDelta.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ CCLayerImpl* greatGrandChild = grandChild->children()[0].get();
+
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setPosition(FloatPoint(8, 6));
+ grandChild->setForceRenderSurface(true);
+ greatGrandChild->setFixedToContainerLayer(true);
+ greatGrandChild->setDrawsContent(true);
+
+ WebTransformationMatrix rotationAboutZ;
+ rotationAboutZ.rotate3d(0, 0, 90);
+ grandChild->setTransform(rotationAboutZ);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ WebTransformationMatrix expectedSurfaceDrawTransform;
+ expectedSurfaceDrawTransform.translate(8, 6);
+ expectedSurfaceDrawTransform.multiply(rotationAboutZ);
+ WebTransformationMatrix expectedGrandChildTransform;
+ WebTransformationMatrix expectedGreatGrandChildTransform;
+ ASSERT_TRUE(grandChild->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceDrawTransform, grandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 30
+ child->setScrollDelta(IntSize(10, 30));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the grandChild remains unchanged, because it scrolls along with the
+ // renderSurface, and the translation is actually in the renderSurface. But, the fixed
+ // position greatGrandChild is more awkward: its actually being drawn with respect to
+ // the renderSurface, but it needs to remain fixed with resepct to a container beyond
+ // that surface. So, the net result is that, unlike previous tests where the fixed
+ // position layer's transform remains unchanged, here the fixed position layer's
+ // transform explicitly contains the translation that cancels out the scroll.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -30); // scrollDelta
+
+ expectedSurfaceDrawTransform.makeIdentity();
+ expectedSurfaceDrawTransform.translate(-10, -30); // scrollDelta
+ expectedSurfaceDrawTransform.translate(8, 6);
+ expectedSurfaceDrawTransform.multiply(rotationAboutZ);
+
+ // The rotation and its inverse are needed to place the scrollDelta compensation in
+ // the correct space. This test will fail if the rotation/inverse are backwards, too,
+ // so it requires perfect order of operations.
+ expectedGreatGrandChildTransform.makeIdentity();
+ expectedGreatGrandChildTransform.multiply(rotationAboutZ.inverse());
+ expectedGreatGrandChildTransform.translate(10, 30); // explicit canceling out the scrollDelta that gets embedded in the fixed position layer's surface.
+ expectedGreatGrandChildTransform.multiply(rotationAboutZ);
+
+ ASSERT_TRUE(grandChild->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceDrawTransform, grandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithMultipleIntermediateSurfaces)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // contributes to a different renderSurface than the fixed-position layer, with
+ // additional renderSurfaces in-between. This checks that the conversion to ancestor
+ // surfaces is accumulated properly in the final matrix transform.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ CCLayerImpl* greatGrandChild = grandChild->children()[0].get();
+
+ // Add one more layer to the test tree for this scenario.
+ {
+ WebTransformationMatrix identity;
+ OwnPtr<CCLayerImpl> fixedPositionChild = CCLayerImpl::create(5);
+ setLayerPropertiesForTesting(fixedPositionChild.get(), identity, identity, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ greatGrandChild->addChild(fixedPositionChild.release());
+ }
+ CCLayerImpl* fixedPositionChild = greatGrandChild->children()[0].get();
+
+ // Actually set up the scenario here.
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setPosition(FloatPoint(8, 6));
+ grandChild->setForceRenderSurface(true);
+ greatGrandChild->setPosition(FloatPoint(40, 60));
+ greatGrandChild->setForceRenderSurface(true);
+ fixedPositionChild->setFixedToContainerLayer(true);
+ fixedPositionChild->setDrawsContent(true);
+
+ // The additional rotations, which are non-commutative with translations, help to
+ // verify that we have correct order-of-operations in the final scroll compensation.
+ // Note that rotating about the center of the layer ensures we do not accidentally
+ // clip away layers that we want to test.
+ WebTransformationMatrix rotationAboutZ;
+ rotationAboutZ.translate(50, 50);
+ rotationAboutZ.rotate3d(0, 0, 90);
+ rotationAboutZ.translate(-50, -50);
+ grandChild->setTransform(rotationAboutZ);
+ greatGrandChild->setTransform(rotationAboutZ);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+
+ WebTransformationMatrix expectedGrandChildSurfaceDrawTransform;
+ expectedGrandChildSurfaceDrawTransform.translate(8, 6);
+ expectedGrandChildSurfaceDrawTransform.multiply(rotationAboutZ);
+
+ WebTransformationMatrix expectedGrandChildTransform;
+
+ WebTransformationMatrix expectedGreatGrandChildSurfaceDrawTransform;
+ expectedGreatGrandChildSurfaceDrawTransform.translate(40, 60);
+ expectedGreatGrandChildSurfaceDrawTransform.multiply(rotationAboutZ);
+
+ WebTransformationMatrix expectedGreatGrandChildTransform;
+
+ WebTransformationMatrix expectedFixedPositionChildTransform;
+
+ ASSERT_TRUE(grandChild->renderSurface());
+ ASSERT_TRUE(greatGrandChild->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildSurfaceDrawTransform, grandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildSurfaceDrawTransform, greatGrandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedFixedPositionChildTransform, fixedPositionChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 30
+ child->setScrollDelta(IntSize(10, 30));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -30); // scrollDelta
+
+ expectedGrandChildSurfaceDrawTransform.makeIdentity();
+ expectedGrandChildSurfaceDrawTransform.translate(-10, -30); // scrollDelta
+ expectedGrandChildSurfaceDrawTransform.translate(8, 6);
+ expectedGrandChildSurfaceDrawTransform.multiply(rotationAboutZ);
+
+ // grandChild, greatGrandChild, and greatGrandChild's surface are not expected to
+ // change, since they are all not fixed, and they are all drawn with respect to
+ // grandChild's surface that already has the scrollDelta accounted for.
+
+ // But the great-great grandchild, "fixedPositionChild", should have a transform that explicitly cancels out the scrollDelta.
+ // The expected transform is:
+ // compoundDrawTransform.inverse() * translate(positive scrollDelta) * compoundOriginTransform
+ WebTransformationMatrix compoundDrawTransform; // transform from greatGrandChildSurface's origin to the root surface.
+ compoundDrawTransform.translate(8, 6); // origin translation of grandChild
+ compoundDrawTransform.multiply(rotationAboutZ); // rotation of grandChild
+ compoundDrawTransform.translate(40, 60); // origin translation of greatGrandChild
+ compoundDrawTransform.multiply(rotationAboutZ); // rotation of greatGrandChild
+
+ expectedFixedPositionChildTransform.makeIdentity();
+ expectedFixedPositionChildTransform.multiply(compoundDrawTransform.inverse());
+ expectedFixedPositionChildTransform.translate(10, 30); // explicit canceling out the scrollDelta that gets embedded in the fixed position layer's surface.
+ expectedFixedPositionChildTransform.multiply(compoundDrawTransform);
+
+ ASSERT_TRUE(grandChild->renderSurface());
+ ASSERT_TRUE(greatGrandChild->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildSurfaceDrawTransform, grandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildSurfaceDrawTransform, greatGrandChild->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedFixedPositionChildTransform, fixedPositionChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithContainerLayerThatHasSurface)
+{
+ // This test checks for correct scroll compensation when the fixed-position container
+ // itself has a renderSurface. In this case, the container layer should be treated
+ // like a layer that contributes to a renderTarget, and that renderTarget
+ // is completely irrelevant; it should not affect the scroll compensation.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ child->setIsContainerForFixedPositionLayers(true);
+ child->setForceRenderSurface(true);
+ grandChild->setFixedToContainerLayer(true);
+ grandChild->setDrawsContent(true);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedSurfaceDrawTransform;
+ expectedSurfaceDrawTransform.translate(0, 0);
+ WebTransformationMatrix expectedChildTransform;
+ WebTransformationMatrix expectedGrandChildTransform;
+ ASSERT_TRUE(child->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceDrawTransform, child->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 10
+ child->setScrollDelta(IntSize(10, 10));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // The surface is translated by scrollDelta, the child transform doesn't change
+ // because it scrolls along with the surface, but the fixed position grandChild
+ // needs to compensate for the scroll translation.
+ expectedSurfaceDrawTransform.makeIdentity();
+ expectedSurfaceDrawTransform.translate(-10, -10);
+ expectedGrandChildTransform.makeIdentity();
+ expectedGrandChildTransform.translate(10, 10);
+
+ ASSERT_TRUE(child->renderSurface());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceDrawTransform, child->renderSurface()->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerThatIsAlsoFixedPositionContainer)
+{
+ // This test checks the scenario where a fixed-position layer also happens to be a
+ // container itself for a descendant fixed position layer. In particular, the layer
+ // should not accidentally be fixed to itself.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ child->setIsContainerForFixedPositionLayers(true);
+ grandChild->setFixedToContainerLayer(true);
+
+ // This should not confuse the grandChild. If correct, the grandChild would still be considered fixed to its container (i.e. "child").
+ grandChild->setIsContainerForFixedPositionLayers(true);
+
+ // Case 1: scrollDelta of 0, 0
+ child->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ WebTransformationMatrix expectedGrandChildTransform;
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+
+ // Case 2: scrollDelta of 10, 10
+ child->setScrollDelta(IntSize(10, 10));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -10);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerThatHasNoContainer)
+{
+ // This test checks scroll compensation when a fixed-position layer does not find any
+ // ancestor that is a "containerForFixedPositionLayers". In this situation, the layer should
+ // be fixed to the viewport -- not the rootLayer, which may have transforms of its own.
+ DebugScopedSetImplThread scopedImplThread;
+
+ OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests();
+ CCLayerImpl* child = root->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ WebTransformationMatrix rotationByZ;
+ rotationByZ.rotate3d(0, 0, 90);
+
+ root->setTransform(rotationByZ);
+ grandChild->setFixedToContainerLayer(true);
+
+ // Case 1: root scrollDelta of 0, 0
+ root->setScrollDelta(IntSize(0, 0));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ WebTransformationMatrix expectedChildTransform;
+ expectedChildTransform.multiply(rotationByZ);
+
+ WebTransformationMatrix expectedGrandChildTransform;
+ expectedGrandChildTransform.multiply(rotationByZ);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+
+ // Case 2: root scrollDelta of 10, 10
+ root->setScrollDelta(IntSize(10, 10));
+ executeCalculateDrawTransformsAndVisibility(root.get());
+
+ // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected.
+ expectedChildTransform.makeIdentity();
+ expectedChildTransform.translate(-10, -10); // the scrollDelta
+ expectedChildTransform.multiply(rotationByZ);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces)
+{
+ // The entire subtree of layers that are outside the clipRect should be culled away,
+ // and should not affect the renderSurfaceLayerList.
+ //
+ // The test tree is set up as follows:
+ // - all layers except the leafNodes are forced to be a new renderSurface that have something to draw.
+ // - parent is a large container layer.
+ // - child has masksToBounds=true to cause clipping.
+ // - grandChild is positioned outside of the child's bounds
+ // - greatGrandChild is also kept outside child's bounds.
+ //
+ // In this configuration, grandChild and greatGrandChild are completely outside the
+ // clipRect, and they should never get scheduled on the list of renderSurfaces.
+ //
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ RefPtr<LayerChromium> greatGrandChild = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(child);
+ child->addChild(grandChild);
+ grandChild->addChild(greatGrandChild);
+
+ // leafNode1 ensures that parent and child are kept on the renderSurfaceLayerList,
+ // even though grandChild and greatGrandChild should be clipped.
+ child->addChild(leafNode1);
+ greatGrandChild->addChild(leafNode2);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(greatGrandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+
+ child->setMasksToBounds(true);
+ child->setOpacity(0.4f);
+ grandChild->setOpacity(0.5);
+ greatGrandChild->setOpacity(0.4f);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+
+ ASSERT_EQ(2U, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsSurfaceWithoutVisibleContent)
+{
+ // When a renderSurface has a clipRect, it is used to clip the contentRect
+ // of the surface. When the renderSurface is animating its transforms, then
+ // the contentRect's position in the clipRect is not defined on the main
+ // thread, and its contentRect should not be clipped.
+
+ // The test tree is set up as follows:
+ // - parent is a container layer that masksToBounds=true to cause clipping.
+ // - child is a renderSurface, which has a clipRect set to the bounds of the parent.
+ // - grandChild is a renderSurface, and the only visible content in child. It is positioned outside of the clipRect from parent.
+
+ // In this configuration, grandChild should be outside the clipped
+ // contentRect of the child, making grandChild not appear in the
+ // renderSurfaceLayerList. However, when we place an animation on the child,
+ // this clipping should be avoided and we should keep the grandChild
+ // in the renderSurfaceLayerList.
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(child);
+ child->addChild(grandChild);
+ grandChild->addChild(leafNode);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(200, 200), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+
+ parent->setMasksToBounds(true);
+ child->setOpacity(0.4f);
+ grandChild->setOpacity(0.4f);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // Without an animation, we should cull child and grandChild from the renderSurfaceLayerList.
+ ASSERT_EQ(1U, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+
+ // Now put an animating transform on child.
+ addAnimatedTransformToController(*child->layerAnimationController(), 10, 30, 0);
+
+ parent->clearRenderSurface();
+ child->clearRenderSurface();
+ grandChild->clearRenderSurface();
+ renderSurfaceLayerList.clear();
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // With an animating transform, we should keep child and grandChild in the renderSurfaceLayerList.
+ ASSERT_EQ(3U, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id());
+ EXPECT_EQ(grandChild->id(), renderSurfaceLayerList[2]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyDrawableContentRectForLayers)
+{
+ // Verify that layers get the appropriate drawableContentRect when their parent masksToBounds is true.
+ //
+ // grandChild1 - completely inside the region; drawableContentRect should be the layer rect expressed in target space.
+ // grandChild2 - partially clipped but NOT masksToBounds; the clipRect will be the intersection of layerBounds and the mask region.
+ // grandChild3 - partially clipped and masksToBounds; the drawableContentRect will still be the intersection of layerBounds and the mask region.
+ // grandChild4 - outside parent's clipRect; the drawableContentRect should be empty.
+ //
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild1 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild3 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild4 = LayerChromium::create();
+
+ parent->addChild(child);
+ child->addChild(grandChild1);
+ child->addChild(grandChild2);
+ child->addChild(grandChild3);
+ child->addChild(grandChild4);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+
+ child->setMasksToBounds(true);
+ grandChild3->setMasksToBounds(true);
+
+ // Force everyone to be a render surface.
+ child->setOpacity(0.4f);
+ grandChild1->setOpacity(0.5);
+ grandChild2->setOpacity(0.5);
+ grandChild3->setOpacity(0.5);
+ grandChild4->setOpacity(0.5);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(5, 5), IntSize(10, 10)), grandChild1->drawableContentRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(15, 15), IntSize(5, 5)), grandChild3->drawableContentRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(15, 15), IntSize(5, 5)), grandChild3->drawableContentRect());
+ EXPECT_TRUE(grandChild4->drawableContentRect().isEmpty());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToSurfaces)
+{
+ // Verify that renderSurfaces (and their layers) get the appropriate clipRects when their parent masksToBounds is true.
+ //
+ // Layers that own renderSurfaces (at least for now) do not inherit any clipping;
+ // instead the surface will enforce the clip for the entire subtree. They may still
+ // have a clipRect of their own layer bounds, however, if masksToBounds was true.
+ //
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild1 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild3 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild4 = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode3 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode4 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->addChild(child);
+ child->addChild(grandChild1);
+ child->addChild(grandChild2);
+ child->addChild(grandChild3);
+ child->addChild(grandChild4);
+
+ // the leaf nodes ensure that these grandChildren become renderSurfaces for this test.
+ grandChild1->addChild(leafNode1);
+ grandChild2->addChild(leafNode2);
+ grandChild3->addChild(leafNode3);
+ grandChild4->addChild(leafNode4);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+
+ child->setMasksToBounds(true);
+ grandChild3->setMasksToBounds(true);
+ grandChild4->setMasksToBounds(true);
+
+ // Force everyone to be a render surface.
+ child->setOpacity(0.4f);
+ grandChild1->setOpacity(0.5);
+ grandChild2->setOpacity(0.5);
+ grandChild3->setOpacity(0.5);
+ grandChild4->setOpacity(0.5);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ ASSERT_TRUE(grandChild1->renderSurface());
+ ASSERT_TRUE(grandChild2->renderSurface());
+ ASSERT_TRUE(grandChild3->renderSurface());
+ EXPECT_FALSE(grandChild4->renderSurface()); // Because grandChild4 is entirely clipped, it is expected to not have a renderSurface.
+
+ // Surfaces are clipped by their parent, but un-affected by the owning layer's masksToBounds.
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild1->renderSurface()->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild2->renderSurface()->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild3->renderSurface()->clipRect());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyAnimationsForRenderSurfaceHierarchy)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface2 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRoot = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS1 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->addChild(renderSurface1);
+ parent->addChild(childOfRoot);
+ renderSurface1->addChild(childOfRS1);
+ renderSurface1->addChild(renderSurface2);
+ renderSurface2->addChild(childOfRS2);
+ childOfRoot->addChild(grandChildOfRoot);
+ childOfRS1->addChild(grandChildOfRS1);
+ childOfRS2->addChild(grandChildOfRS2);
+
+ // Make our render surfaces.
+ renderSurface1->setForceRenderSurface(true);
+ renderSurface2->setForceRenderSurface(true);
+
+ // Put an animated opacity on the render surface.
+ addOpacityTransitionToController(*renderSurface1->layerAnimationController(), 10, 1, 0, false);
+
+ // Also put an animated opacity on a layer without descendants.
+ addOpacityTransitionToController(*grandChildOfRoot->layerAnimationController(), 10, 1, 0, false);
+
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(1, 1);
+ WebTransformationMatrix sublayerTransform;
+ sublayerTransform.scale3d(10, 1, 1);
+
+ // Put a transform animation on the render surface.
+ addAnimatedTransformToController(*renderSurface2->layerAnimationController(), 10, 30, 0);
+
+ // Also put transform animations on grandChildOfRoot, and grandChildOfRS2
+ addAnimatedTransformToController(*grandChildOfRoot->layerAnimationController(), 10, 30, 0);
+ addAnimatedTransformToController(*grandChildOfRS2->layerAnimationController(), 10, 30, 0);
+
+ setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25, 0), FloatPoint(2.5, 0), IntSize(10, 10), false);
+
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Only layers that are associated with render surfaces should have an actual renderSurface() value.
+ //
+ ASSERT_TRUE(parent->renderSurface());
+ ASSERT_FALSE(childOfRoot->renderSurface());
+ ASSERT_FALSE(grandChildOfRoot->renderSurface());
+
+ ASSERT_TRUE(renderSurface1->renderSurface());
+ ASSERT_FALSE(childOfRS1->renderSurface());
+ ASSERT_FALSE(grandChildOfRS1->renderSurface());
+
+ ASSERT_TRUE(renderSurface2->renderSurface());
+ ASSERT_FALSE(childOfRS2->renderSurface());
+ ASSERT_FALSE(grandChildOfRS2->renderSurface());
+
+ // Verify all renderTarget accessors
+ //
+ EXPECT_EQ(parent, parent->renderTarget());
+ EXPECT_EQ(parent, childOfRoot->renderTarget());
+ EXPECT_EQ(parent, grandChildOfRoot->renderTarget());
+
+ EXPECT_EQ(renderSurface1, renderSurface1->renderTarget());
+ EXPECT_EQ(renderSurface1, childOfRS1->renderTarget());
+ EXPECT_EQ(renderSurface1, grandChildOfRS1->renderTarget());
+
+ EXPECT_EQ(renderSurface2, renderSurface2->renderTarget());
+ EXPECT_EQ(renderSurface2, childOfRS2->renderTarget());
+ EXPECT_EQ(renderSurface2, grandChildOfRS2->renderTarget());
+
+ // Verify drawOpacityIsAnimating values
+ //
+ EXPECT_FALSE(parent->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRoot->drawOpacityIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface1->drawOpacityIsAnimating());
+ EXPECT_TRUE(renderSurface1->renderSurface()->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRS1->drawOpacityIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface2->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface2->renderSurface()->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRS2->drawOpacityIsAnimating());
+ EXPECT_FALSE(grandChildOfRS2->drawOpacityIsAnimating());
+
+ // Verify drawTransformsAnimatingInTarget values
+ //
+ EXPECT_FALSE(parent->drawTransformIsAnimating());
+ EXPECT_FALSE(childOfRoot->drawTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->renderSurface()->targetSurfaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS1->drawTransformIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface2->drawTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->renderSurface()->targetSurfaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS2->drawTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRS2->drawTransformIsAnimating());
+
+ // Verify drawTransformsAnimatingInScreen values
+ //
+ EXPECT_FALSE(parent->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(childOfRoot->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->renderSurface()->screenSpaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS1->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->renderSurface()->screenSpaceTransformsAreAnimating());
+ EXPECT_TRUE(childOfRS2->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRS2->screenSpaceTransformIsAnimating());
+
+
+ // Sanity check. If these fail there is probably a bug in the test itself.
+ // It is expected that we correctly set up transforms so that the y-component of the screen-space transform
+ // encodes the "depth" of the layer in the tree.
+ EXPECT_FLOAT_EQ(1, parent->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(2, childOfRoot->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3, grandChildOfRoot->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(2, renderSurface1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3, childOfRS1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4, grandChildOfRS1->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(3, renderSurface2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4, childOfRS2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(5, grandChildOfRS2->screenSpaceTransform().m42());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForIdentityTransform)
+{
+ // Test the calculateVisibleRect() function works correctly for identity transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ IntRect layerContentRect = IntRect(IntPoint(10, 10), IntSize(30, 30));
+ IntRect expected = IntRect(IntPoint(10, 10), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerContentRect = IntRect(IntPoint(120, 120), IntSize(30, 30));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: Layer is partially overlapping the surface rect.
+ layerContentRect = IntRect(IntPoint(80, 80), IntSize(30, 30));
+ expected = IntRect(IntPoint(80, 80), IntSize(20, 20));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForTranslations)
+{
+ // Test the calculateVisibleRect() function works correctly for scaling transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(10, 10);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(120, 120);
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: Layer is partially overlapping the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(80, 80);
+ expected = IntRect(IntPoint(0, 0), IntSize(20, 20));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor2DRotations)
+{
+ // Test the calculateVisibleRect() function works correctly for rotations about z-axis (i.e. 2D rotations).
+ // Remember that calculateVisibleRect() should return the visible rect in the layer's space.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(50, 50);
+ layerToSurfaceTransform.rotate(45);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(-50, 0);
+ layerToSurfaceTransform.rotate(45);
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: The layer is rotated about its top-left corner. In surface space, the layer
+ // is oriented diagonally, with the left half outside of the renderSurface. In
+ // this case, the visible rect should still be the entire layer (remember the
+ // visible rect is computed in layer space); both the top-left and
+ // bottom-right corners of the layer are still visible.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.rotate(45);
+ expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 4: The layer is rotated about its top-left corner, and translated upwards. In
+ // surface space, the layer is oriented diagonally, with only the top corner
+ // of the surface overlapping the layer. In layer space, the render surface
+ // overlaps the right side of the layer. The visible rect should be the
+ // layer's right half.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(0, -sqrt(2.0) * 15);
+ layerToSurfaceTransform.rotate(45);
+ expected = IntRect(IntPoint(15, 0), IntSize(15, 30)); // right half of layer bounds.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicTransform)
+{
+ // Test that the calculateVisibleRect() function works correctly for 3d transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Orthographic projection of a layer rotated about y-axis by 45 degrees, should be fully contained in the renderSurface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Orthographic projection of a layer rotated about y-axis by 45 degrees, but
+ // shifted to the side so only the right-half the layer would be visible on
+ // the surface.
+ double halfWidthOfRotatedLayer = (100 / sqrt(2.0)) * 0.5; // 100 is the un-rotated layer width; divided by sqrt(2) is the rotated width.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(-halfWidthOfRotatedLayer, 0);
+ layerToSurfaceTransform.rotate3d(0, 45, 0); // rotates about the left edge of the layer
+ expected = IntRect(IntPoint(50, 0), IntSize(50, 100)); // right half of the layer.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveTransform)
+{
+ // Test the calculateVisibleRect() function works correctly when the layer has a
+ // perspective projection onto the target surface.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-50, -50), IntSize(200, 200));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Even though the layer is twice as large as the surface, due to perspective
+ // foreshortening, the layer will fit fully in the surface when its translated
+ // more than the perspective amount.
+ layerToSurfaceTransform.makeIdentity();
+
+ // The following sequence of transforms applies the perspective about the center of the surface.
+ layerToSurfaceTransform.translate(50, 50);
+ layerToSurfaceTransform.applyPerspective(9);
+ layerToSurfaceTransform.translate(-50, -50);
+
+ // This translate places the layer in front of the surface's projection plane.
+ layerToSurfaceTransform.translate3d(0, 0, -27);
+
+ IntRect expected = IntRect(IntPoint(-50, -50), IntSize(200, 200));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: same projection as before, except that the layer is also translated to the
+ // side, so that only the right half of the layer should be visible.
+ //
+ // Explanation of expected result:
+ // The perspective ratio is (z distance between layer and camera origin) / (z distance between projection plane and camera origin) == ((-27 - 9) / 9)
+ // Then, by similar triangles, if we want to move a layer by translating -50 units in projected surface units (so that only half of it is
+ // visible), then we would need to translate by (-36 / 9) * -50 == -200 in the layer's units.
+ //
+ layerToSurfaceTransform.translate3d(-200, 0, 0);
+ expected = IntRect(IntPoint(50, -50), IntSize(100, 200)); // The right half of the layer's bounding rect.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicIsNotClippedBehindSurface)
+{
+ // There is currently no explicit concept of an orthographic projection plane in our
+ // code (nor in the CSS spec to my knowledge). Therefore, layers that are technically
+ // behind the surface in an orthographic world should not be clipped when they are
+ // flattened to the surface.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // This sequence of transforms effectively rotates the layer about the y-axis at the
+ // center of the layer.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(50, 0);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ layerToSurfaceTransform.translate(-50, 0);
+
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveWhenClippedByW)
+{
+ // Test the calculateVisibleRect() function works correctly when projecting a surface
+ // onto a layer, but the layer is partially behind the camera (not just behind the
+ // projection plane). In this case, the cartesian coordinates may seem to be valid,
+ // but actually they are not. The visibleRect needs to be properly clipped by the
+ // w = 0 plane in homogeneous coordinates before converting to cartesian coordinates.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-10, -1), IntSize(20, 2));
+ WebTransformationMatrix layerToSurfaceTransform;
+
+ // The layer is positioned so that the right half of the layer should be in front of
+ // the camera, while the other half is behind the surface's projection plane. The
+ // following sequence of transforms applies the perspective and rotation about the
+ // center of the layer.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.applyPerspective(1);
+ layerToSurfaceTransform.translate3d(-2, 0, 1);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+
+ // Sanity check that this transform does indeed cause w < 0 when applying the
+ // transform, otherwise this code is not testing the intended scenario.
+ bool clipped = false;
+ CCMathUtil::mapQuad(layerToSurfaceTransform, FloatQuad(FloatRect(layerContentRect)), clipped);
+ ASSERT_TRUE(clipped);
+
+ int expectedXPosition = 0;
+ int expectedWidth = 10;
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_EQ(expectedXPosition, actual.x());
+ EXPECT_EQ(expectedWidth, actual.width());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForPerspectiveUnprojection)
+{
+ // To determine visibleRect in layer space, there needs to be an un-projection from
+ // surface space to layer space. When the original transform was a perspective
+ // projection that was clipped, it returns a rect that encloses the clipped bounds.
+ // Un-projecting this new rect may require clipping again.
+
+ // This sequence of transforms causes one corner of the layer to protrude across the w = 0 plane, and should be clipped.
+ IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-10, -10), IntSize(20, 20));
+ WebTransformationMatrix layerToSurfaceTransform;
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.applyPerspective(1);
+ layerToSurfaceTransform.translate3d(0, 0, -5);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ layerToSurfaceTransform.rotate3d(80, 0, 0);
+
+ // Sanity check that un-projection does indeed cause w < 0, otherwise this code is not
+ // testing the intended scenario.
+ bool clipped = false;
+ FloatRect clippedRect = CCMathUtil::mapClippedRect(layerToSurfaceTransform, layerContentRect);
+ CCMathUtil::projectQuad(layerToSurfaceTransform.inverse(), FloatQuad(clippedRect), clipped);
+ ASSERT_TRUE(clipped);
+
+ // Only the corner of the layer is not visible on the surface because of being
+ // clipped. But, the net result of rounding visible region to an axis-aligned rect is
+ // that the entire layer should still be considered visible.
+ IntRect expected = IntRect(IntPoint(-10, -10), IntSize(20, 20));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithoutPreserves3d)
+{
+ // Verify the behavior of back-face culling when there are no preserve-3d layers. Note
+ // that 3d transforms still apply in this case, but they are "flattened" to each
+ // parent layer according to current W3C spec.
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChildOfFrontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChildOfFrontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChildOfBackFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChildOfBackFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->addChild(frontFacingChild);
+ parent->addChild(backFacingChild);
+ parent->addChild(frontFacingSurface);
+ parent->addChild(backFacingSurface);
+ frontFacingSurface->addChild(frontFacingChildOfFrontFacingSurface);
+ frontFacingSurface->addChild(backFacingChildOfFrontFacingSurface);
+ backFacingSurface->addChild(frontFacingChildOfBackFacingSurface);
+ backFacingSurface->addChild(backFacingChildOfBackFacingSurface);
+
+ // Nothing is double-sided
+ frontFacingChild->setDoubleSided(false);
+ backFacingChild->setDoubleSided(false);
+ frontFacingSurface->setDoubleSided(false);
+ backFacingSurface->setDoubleSided(false);
+ frontFacingChildOfFrontFacingSurface->setDoubleSided(false);
+ backFacingChildOfFrontFacingSurface->setDoubleSided(false);
+ frontFacingChildOfBackFacingSurface->setDoubleSided(false);
+ backFacingChildOfBackFacingSurface->setDoubleSided(false);
+
+ WebTransformationMatrix backfaceMatrix;
+ backfaceMatrix.translate(50, 50);
+ backfaceMatrix.rotate3d(0, 1, 0, 180);
+ backfaceMatrix.translate(-50, -50);
+
+ // Having a descendant and opacity will force these to have render surfaces.
+ frontFacingSurface->setOpacity(0.5);
+ backFacingSurface->setOpacity(0.5);
+
+ // Nothing preserves 3d. According to current W3C CSS Transforms spec, these layers
+ // should blindly use their own local transforms to determine back-face culling.
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChild.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingChildOfFrontFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChildOfFrontFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingChildOfBackFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChildOfBackFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // Verify which renderSurfaces were created.
+ EXPECT_FALSE(frontFacingChild->renderSurface());
+ EXPECT_FALSE(backFacingChild->renderSurface());
+ EXPECT_TRUE(frontFacingSurface->renderSurface());
+ EXPECT_TRUE(backFacingSurface->renderSurface());
+ EXPECT_FALSE(frontFacingChildOfFrontFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingChildOfFrontFacingSurface->renderSurface());
+ EXPECT_FALSE(frontFacingChildOfBackFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingChildOfBackFacingSurface->renderSurface());
+
+ // Verify the renderSurfaceLayerList.
+ ASSERT_EQ(3u, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->id());
+ // Even though the back facing surface LAYER gets culled, the other descendants should still be added, so the SURFACE should not be culled.
+ EXPECT_EQ(backFacingSurface->id(), renderSurfaceLayerList[2]->id());
+
+ // Verify root surface's layerList.
+ ASSERT_EQ(3u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingChild->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[1]->id());
+ EXPECT_EQ(backFacingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[2]->id());
+
+ // Verify frontFacingSurface's layerList.
+ ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(frontFacingChildOfFrontFacingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id());
+
+ // Verify backFacingSurface's layerList; its own layer should be culled from the surface list.
+ ASSERT_EQ(1u, renderSurfaceLayerList[2]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingChildOfBackFacingSurface->id(), renderSurfaceLayerList[2]->renderSurface()->layerList()[0]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3d)
+{
+ // Verify the behavior of back-face culling when preserves-3d transform style is used.
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChildOfFrontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChildOfFrontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingChildOfBackFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingChildOfBackFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> dummyReplicaLayer1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> dummyReplicaLayer2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->addChild(frontFacingChild);
+ parent->addChild(backFacingChild);
+ parent->addChild(frontFacingSurface);
+ parent->addChild(backFacingSurface);
+ frontFacingSurface->addChild(frontFacingChildOfFrontFacingSurface);
+ frontFacingSurface->addChild(backFacingChildOfFrontFacingSurface);
+ backFacingSurface->addChild(frontFacingChildOfBackFacingSurface);
+ backFacingSurface->addChild(backFacingChildOfBackFacingSurface);
+
+ // Nothing is double-sided
+ frontFacingChild->setDoubleSided(false);
+ backFacingChild->setDoubleSided(false);
+ frontFacingSurface->setDoubleSided(false);
+ backFacingSurface->setDoubleSided(false);
+ frontFacingChildOfFrontFacingSurface->setDoubleSided(false);
+ backFacingChildOfFrontFacingSurface->setDoubleSided(false);
+ frontFacingChildOfBackFacingSurface->setDoubleSided(false);
+ backFacingChildOfBackFacingSurface->setDoubleSided(false);
+
+ WebTransformationMatrix backfaceMatrix;
+ backfaceMatrix.translate(50, 50);
+ backfaceMatrix.rotate3d(0, 1, 0, 180);
+ backfaceMatrix.translate(-50, -50);
+
+ // Opacity will not force creation of renderSurfaces in this case because of the
+ // preserve-3d transform style. Instead, an example of when a surface would be
+ // created with preserve-3d is when there is a replica layer.
+ frontFacingSurface->setReplicaLayer(dummyReplicaLayer1.get());
+ backFacingSurface->setReplicaLayer(dummyReplicaLayer2.get());
+
+ // Each surface creates its own new 3d rendering context (as defined by W3C spec).
+ // According to current W3C CSS Transforms spec, layers in a 3d rendering context
+ // should use the transform with respect to that context. This 3d rendering context
+ // occurs when (a) parent's transform style is flat and (b) the layer's transform
+ // style is preserve-3d.
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); // parent transform style is flat.
+ setLayerPropertiesForTesting(frontFacingChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChild.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); // surface transform style is preserve-3d.
+ setLayerPropertiesForTesting(backFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); // surface transform style is preserve-3d.
+ setLayerPropertiesForTesting(frontFacingChildOfFrontFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChildOfFrontFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(frontFacingChildOfBackFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(backFacingChildOfBackFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // Verify which renderSurfaces were created.
+ EXPECT_FALSE(frontFacingChild->renderSurface());
+ EXPECT_FALSE(backFacingChild->renderSurface());
+ EXPECT_TRUE(frontFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingSurface->renderSurface());
+ EXPECT_FALSE(frontFacingChildOfFrontFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingChildOfFrontFacingSurface->renderSurface());
+ EXPECT_FALSE(frontFacingChildOfBackFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingChildOfBackFacingSurface->renderSurface());
+
+ // Verify the renderSurfaceLayerList. The back-facing surface should be culled.
+ ASSERT_EQ(2u, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->id());
+
+ // Verify root surface's layerList.
+ ASSERT_EQ(2u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingChild->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[1]->id());
+
+ // Verify frontFacingSurface's layerList.
+ ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(frontFacingChildOfFrontFacingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithAnimatingTransforms)
+{
+ // Verify that layers are appropriately culled when their back face is showing and
+ // they are not double sided, while animations are going on.
+ //
+ // Layers that are animating do not get culled on the main thread, as their transforms should be
+ // treated as "unknown" so we can not be sure that their back face is really showing.
+ //
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> animatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> childOfAnimatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> animatingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> child2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->addChild(child);
+ parent->addChild(animatingSurface);
+ animatingSurface->addChild(childOfAnimatingSurface);
+ parent->addChild(animatingChild);
+ parent->addChild(child2);
+
+ // Nothing is double-sided
+ child->setDoubleSided(false);
+ child2->setDoubleSided(false);
+ animatingSurface->setDoubleSided(false);
+ childOfAnimatingSurface->setDoubleSided(false);
+ animatingChild->setDoubleSided(false);
+
+ WebTransformationMatrix backfaceMatrix;
+ backfaceMatrix.translate(50, 50);
+ backfaceMatrix.rotate3d(0, 1, 0, 180);
+ backfaceMatrix.translate(-50, -50);
+
+ // Make our render surface.
+ animatingSurface->setForceRenderSurface(true);
+
+ // Animate the transform on the render surface.
+ addAnimatedTransformToController(*animatingSurface->layerAnimationController(), 10, 30, 0);
+ // This is just an animating layer, not a surface.
+ addAnimatedTransformToController(*animatingChild->layerAnimationController(), 10, 30, 0);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(animatingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(childOfAnimatingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(animatingChild.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ EXPECT_FALSE(child->renderSurface());
+ EXPECT_TRUE(animatingSurface->renderSurface());
+ EXPECT_FALSE(childOfAnimatingSurface->renderSurface());
+ EXPECT_FALSE(animatingChild->renderSurface());
+ EXPECT_FALSE(child2->renderSurface());
+
+ // Verify that the animatingChild and childOfAnimatingSurface were not culled, but that child was.
+ ASSERT_EQ(2u, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->id());
+
+ // The non-animating child be culled from the layer list for the parent render surface.
+ ASSERT_EQ(3u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(animatingChild->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[1]->id());
+ EXPECT_EQ(child2->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[2]->id());
+
+ ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(childOfAnimatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id());
+
+ EXPECT_FALSE(child2->visibleContentRect().isEmpty());
+
+ // The animating layers should have a visibleContentRect that represents the area of the front face that is within the viewport.
+ EXPECT_EQ(animatingChild->visibleContentRect(), IntRect(IntPoint(), animatingChild->contentBounds()));
+ EXPECT_EQ(animatingSurface->visibleContentRect(), IntRect(IntPoint(), animatingSurface->contentBounds()));
+ // And layers in the subtree of the animating layer should have valid visibleContentRects also.
+ EXPECT_EQ(childOfAnimatingSurface->visibleContentRect(), IntRect(IntPoint(), childOfAnimatingSurface->contentBounds()));
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3dForFlatteningSurface)
+{
+ // Verify the behavior of back-face culling for a renderSurface that is created
+ // when it flattens its subtree, and its parent has preserves-3d.
+
+ const WebTransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> frontFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> backFacingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> child1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> child2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->addChild(frontFacingSurface);
+ parent->addChild(backFacingSurface);
+ frontFacingSurface->addChild(child1);
+ backFacingSurface->addChild(child2);
+
+ // RenderSurfaces are not double-sided
+ frontFacingSurface->setDoubleSided(false);
+ backFacingSurface->setDoubleSided(false);
+
+ WebTransformationMatrix backfaceMatrix;
+ backfaceMatrix.translate(50, 50);
+ backfaceMatrix.rotate3d(0, 1, 0, 180);
+ backfaceMatrix.translate(-50, -50);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); // parent transform style is preserve3d.
+ setLayerPropertiesForTesting(frontFacingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); // surface transform style is flat.
+ setLayerPropertiesForTesting(backFacingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); // surface transform style is flat.
+ setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // Verify which renderSurfaces were created.
+ EXPECT_TRUE(frontFacingSurface->renderSurface());
+ EXPECT_FALSE(backFacingSurface->renderSurface()); // because it should be culled
+ EXPECT_FALSE(child1->renderSurface());
+ EXPECT_FALSE(child2->renderSurface());
+
+ // Verify the renderSurfaceLayerList. The back-facing surface should be culled.
+ ASSERT_EQ(2u, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->id());
+
+ // Verify root surface's layerList.
+ ASSERT_EQ(1u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id());
+
+ // Verify frontFacingSurface's layerList.
+ ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ EXPECT_EQ(frontFacingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(child1->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForEmptyLayerList)
+{
+ // Hit testing on an empty renderSurfaceLayerList should return a null pointer.
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+
+ IntPoint testPoint(0, 0);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(10, 20);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleLayer)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+
+ // Hit testing for a point outside the layer should return a null pointer.
+ IntPoint testPoint(101, 101);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(-1, -1);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the root layer.
+ testPoint = IntPoint(1, 1);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+
+ testPoint = IntPoint(99, 99);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForUninvertibleTransform)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345);
+
+ WebTransformationMatrix uninvertibleTransform;
+ uninvertibleTransform.setM11(0);
+ uninvertibleTransform.setM22(0);
+ uninvertibleTransform.setM33(0);
+ uninvertibleTransform.setM44(0);
+ ASSERT_FALSE(uninvertibleTransform.isInvertible());
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), uninvertibleTransform, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+ ASSERT_FALSE(root->screenSpaceTransform().isInvertible());
+
+ // Hit testing any point should not hit the layer. If the invertible matrix is
+ // accidentally ignored and treated like an identity, then the hit testing will
+ // incorrectly hit the layer when it shouldn't.
+ IntPoint testPoint(1, 1);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(10, 10);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(10, 30);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(50, 50);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(67, 48);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(99, 99);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(-1, -1);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSinglePositionedLayer)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(50, 50); // this layer is positioned, and hit testing should correctly know where the layer is located.
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+
+ // Hit testing for a point outside the layer should return a null pointer.
+ IntPoint testPoint(49, 49);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Even though the layer exists at (101, 101), it should not be visible there since the root renderSurface would clamp it.
+ testPoint = IntPoint(101, 101);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the root layer.
+ testPoint = IntPoint(51, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+
+ testPoint = IntPoint(99, 99);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleRotatedLayer)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345);
+
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix rotation45DegreesAboutCenter;
+ rotation45DegreesAboutCenter.translate(50, 50);
+ rotation45DegreesAboutCenter.rotate3d(0, 0, 45);
+ rotation45DegreesAboutCenter.translate(-50, -50);
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), rotation45DegreesAboutCenter, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+
+ // Hit testing for points outside the layer.
+ // These corners would have been inside the un-transformed layer, but they should not hit the correctly transformed layer.
+ IntPoint testPoint(99, 99);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(1, 1);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the root layer.
+ testPoint = IntPoint(1, 50);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+
+ // Hit testing the corners that would overlap the unclipped layer, but are outside the clipped region.
+ testPoint = IntPoint(50, -1);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_FALSE(resultLayer);
+
+ testPoint = IntPoint(-1, 50);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_FALSE(resultLayer);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSinglePerspectiveLayer)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345);
+
+ WebTransformationMatrix identityMatrix;
+
+ // perspectiveProjectionAboutCenter * translationByZ is designed so that the 100 x 100 layer becomes 50 x 50, and remains centered at (50, 50).
+ WebTransformationMatrix perspectiveProjectionAboutCenter;
+ perspectiveProjectionAboutCenter.translate(50, 50);
+ perspectiveProjectionAboutCenter.applyPerspective(1);
+ perspectiveProjectionAboutCenter.translate(-50, -50);
+ WebTransformationMatrix translationByZ;
+ translationByZ.translate3d(0, 0, -1);
+
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), perspectiveProjectionAboutCenter * translationByZ, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+
+ // Hit testing for points outside the layer.
+ // These corners would have been inside the un-transformed layer, but they should not hit the correctly transformed layer.
+ IntPoint testPoint(24, 24);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(76, 76);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the root layer.
+ testPoint = IntPoint(26, 26);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+
+ testPoint = IntPoint(74, 74);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleLayerWithScaledContents)
+{
+ // A layer's visibleContentRect is actually in the layer's content space. The
+ // screenSpaceTransform converts from the layer's origin space to screen space. This
+ // test makes sure that hit testing works correctly accounts for the contents scale.
+ // A contentsScale that is not 1 effectively forces a non-identity transform between
+ // layer's content space and layer's origin space. The hit testing code must take this into account.
+ //
+ // To test this, the layer is positioned at (25, 25), and is size (50, 50). If
+ // contentsScale is ignored, then hit testing will mis-interpret the visibleContentRect
+ // as being larger than the actual bounds of the layer.
+ //
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, FloatPoint(0, 0), IntSize(100, 100), false);
+
+ {
+ FloatPoint position(25, 25);
+ IntSize bounds(50, 50);
+ OwnPtr<CCLayerImpl> testLayer = CCLayerImpl::create(12345);
+ setLayerPropertiesForTesting(testLayer.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+
+ // override contentBounds
+ testLayer->setContentBounds(IntSize(100, 100));
+
+ testLayer->setDrawsContent(true);
+ root->addChild(testLayer.release());
+ }
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ // The visibleContentRect for testLayer is actually 100x100, even though its layout size is 50x50, positioned at 25x25.
+ CCLayerImpl* testLayer = root->children()[0].get();
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(100, 100)), testLayer->visibleContentRect());
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+
+ // Hit testing for a point outside the layer should return a null pointer (the root layer does not draw content, so it will not be hit tested either).
+ IntPoint testPoint(101, 101);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(24, 24);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(76, 76);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the test layer.
+ testPoint = IntPoint(26, 26);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+
+ testPoint = IntPoint(74, 74);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(12345, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSimpleClippedLayer)
+{
+ // Test that hit-testing will only work for the visible portion of a layer, and not
+ // the entire layer bounds. Here we just test the simple axis-aligned case.
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, FloatPoint(0, 0), IntSize(100, 100), false);
+
+ {
+ OwnPtr<CCLayerImpl> clippingLayer = CCLayerImpl::create(123);
+ FloatPoint position(25, 25); // this layer is positioned, and hit testing should correctly know where the layer is located.
+ IntSize bounds(50, 50);
+ setLayerPropertiesForTesting(clippingLayer.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ clippingLayer->setMasksToBounds(true);
+
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(456);
+ position = FloatPoint(-50, -50);
+ bounds = IntSize(300, 300);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child->setDrawsContent(true);
+ clippingLayer->addChild(child.release());
+ root->addChild(clippingLayer.release());
+ }
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+ ASSERT_EQ(456, root->renderSurface()->layerList()[0]->id());
+
+ // Hit testing for a point outside the layer should return a null pointer.
+ // Despite the child layer being very large, it should be clipped to the root layer's bounds.
+ IntPoint testPoint(24, 24);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Even though the layer exists at (101, 101), it should not be visible there since the clippingLayer would clamp it.
+ testPoint = IntPoint(76, 76);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the child layer.
+ testPoint = IntPoint(26, 26);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(456, resultLayer->id());
+
+ testPoint = IntPoint(74, 74);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(456, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultiClippedRotatedLayer)
+{
+ // This test checks whether hit testing correctly avoids hit testing with multiple
+ // ancestors that clip in non axis-aligned ways. To pass this test, the hit testing
+ // algorithm needs to recognize that multiple parent layers may clip the layer, and
+ // should not actually hit those clipped areas.
+ //
+ // The child and grandChild layers are both initialized to clip the rotatedLeaf. The
+ // child layer is rotated about the top-left corner, so that the root + child clips
+ // combined create a triangle. The rotatedLeaf will only be visible where it overlaps
+ // this triangle.
+ //
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(123);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ root->setMasksToBounds(true);
+
+ {
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(456);
+ OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(789);
+ OwnPtr<CCLayerImpl> rotatedLeaf = CCLayerImpl::create(2468);
+
+ position = FloatPoint(10, 10);
+ bounds = IntSize(80, 80);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child->setMasksToBounds(true);
+
+ WebTransformationMatrix rotation45DegreesAboutCorner;
+ rotation45DegreesAboutCorner.rotate3d(0, 0, 45);
+
+ position = FloatPoint(0, 0); // remember, positioned with respect to its parent which is already at 10, 10
+ bounds = IntSize(200, 200); // to ensure it covers at least sqrt(2) * 100.
+ setLayerPropertiesForTesting(grandChild.get(), rotation45DegreesAboutCorner, identityMatrix, anchor, position, bounds, false);
+ grandChild->setMasksToBounds(true);
+
+ // Rotates about the center of the layer
+ WebTransformationMatrix rotatedLeafTransform;
+ rotatedLeafTransform.translate(-10, -10); // cancel out the grandParent's position
+ rotatedLeafTransform.rotate3d(0, 0, -45); // cancel out the corner 45-degree rotation of the parent.
+ rotatedLeafTransform.translate(50, 50);
+ rotatedLeafTransform.rotate3d(0, 0, 45);
+ rotatedLeafTransform.translate(-50, -50);
+ position = FloatPoint(0, 0);
+ bounds = IntSize(100, 100);
+ setLayerPropertiesForTesting(rotatedLeaf.get(), rotatedLeafTransform, identityMatrix, anchor, position, bounds, false);
+ rotatedLeaf->setDrawsContent(true);
+
+ grandChild->addChild(rotatedLeaf.release());
+ child->addChild(grandChild.release());
+ root->addChild(child.release());
+ }
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ // The grandChild is expected to create a renderSurface because it masksToBounds and is not axis aligned.
+ ASSERT_EQ(2u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ ASSERT_EQ(789, renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id()); // grandChild's surface.
+ ASSERT_EQ(1u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ ASSERT_EQ(2468, renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+
+ // (11, 89) is close to the the bottom left corner within the clip, but it is not inside the layer.
+ IntPoint testPoint(11, 89);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Closer inwards from the bottom left will overlap the layer.
+ testPoint = IntPoint(25, 75);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(2468, resultLayer->id());
+
+ // (4, 50) is inside the unclipped layer, but that corner of the layer should be
+ // clipped away by the grandParent and should not get hit. If hit testing blindly uses
+ // visibleContentRect without considering how parent may clip the layer, then hit
+ // testing would accidentally think that the point successfully hits the layer.
+ testPoint = IntPoint(4, 50);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // (11, 50) is inside the layer and within the clipped area.
+ testPoint = IntPoint(11, 50);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(2468, resultLayer->id());
+
+ // Around the middle, just to the right and up, would have hit the layer except that
+ // that area should be clipped away by the parent.
+ testPoint = IntPoint(51, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Around the middle, just to the left and down, should successfully hit the layer.
+ testPoint = IntPoint(49, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(2468, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForNonClippingIntermediateLayer)
+{
+ // This test checks that hit testing code does not accidentally clip to layer
+ // bounds for a layer that actually does not clip.
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, FloatPoint(0, 0), IntSize(100, 100), false);
+
+ {
+ OwnPtr<CCLayerImpl> intermediateLayer = CCLayerImpl::create(123);
+ FloatPoint position(10, 10); // this layer is positioned, and hit testing should correctly know where the layer is located.
+ IntSize bounds(50, 50);
+ setLayerPropertiesForTesting(intermediateLayer.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ // Sanity check the intermediate layer should not clip.
+ ASSERT_FALSE(intermediateLayer->masksToBounds());
+ ASSERT_FALSE(intermediateLayer->maskLayer());
+
+ // The child of the intermediateLayer is translated so that it does not overlap intermediateLayer at all.
+ // If child is incorrectly clipped, we would not be able to hit it successfully.
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(456);
+ position = FloatPoint(60, 60); // 70, 70 in screen space
+ bounds = IntSize(20, 20);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child->setDrawsContent(true);
+ intermediateLayer->addChild(child.release());
+ root->addChild(intermediateLayer.release());
+ }
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(1u, root->renderSurface()->layerList().size());
+ ASSERT_EQ(456, root->renderSurface()->layerList()[0]->id());
+
+ // Hit testing for a point outside the layer should return a null pointer.
+ IntPoint testPoint(69, 69);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ testPoint = IntPoint(91, 91);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ EXPECT_FALSE(resultLayer);
+
+ // Hit testing for a point inside should return the child layer.
+ testPoint = IntPoint(71, 71);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(456, resultLayer->id());
+
+ testPoint = IntPoint(89, 89);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(456, resultLayer->id());
+}
+
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultipleLayers)
+{
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ {
+ // child 1 and child2 are initialized to overlap between x=50 and x=60.
+ // grandChild is set to overlap both child1 and child2 between y=50 and y=60.
+ // The expected stacking order is:
+ // (front) child2, (second) grandChild, (third) child1, and (back) the root layer behind all other layers.
+
+ OwnPtr<CCLayerImpl> child1 = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ OwnPtr<CCLayerImpl> grandChild1 = CCLayerImpl::create(4);
+
+ position = FloatPoint(10, 10);
+ bounds = IntSize(50, 50);
+ setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child1->setDrawsContent(true);
+
+ position = FloatPoint(50, 10);
+ bounds = IntSize(50, 50);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child2->setDrawsContent(true);
+
+ // Remember that grandChild is positioned with respect to its parent (i.e. child1).
+ // In screen space, the intended position is (10, 50), with size 100 x 50.
+ position = FloatPoint(0, 40);
+ bounds = IntSize(100, 50);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ grandChild1->setDrawsContent(true);
+
+ child1->addChild(grandChild1.release());
+ root->addChild(child1.release());
+ root->addChild(child2.release());
+ }
+
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* child2 = root->children()[1].get();
+ CCLayerImpl* grandChild1 = child1->children()[0].get();
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_TRUE(child1);
+ ASSERT_TRUE(child2);
+ ASSERT_TRUE(grandChild1);
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(4u, root->renderSurface()->layerList().size());
+ ASSERT_EQ(1, root->renderSurface()->layerList()[0]->id()); // root layer
+ ASSERT_EQ(2, root->renderSurface()->layerList()[1]->id()); // child1
+ ASSERT_EQ(4, root->renderSurface()->layerList()[2]->id()); // grandChild1
+ ASSERT_EQ(3, root->renderSurface()->layerList()[3]->id()); // child2
+
+ // Nothing overlaps the rootLayer at (1, 1), so hit testing there should find the root layer.
+ IntPoint testPoint = IntPoint(1, 1);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(1, resultLayer->id());
+
+ // At (15, 15), child1 and root are the only layers. child1 is expected to be on top.
+ testPoint = IntPoint(15, 15);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(2, resultLayer->id());
+
+ // At (51, 20), child1 and child2 overlap. child2 is expected to be on top.
+ testPoint = IntPoint(51, 20);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (80, 51), child2 and grandChild1 overlap. child2 is expected to be on top.
+ testPoint = IntPoint(80, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (51, 51), all layers overlap each other. child2 is expected to be on top of all other layers.
+ testPoint = IntPoint(51, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (20, 51), child1 and grandChild1 overlap. grandChild1 is expected to be on top.
+ testPoint = IntPoint(20, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(4, resultLayer->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultipleLayerLists)
+{
+ //
+ // The geometry is set up similarly to the previous case, but
+ // all layers are forced to be renderSurfaces now.
+ //
+ DebugScopedSetImplThread thisScopeIsOnImplThread;
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+
+ WebTransformationMatrix identityMatrix;
+ FloatPoint anchor(0, 0);
+ FloatPoint position(0, 0);
+ IntSize bounds(100, 100);
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ root->setDrawsContent(true);
+
+ {
+ // child 1 and child2 are initialized to overlap between x=50 and x=60.
+ // grandChild is set to overlap both child1 and child2 between y=50 and y=60.
+ // The expected stacking order is:
+ // (front) child2, (second) grandChild, (third) child1, and (back) the root layer behind all other layers.
+
+ OwnPtr<CCLayerImpl> child1 = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3);
+ OwnPtr<CCLayerImpl> grandChild1 = CCLayerImpl::create(4);
+
+ position = FloatPoint(10, 10);
+ bounds = IntSize(50, 50);
+ setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child1->setDrawsContent(true);
+ child1->setForceRenderSurface(true);
+
+ position = FloatPoint(50, 10);
+ bounds = IntSize(50, 50);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ child2->setDrawsContent(true);
+ child2->setForceRenderSurface(true);
+
+ // Remember that grandChild is positioned with respect to its parent (i.e. child1).
+ // In screen space, the intended position is (10, 50), with size 100 x 50.
+ position = FloatPoint(0, 40);
+ bounds = IntSize(100, 50);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false);
+ grandChild1->setDrawsContent(true);
+ grandChild1->setForceRenderSurface(true);
+
+ child1->addChild(grandChild1.release());
+ root->addChild(child1.release());
+ root->addChild(child2.release());
+ }
+
+ CCLayerImpl* child1 = root->children()[0].get();
+ CCLayerImpl* child2 = root->children()[1].get();
+ CCLayerImpl* grandChild1 = child1->children()[0].get();
+
+ Vector<CCLayerImpl*> renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root->bounds(), 1, 0, dummyMaxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ // Sanity check the scenario we just created.
+ ASSERT_TRUE(child1);
+ ASSERT_TRUE(child2);
+ ASSERT_TRUE(grandChild1);
+ ASSERT_TRUE(child1->renderSurface());
+ ASSERT_TRUE(child2->renderSurface());
+ ASSERT_TRUE(grandChild1->renderSurface());
+ ASSERT_EQ(4u, renderSurfaceLayerList.size());
+ ASSERT_EQ(3u, root->renderSurface()->layerList().size()); // The root surface has the root layer, and child1's and child2's renderSurfaces.
+ ASSERT_EQ(2u, child1->renderSurface()->layerList().size()); // The child1 surface has the child1 layer and grandChild1's renderSurface.
+ ASSERT_EQ(1u, child2->renderSurface()->layerList().size());
+ ASSERT_EQ(1u, grandChild1->renderSurface()->layerList().size());
+ ASSERT_EQ(1, renderSurfaceLayerList[0]->id()); // root layer
+ ASSERT_EQ(2, renderSurfaceLayerList[1]->id()); // child1
+ ASSERT_EQ(4, renderSurfaceLayerList[2]->id()); // grandChild1
+ ASSERT_EQ(3, renderSurfaceLayerList[3]->id()); // child2
+
+ // Nothing overlaps the rootLayer at (1, 1), so hit testing there should find the root layer.
+ IntPoint testPoint = IntPoint(1, 1);
+ CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(1, resultLayer->id());
+
+ // At (15, 15), child1 and root are the only layers. child1 is expected to be on top.
+ testPoint = IntPoint(15, 15);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(2, resultLayer->id());
+
+ // At (51, 20), child1 and child2 overlap. child2 is expected to be on top.
+ testPoint = IntPoint(51, 20);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (80, 51), child2 and grandChild1 overlap. child2 is expected to be on top.
+ testPoint = IntPoint(80, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (51, 51), all layers overlap each other. child2 is expected to be on top of all other layers.
+ testPoint = IntPoint(51, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(3, resultLayer->id());
+
+ // At (20, 51), child1 and grandChild1 overlap. grandChild1 is expected to be on top.
+ testPoint = IntPoint(20, 51);
+ resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList);
+ ASSERT_TRUE(resultLayer);
+ EXPECT_EQ(4, resultLayer->id());
+}
+
+class MockContentLayerDelegate : public ContentLayerDelegate {
+public:
+ MockContentLayerDelegate() { }
+ virtual ~MockContentLayerDelegate() { }
+ virtual void paintContents(SkCanvas*, const IntRect& clip, FloatRect& opaque) OVERRIDE { }
+};
+
+PassRefPtr<ContentLayerChromium> createDrawableContentLayerChromium(ContentLayerDelegate* delegate)
+{
+ RefPtr<ContentLayerChromium> toReturn = ContentLayerChromium::create(delegate);
+ toReturn->setIsDrawable(true);
+ return toReturn.release();
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyLayerTransformsInHighDPI)
+{
+ // Verify draw and screen space transforms of layers not in a surface.
+ MockContentLayerDelegate delegate;
+ WebTransformationMatrix identityMatrix;
+
+ RefPtr<ContentLayerChromium> parent = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true);
+
+ RefPtr<ContentLayerChromium> child = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(2, 2), IntSize(10, 10), true);
+
+ RefPtr<ContentLayerChromium> childNoScale = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(childNoScale.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(2, 2), IntSize(10, 10), true);
+
+ parent->addChild(child);
+ parent->addChild(childNoScale);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+
+ const double deviceScaleFactor = 2.5;
+ parent->setContentsScale(deviceScaleFactor);
+ child->setContentsScale(deviceScaleFactor);
+ EXPECT_EQ(childNoScale->contentsScale(), 1);
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), deviceScaleFactor, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ EXPECT_EQ(1u, renderSurfaceLayerList.size());
+
+ // Verify parent transforms
+ WebTransformationMatrix expectedParentTransform;
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedParentTransform, parent->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedParentTransform, parent->drawTransform());
+
+ // Verify results of transformed parent rects
+ FloatRect parentContentBounds(FloatPoint(), FloatSize(parent->contentBounds()));
+
+ FloatRect parentDrawRect = CCMathUtil::mapClippedRect(parent->drawTransform(), parentContentBounds);
+ FloatRect parentScreenSpaceRect = CCMathUtil::mapClippedRect(parent->screenSpaceTransform(), parentContentBounds);
+
+ FloatRect expectedParentDrawRect(FloatPoint(), parent->bounds());
+ expectedParentDrawRect.scale(deviceScaleFactor);
+ EXPECT_FLOAT_RECT_EQ(expectedParentDrawRect, parentDrawRect);
+ EXPECT_FLOAT_RECT_EQ(expectedParentDrawRect, parentScreenSpaceRect);
+
+ // Verify child transforms
+ WebTransformationMatrix expectedChildTransform;
+ expectedChildTransform.translate(deviceScaleFactor * child->position().x(), deviceScaleFactor * child->position().y());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->screenSpaceTransform());
+
+ // Verify results of transformed child rects
+ FloatRect childContentBounds(FloatPoint(), FloatSize(child->contentBounds()));
+
+ FloatRect childDrawRect = CCMathUtil::mapClippedRect(child->drawTransform(), childContentBounds);
+ FloatRect childScreenSpaceRect = CCMathUtil::mapClippedRect(child->screenSpaceTransform(), childContentBounds);
+
+ FloatRect expectedChildDrawRect(FloatPoint(), child->bounds());
+ expectedChildDrawRect.move(child->position().x(), child->position().y());
+ expectedChildDrawRect.scale(deviceScaleFactor);
+ EXPECT_FLOAT_RECT_EQ(expectedChildDrawRect, childDrawRect);
+ EXPECT_FLOAT_RECT_EQ(expectedChildDrawRect, childScreenSpaceRect);
+
+ // Verify childNoScale transforms
+ WebTransformationMatrix expectedChildNoScaleTransform = child->drawTransform();
+ // All transforms operate on content rects. The child's content rect
+ // incorporates device scale, but the childNoScale does not; add it here.
+ expectedChildNoScaleTransform.scale(deviceScaleFactor);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildNoScaleTransform, childNoScale->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildNoScaleTransform, childNoScale->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceTransformsInHighDPI)
+{
+ MockContentLayerDelegate delegate;
+ WebTransformationMatrix identityMatrix;
+
+ RefPtr<ContentLayerChromium> parent = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(30, 30), true);
+
+ RefPtr<ContentLayerChromium> child = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(2, 2), IntSize(10, 10), true);
+
+ WebTransformationMatrix replicaTransform;
+ replicaTransform.scaleNonUniform(1, -1);
+ RefPtr<ContentLayerChromium> replica = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(replica.get(), replicaTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(2, 2), IntSize(10, 10), true);
+
+ // This layer should end up in the same surface as child, with the same draw
+ // and screen space transforms.
+ RefPtr<ContentLayerChromium> duplicateChildNonOwner = createDrawableContentLayerChromium(&delegate);
+ setLayerPropertiesForTesting(duplicateChildNonOwner.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), true);
+
+ parent->addChild(child);
+ child->addChild(duplicateChildNonOwner);
+ child->setReplicaLayer(replica.get());
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+
+ const double deviceScaleFactor = 1.5;
+ parent->setContentsScale(deviceScaleFactor);
+ child->setContentsScale(deviceScaleFactor);
+ duplicateChildNonOwner->setContentsScale(deviceScaleFactor);
+ replica->setContentsScale(deviceScaleFactor);
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent->bounds(), deviceScaleFactor, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ // We should have two render surfaces. The root's render surface and child's
+ // render surface (it needs one because it has a replica layer).
+ EXPECT_EQ(2u, renderSurfaceLayerList.size());
+
+ WebTransformationMatrix expectedParentTransform;
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedParentTransform, parent->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedParentTransform, parent->drawTransform());
+
+ WebTransformationMatrix expectedDrawTransform;
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedDrawTransform, child->drawTransform());
+
+ WebTransformationMatrix expectedScreenSpaceTransform;
+ expectedScreenSpaceTransform.translate(deviceScaleFactor * child->position().x(), deviceScaleFactor * child->position().y());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedScreenSpaceTransform, child->screenSpaceTransform());
+
+ WebTransformationMatrix expectedDuplicateChildDrawTransform = child->drawTransform();
+ EXPECT_TRANSFORMATION_MATRIX_EQ(child->drawTransform(), duplicateChildNonOwner->drawTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(child->screenSpaceTransform(), duplicateChildNonOwner->screenSpaceTransform());
+ EXPECT_INT_RECT_EQ(child->drawableContentRect(), duplicateChildNonOwner->drawableContentRect());
+ EXPECT_EQ(child->contentBounds(), duplicateChildNonOwner->contentBounds());
+
+ WebTransformationMatrix expectedRenderSurfaceDrawTransform;
+ expectedRenderSurfaceDrawTransform.translate(deviceScaleFactor * child->position().x(), deviceScaleFactor * child->position().y());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedRenderSurfaceDrawTransform, child->renderSurface()->drawTransform());
+
+ WebTransformationMatrix expectedSurfaceDrawTransform;
+ expectedSurfaceDrawTransform.translate(deviceScaleFactor * 2, deviceScaleFactor * 2);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceDrawTransform, child->renderSurface()->drawTransform());
+
+ WebTransformationMatrix expectedSurfaceScreenSpaceTransform;
+ expectedSurfaceScreenSpaceTransform.translate(deviceScaleFactor * 2, deviceScaleFactor * 2);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceScreenSpaceTransform, child->renderSurface()->screenSpaceTransform());
+
+ WebTransformationMatrix expectedReplicaDrawTransform;
+ expectedReplicaDrawTransform.setM22(-1);
+ expectedReplicaDrawTransform.setM41(6);
+ expectedReplicaDrawTransform.setM42(6);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedReplicaDrawTransform, child->renderSurface()->replicaDrawTransform());
+
+ WebTransformationMatrix expectedReplicaScreenSpaceTransform;
+ expectedReplicaScreenSpaceTransform.setM22(-1);
+ expectedReplicaScreenSpaceTransform.setM41(6);
+ expectedReplicaScreenSpaceTransform.setM42(6);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(expectedReplicaScreenSpaceTransform, child->renderSurface()->replicaScreenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifySubtreeSearch)
+{
+ RefPtr<LayerChromium> root = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ RefPtr<LayerChromium> maskLayer = LayerChromium::create();
+ RefPtr<LayerChromium> replicaLayer = LayerChromium::create();
+
+ grandChild->setReplicaLayer(replicaLayer.get());
+ child->addChild(grandChild.get());
+ child->setMaskLayer(maskLayer.get());
+ root->addChild(child.get());
+
+ int nonexistentId = -1;
+ EXPECT_EQ(root, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), root->id()));
+ EXPECT_EQ(child, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), child->id()));
+ EXPECT_EQ(grandChild, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), grandChild->id()));
+ EXPECT_EQ(maskLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), maskLayer->id()));
+ EXPECT_EQ(replicaLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), replicaLayer->id()));
+ EXPECT_EQ(0, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), nonexistentId));
+}
+
+} // namespace
diff --git a/cc/CCLayerTreeHostImpl.cpp b/cc/CCLayerTreeHostImpl.cpp
new file mode 100644
index 0000000..5e90341
--- /dev/null
+++ b/cc/CCLayerTreeHostImpl.cpp
@@ -0,0 +1,1277 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTreeHostImpl.h"
+
+#include "CCActiveGestureAnimation.h"
+#include "CCDamageTracker.h"
+#include "CCDebugRectHistory.h"
+#include "CCDelayBasedTimeSource.h"
+#include "CCFontAtlas.h"
+#include "CCFrameRateCounter.h"
+#include "CCHeadsUpDisplayLayerImpl.h"
+#include "CCLayerIterator.h"
+#include "CCLayerTreeHost.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCMathUtil.h"
+#include "CCOverdrawMetrics.h"
+#include "CCPageScaleAnimation.h"
+#include "CCPrioritizedTextureManager.h"
+#include "CCRenderPassDrawQuad.h"
+#include "CCRendererGL.h"
+#include "CCRenderingStats.h"
+#include "CCScrollbarAnimationController.h"
+#include "CCScrollbarLayerImpl.h"
+#include "CCSettings.h"
+#include "CCSingleThreadProxy.h"
+#include "TextStream.h"
+#include "TraceEvent.h"
+#include <wtf/CurrentTime.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+void didVisibilityChange(WebCore::CCLayerTreeHostImpl* id, bool visible)
+{
+ if (visible) {
+ TRACE_EVENT_ASYNC_BEGIN1("webkit", "CCLayerTreeHostImpl::setVisible", id, "CCLayerTreeHostImpl", id);
+ return;
+ }
+
+ TRACE_EVENT_ASYNC_END0("webkit", "CCLayerTreeHostImpl::setVisible", id);
+}
+
+} // namespace
+
+namespace WebCore {
+
+class CCLayerTreeHostImplTimeSourceAdapter : public CCTimeSourceClient {
+ WTF_MAKE_NONCOPYABLE(CCLayerTreeHostImplTimeSourceAdapter);
+public:
+ static PassOwnPtr<CCLayerTreeHostImplTimeSourceAdapter> create(CCLayerTreeHostImpl* layerTreeHostImpl, PassRefPtr<CCDelayBasedTimeSource> timeSource)
+ {
+ return adoptPtr(new CCLayerTreeHostImplTimeSourceAdapter(layerTreeHostImpl, timeSource));
+ }
+ virtual ~CCLayerTreeHostImplTimeSourceAdapter()
+ {
+ m_timeSource->setClient(0);
+ m_timeSource->setActive(false);
+ }
+
+ virtual void onTimerTick() OVERRIDE
+ {
+ // FIXME: We require that animate be called on the impl thread. This
+ // avoids asserts in single threaded mode. Ideally background ticking
+ // would be handled by the proxy/scheduler and this could be removed.
+ DebugScopedSetImplThread impl;
+
+ m_layerTreeHostImpl->animate(monotonicallyIncreasingTime(), currentTime());
+ }
+
+ void setActive(bool active)
+ {
+ if (active != m_timeSource->active())
+ m_timeSource->setActive(active);
+ }
+
+private:
+ CCLayerTreeHostImplTimeSourceAdapter(CCLayerTreeHostImpl* layerTreeHostImpl, PassRefPtr<CCDelayBasedTimeSource> timeSource)
+ : m_layerTreeHostImpl(layerTreeHostImpl)
+ , m_timeSource(timeSource)
+ {
+ m_timeSource->setClient(this);
+ }
+
+ CCLayerTreeHostImpl* m_layerTreeHostImpl;
+ RefPtr<CCDelayBasedTimeSource> m_timeSource;
+};
+
+PassOwnPtr<CCLayerTreeHostImpl> CCLayerTreeHostImpl::create(const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client)
+{
+ return adoptPtr(new CCLayerTreeHostImpl(settings, client));
+}
+
+CCLayerTreeHostImpl::CCLayerTreeHostImpl(const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client)
+ : m_client(client)
+ , m_sourceFrameNumber(-1)
+ , m_rootScrollLayerImpl(0)
+ , m_currentlyScrollingLayerImpl(0)
+ , m_hudLayerImpl(0)
+ , m_scrollingLayerIdFromPreviousTree(-1)
+ , m_scrollDeltaIsInScreenSpace(false)
+ , m_settings(settings)
+ , m_deviceScaleFactor(1)
+ , m_visible(true)
+ , m_contentsTexturesPurged(false)
+ , m_memoryAllocationLimitBytes(CCPrioritizedTextureManager::defaultMemoryAllocationLimit())
+ , m_pageScale(1)
+ , m_pageScaleDelta(1)
+ , m_sentPageScaleDelta(1)
+ , m_minPageScale(0)
+ , m_maxPageScale(0)
+ , m_backgroundColor(0)
+ , m_hasTransparentBackground(false)
+ , m_needsAnimateLayers(false)
+ , m_pinchGestureActive(false)
+ , m_fpsCounter(CCFrameRateCounter::create())
+ , m_debugRectHistory(CCDebugRectHistory::create())
+{
+ ASSERT(CCProxy::isImplThread());
+ didVisibilityChange(this, m_visible);
+}
+
+CCLayerTreeHostImpl::~CCLayerTreeHostImpl()
+{
+ ASSERT(CCProxy::isImplThread());
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::~CCLayerTreeHostImpl()");
+
+ if (m_rootLayerImpl)
+ clearRenderSurfaces();
+}
+
+void CCLayerTreeHostImpl::beginCommit()
+{
+}
+
+void CCLayerTreeHostImpl::commitComplete()
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::commitComplete");
+ // Recompute max scroll position; must be after layer content bounds are
+ // updated.
+ updateMaxScrollPosition();
+}
+
+bool CCLayerTreeHostImpl::canDraw()
+{
+ if (!m_rootLayerImpl) {
+ TRACE_EVENT_INSTANT0("cc", "CCLayerTreeHostImpl::canDraw no root layer");
+ return false;
+ }
+ if (deviceViewportSize().isEmpty()) {
+ TRACE_EVENT_INSTANT0("cc", "CCLayerTreeHostImpl::canDraw empty viewport");
+ return false;
+ }
+ if (!m_renderer) {
+ TRACE_EVENT_INSTANT0("cc", "CCLayerTreeHostImpl::canDraw no renderer");
+ return false;
+ }
+ if (m_contentsTexturesPurged) {
+ TRACE_EVENT_INSTANT0("cc", "CCLayerTreeHostImpl::canDraw contents textures purged");
+ return false;
+ }
+ return true;
+}
+
+CCGraphicsContext* CCLayerTreeHostImpl::context() const
+{
+ return m_context.get();
+}
+
+void CCLayerTreeHostImpl::animate(double monotonicTime, double wallClockTime)
+{
+ animatePageScale(monotonicTime);
+ animateLayers(monotonicTime, wallClockTime);
+ animateGestures(monotonicTime);
+ animateScrollbars(monotonicTime);
+}
+
+void CCLayerTreeHostImpl::startPageScaleAnimation(const IntSize& targetPosition, bool anchorPoint, float pageScale, double startTime, double duration)
+{
+ if (!m_rootScrollLayerImpl)
+ return;
+
+ IntSize scrollTotal = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
+ scrollTotal.scale(m_pageScaleDelta);
+ float scaleTotal = m_pageScale * m_pageScaleDelta;
+ IntSize scaledContentSize = contentSize();
+ scaledContentSize.scale(m_pageScaleDelta);
+
+ m_pageScaleAnimation = CCPageScaleAnimation::create(scrollTotal, scaleTotal, m_deviceViewportSize, scaledContentSize, startTime);
+
+ if (anchorPoint) {
+ IntSize windowAnchor(targetPosition);
+ windowAnchor.scale(scaleTotal / pageScale);
+ windowAnchor -= scrollTotal;
+ m_pageScaleAnimation->zoomWithAnchor(windowAnchor, pageScale, duration);
+ } else
+ m_pageScaleAnimation->zoomTo(targetPosition, pageScale, duration);
+
+ m_client->setNeedsRedrawOnImplThread();
+ m_client->setNeedsCommitOnImplThread();
+}
+
+void CCLayerTreeHostImpl::setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAnimation> gestureAnimation)
+{
+ m_activeGestureAnimation = gestureAnimation;
+
+ if (m_activeGestureAnimation)
+ m_client->setNeedsRedrawOnImplThread();
+}
+
+void CCLayerTreeHostImpl::scheduleAnimation()
+{
+ m_client->setNeedsRedrawOnImplThread();
+}
+
+void CCLayerTreeHostImpl::trackDamageForAllSurfaces(CCLayerImpl* rootDrawLayer, const CCLayerList& renderSurfaceLayerList)
+{
+ // For now, we use damage tracking to compute a global scissor. To do this, we must
+ // compute all damage tracking before drawing anything, so that we know the root
+ // damage rect. The root damage rect is then used to scissor each surface.
+
+ for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
+ CCLayerImpl* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex];
+ CCRenderSurface* renderSurface = renderSurfaceLayer->renderSurface();
+ ASSERT(renderSurface);
+ renderSurface->damageTracker()->updateDamageTrackingState(renderSurface->layerList(), renderSurfaceLayer->id(), renderSurface->surfacePropertyChangedOnlyFromDescendant(), renderSurface->contentRect(), renderSurfaceLayer->maskLayer(), renderSurfaceLayer->filters());
+ }
+}
+
+void CCLayerTreeHostImpl::calculateRenderSurfaceLayerList(CCLayerList& renderSurfaceLayerList)
+{
+ ASSERT(renderSurfaceLayerList.isEmpty());
+ ASSERT(m_rootLayerImpl);
+ ASSERT(m_renderer); // For maxTextureSize.
+
+ {
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::calcDrawEtc");
+ CCLayerTreeHostCommon::calculateDrawTransforms(m_rootLayerImpl.get(), deviceViewportSize(), m_deviceScaleFactor, &m_layerSorter, rendererCapabilities().maxTextureSize, renderSurfaceLayerList);
+ CCLayerTreeHostCommon::calculateVisibleRects(renderSurfaceLayerList);
+
+ trackDamageForAllSurfaces(m_rootLayerImpl.get(), renderSurfaceLayerList);
+ }
+}
+
+bool CCLayerTreeHostImpl::calculateRenderPasses(FrameData& frame)
+{
+ ASSERT(frame.renderPasses.isEmpty());
+
+ calculateRenderSurfaceLayerList(*frame.renderSurfaceLayerList);
+
+ TRACE_EVENT1("cc", "CCLayerTreeHostImpl::calculateRenderPasses", "renderSurfaceLayerList.size()", static_cast<long long unsigned>(frame.renderSurfaceLayerList->size()));
+
+ // Create the render passes in dependency order.
+ HashMap<CCRenderSurface*, CCRenderPass*> surfacePassMap;
+ for (int surfaceIndex = frame.renderSurfaceLayerList->size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
+ CCLayerImpl* renderSurfaceLayer = (*frame.renderSurfaceLayerList)[surfaceIndex];
+ CCRenderSurface* renderSurface = renderSurfaceLayer->renderSurface();
+
+ int renderPassId = renderSurfaceLayer->id();
+ IntRect outputRect = renderSurface->contentRect();
+ const WebTransformationMatrix& transformToRootTarget = renderSurface->screenSpaceTransform();
+ OwnPtr<CCRenderPass> pass = CCRenderPass::create(renderPassId, outputRect, transformToRootTarget);
+ pass->setDamageRect(renderSurface->damageTracker()->currentDamageRect());
+ pass->setFilters(renderSurfaceLayer->filters());
+ pass->setBackgroundFilters(renderSurfaceLayer->backgroundFilters());
+
+ surfacePassMap.add(renderSurface, pass.get());
+ frame.renderPasses.append(pass.get());
+ frame.renderPassesById.add(renderPassId, pass.release());
+ }
+
+ bool recordMetricsForFrame = true; // FIXME: In the future, disable this when about:tracing is off.
+ CCOcclusionTrackerImpl occlusionTracker(m_rootLayerImpl->renderSurface()->contentRect(), recordMetricsForFrame);
+ occlusionTracker.setMinimumTrackingSize(m_settings.minimumOcclusionTrackingSize);
+
+ if (settings().showOccludingRects)
+ occlusionTracker.setOccludingScreenSpaceRectsContainer(&frame.occludingScreenSpaceRects);
+
+ // Add quads to the Render passes in FrontToBack order to allow for testing occlusion and performing culling during the tree walk.
+ typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
+
+ // Typically when we are missing a texture and use a checkerboard quad, we still draw the frame. However when the layer being
+ // checkerboarded is moving due to an impl-animation, we drop the frame to avoid flashing due to the texture suddenly appearing
+ // in the future.
+ bool drawFrame = true;
+
+ CCLayerIteratorType end = CCLayerIteratorType::end(frame.renderSurfaceLayerList);
+ for (CCLayerIteratorType it = CCLayerIteratorType::begin(frame.renderSurfaceLayerList); it != end; ++it) {
+ CCRenderSurface* renderSurface = it.targetRenderSurfaceLayer()->renderSurface();
+ CCRenderPass* pass = surfacePassMap.get(renderSurface);
+ bool hadMissingTiles = false;
+
+ occlusionTracker.enterLayer(it);
+
+ if (it.representsContributingRenderSurface()) {
+ CCRenderPass* contributingRenderPass = surfacePassMap.get(it->renderSurface());
+ pass->appendQuadsForRenderSurfaceLayer(*it, contributingRenderPass, &occlusionTracker);
+ } else if (it.representsItself() && !it->visibleContentRect().isEmpty()) {
+ bool hasOcclusionFromOutsideTargetSurface;
+ if (occlusionTracker.occluded(*it, it->visibleContentRect(), &hasOcclusionFromOutsideTargetSurface)) {
+ if (hasOcclusionFromOutsideTargetSurface)
+ pass->setHasOcclusionFromOutsideTargetSurface(hasOcclusionFromOutsideTargetSurface);
+ } else {
+ it->willDraw(m_resourceProvider.get());
+ frame.willDrawLayers.append(*it);
+ pass->appendQuadsForLayer(*it, &occlusionTracker, hadMissingTiles);
+ }
+ }
+
+ if (hadMissingTiles) {
+ bool layerHasAnimatingTransform = it->screenSpaceTransformIsAnimating() || it->drawTransformIsAnimating();
+ if (layerHasAnimatingTransform)
+ drawFrame = false;
+ }
+
+ occlusionTracker.leaveLayer(it);
+ }
+
+#if !ASSERT_DISABLED
+ for (size_t i = 0; i < frame.renderPasses.size(); ++i) {
+ for (size_t j = 0; j < frame.renderPasses[i]->quadList().size(); ++j)
+ ASSERT(frame.renderPasses[i]->quadList()[j]->sharedQuadStateId() >= 0);
+ }
+#endif
+
+ if (!m_hasTransparentBackground) {
+ frame.renderPasses.last()->setHasTransparentBackground(false);
+ frame.renderPasses.last()->appendQuadsToFillScreen(m_rootLayerImpl.get(), m_backgroundColor, occlusionTracker);
+ }
+
+ if (drawFrame)
+ occlusionTracker.overdrawMetrics().recordMetrics(this);
+
+ removeRenderPasses(CullRenderPassesWithNoQuads(), frame);
+ m_renderer->decideRenderPassAllocationsForFrame(frame.renderPasses);
+ removeRenderPasses(CullRenderPassesWithCachedTextures(*m_renderer), frame);
+
+ return drawFrame;
+}
+
+void CCLayerTreeHostImpl::animateLayersRecursive(CCLayerImpl* current, double monotonicTime, double wallClockTime, CCAnimationEventsVector* events, bool& didAnimate, bool& needsAnimateLayers)
+{
+ bool subtreeNeedsAnimateLayers = false;
+
+ CCLayerAnimationController* currentController = current->layerAnimationController();
+
+ bool hadActiveAnimation = currentController->hasActiveAnimation();
+ currentController->animate(monotonicTime, events);
+ bool startedAnimation = events->size() > 0;
+
+ // We animated if we either ticked a running animation, or started a new animation.
+ if (hadActiveAnimation || startedAnimation)
+ didAnimate = true;
+
+ // If the current controller still has an active animation, we must continue animating layers.
+ if (currentController->hasActiveAnimation())
+ subtreeNeedsAnimateLayers = true;
+
+ for (size_t i = 0; i < current->children().size(); ++i) {
+ bool childNeedsAnimateLayers = false;
+ animateLayersRecursive(current->children()[i].get(), monotonicTime, wallClockTime, events, didAnimate, childNeedsAnimateLayers);
+ if (childNeedsAnimateLayers)
+ subtreeNeedsAnimateLayers = true;
+ }
+
+ needsAnimateLayers = subtreeNeedsAnimateLayers;
+}
+
+void CCLayerTreeHostImpl::setBackgroundTickingEnabled(bool enabled)
+{
+ // Lazily create the timeSource adapter so that we can vary the interval for testing.
+ if (!m_timeSourceClientAdapter)
+ m_timeSourceClientAdapter = CCLayerTreeHostImplTimeSourceAdapter::create(this, CCDelayBasedTimeSource::create(lowFrequencyAnimationInterval(), CCProxy::currentThread()));
+
+ m_timeSourceClientAdapter->setActive(enabled);
+}
+
+IntSize CCLayerTreeHostImpl::contentSize() const
+{
+ // TODO(aelias): Hardcoding the first child here is weird. Think of
+ // a cleaner way to get the contentBounds on the Impl side.
+ if (!m_rootScrollLayerImpl || m_rootScrollLayerImpl->children().isEmpty())
+ return IntSize();
+ return m_rootScrollLayerImpl->children()[0]->contentBounds();
+}
+
+static inline CCRenderPass* findRenderPassById(int renderPassId, const CCLayerTreeHostImpl::FrameData& frame)
+{
+ CCRenderPassIdHashMap::const_iterator it = frame.renderPassesById.find(renderPassId);
+ ASSERT(it != frame.renderPassesById.end());
+ return it->second.get();
+}
+
+static void removeRenderPassesRecursive(int removeRenderPassId, CCLayerTreeHostImpl::FrameData& frame)
+{
+ CCRenderPass* removeRenderPass = findRenderPassById(removeRenderPassId, frame);
+ size_t removeIndex = frame.renderPasses.find(removeRenderPass);
+
+ // The pass was already removed by another quad - probably the original, and we are the replica.
+ if (removeIndex == notFound)
+ return;
+
+ const CCRenderPass* removedPass = frame.renderPasses[removeIndex];
+ frame.renderPasses.remove(removeIndex);
+
+ // Now follow up for all RenderPass quads and remove their RenderPasses recursively.
+ const CCQuadList& quadList = removedPass->quadList();
+ CCQuadList::constBackToFrontIterator quadListIterator = quadList.backToFrontBegin();
+ for (; quadListIterator != quadList.backToFrontEnd(); ++quadListIterator) {
+ CCDrawQuad* currentQuad = (*quadListIterator).get();
+ if (currentQuad->material() != CCDrawQuad::RenderPass)
+ continue;
+
+ int nextRemoveRenderPassId = CCRenderPassDrawQuad::materialCast(currentQuad)->renderPassId();
+ removeRenderPassesRecursive(nextRemoveRenderPassId, frame);
+ }
+}
+
+bool CCLayerTreeHostImpl::CullRenderPassesWithCachedTextures::shouldRemoveRenderPass(const CCRenderPassDrawQuad& quad, const FrameData&) const
+{
+ return quad.contentsChangedSinceLastFrame().isEmpty() && m_renderer.haveCachedResourcesForRenderPassId(quad.renderPassId());
+}
+
+bool CCLayerTreeHostImpl::CullRenderPassesWithNoQuads::shouldRemoveRenderPass(const CCRenderPassDrawQuad& quad, const FrameData& frame) const
+{
+ const CCRenderPass* renderPass = findRenderPassById(quad.renderPassId(), frame);
+ size_t passIndex = frame.renderPasses.find(renderPass);
+
+ bool renderPassAlreadyRemoved = passIndex == notFound;
+ if (renderPassAlreadyRemoved)
+ return false;
+
+ // If any quad or RenderPass draws into this RenderPass, then keep it.
+ const CCQuadList& quadList = frame.renderPasses[passIndex]->quadList();
+ for (CCQuadList::constBackToFrontIterator quadListIterator = quadList.backToFrontBegin(); quadListIterator != quadList.backToFrontEnd(); ++quadListIterator) {
+ CCDrawQuad* currentQuad = quadListIterator->get();
+
+ if (currentQuad->material() != CCDrawQuad::RenderPass)
+ return false;
+
+ const CCRenderPass* contributingPass = findRenderPassById(CCRenderPassDrawQuad::materialCast(currentQuad)->renderPassId(), frame);
+ if (frame.renderPasses.contains(contributingPass))
+ return false;
+ }
+ return true;
+}
+
+// Defined for linking tests.
+template void CCLayerTreeHostImpl::removeRenderPasses<CCLayerTreeHostImpl::CullRenderPassesWithCachedTextures>(CullRenderPassesWithCachedTextures, FrameData&);
+template void CCLayerTreeHostImpl::removeRenderPasses<CCLayerTreeHostImpl::CullRenderPassesWithNoQuads>(CullRenderPassesWithNoQuads, FrameData&);
+
+// static
+template<typename RenderPassCuller>
+void CCLayerTreeHostImpl::removeRenderPasses(RenderPassCuller culler, FrameData& frame)
+{
+ for (size_t it = culler.renderPassListBegin(frame.renderPasses); it != culler.renderPassListEnd(frame.renderPasses); it = culler.renderPassListNext(it)) {
+ const CCRenderPass* currentPass = frame.renderPasses[it];
+ const CCQuadList& quadList = currentPass->quadList();
+ CCQuadList::constBackToFrontIterator quadListIterator = quadList.backToFrontBegin();
+
+ for (; quadListIterator != quadList.backToFrontEnd(); ++quadListIterator) {
+ CCDrawQuad* currentQuad = quadListIterator->get();
+
+ if (currentQuad->material() != CCDrawQuad::RenderPass)
+ continue;
+
+ CCRenderPassDrawQuad* renderPassQuad = static_cast<CCRenderPassDrawQuad*>(currentQuad);
+ if (!culler.shouldRemoveRenderPass(*renderPassQuad, frame))
+ continue;
+
+ // We are changing the vector in the middle of iteration. Because we
+ // delete render passes that draw into the current pass, we are
+ // guaranteed that any data from the iterator to the end will not
+ // change. So, capture the iterator position from the end of the
+ // list, and restore it after the change.
+ int positionFromEnd = frame.renderPasses.size() - it;
+ removeRenderPassesRecursive(renderPassQuad->renderPassId(), frame);
+ it = frame.renderPasses.size() - positionFromEnd;
+ ASSERT(it >= 0);
+ }
+ }
+}
+
+bool CCLayerTreeHostImpl::prepareToDraw(FrameData& frame)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::prepareToDraw");
+ ASSERT(canDraw());
+
+ frame.renderSurfaceLayerList = &m_renderSurfaceLayerList;
+ frame.renderPasses.clear();
+ frame.renderPassesById.clear();
+ frame.renderSurfaceLayerList->clear();
+ frame.willDrawLayers.clear();
+
+ if (!calculateRenderPasses(frame))
+ return false;
+
+ // If we return true, then we expect drawLayers() to be called before this function is called again.
+ return true;
+}
+
+void CCLayerTreeHostImpl::releaseContentsTextures()
+{
+ if (m_contentsTexturesPurged)
+ return;
+ m_resourceProvider->deleteOwnedResources(CCRenderer::ContentPool);
+ m_contentsTexturesPurged = true;
+ m_client->setNeedsCommitOnImplThread();
+}
+
+void CCLayerTreeHostImpl::setMemoryAllocationLimitBytes(size_t bytes)
+{
+ if (m_memoryAllocationLimitBytes == bytes)
+ return;
+ m_memoryAllocationLimitBytes = bytes;
+
+ ASSERT(bytes);
+ m_client->setNeedsCommitOnImplThread();
+}
+
+void CCLayerTreeHostImpl::onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds)
+{
+ m_client->onVSyncParametersChanged(monotonicTimebase, intervalInSeconds);
+}
+
+void CCLayerTreeHostImpl::drawLayers(const FrameData& frame)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::drawLayers");
+ ASSERT(canDraw());
+ ASSERT(!frame.renderPasses.isEmpty());
+
+ // FIXME: use the frame begin time from the overall compositor scheduler.
+ // This value is currently inaccessible because it is up in Chromium's
+ // RenderWidget.
+ m_fpsCounter->markBeginningOfFrame(currentTime());
+
+ if (m_settings.showDebugRects())
+ m_debugRectHistory->saveDebugRectsForCurrentFrame(m_rootLayerImpl.get(), *frame.renderSurfaceLayerList, frame.occludingScreenSpaceRects, settings());
+
+ // Because the contents of the HUD depend on everything else in the frame, the contents
+ // of its texture are updated as the last thing before the frame is drawn.
+ if (m_hudLayerImpl)
+ m_hudLayerImpl->updateHudTexture(m_resourceProvider.get());
+
+ m_renderer->drawFrame(frame.renderPasses, frame.renderPassesById);
+
+ // Once a RenderPass has been drawn, its damage should be cleared in
+ // case the RenderPass will be reused next frame.
+ for (unsigned int i = 0; i < frame.renderPasses.size(); i++)
+ frame.renderPasses[i]->setDamageRect(FloatRect());
+
+ // The next frame should start by assuming nothing has changed, and changes are noted as they occur.
+ for (unsigned int i = 0; i < frame.renderSurfaceLayerList->size(); i++)
+ (*frame.renderSurfaceLayerList)[i]->renderSurface()->damageTracker()->didDrawDamagedArea();
+ m_rootLayerImpl->resetAllChangeTrackingForSubtree();
+}
+
+void CCLayerTreeHostImpl::didDrawAllLayers(const FrameData& frame)
+{
+ for (size_t i = 0; i < frame.willDrawLayers.size(); ++i)
+ frame.willDrawLayers[i]->didDraw(m_resourceProvider.get());
+}
+
+void CCLayerTreeHostImpl::finishAllRendering()
+{
+ if (m_renderer)
+ m_renderer->finish();
+}
+
+bool CCLayerTreeHostImpl::isContextLost()
+{
+ return m_renderer && m_renderer->isContextLost();
+}
+
+const RendererCapabilities& CCLayerTreeHostImpl::rendererCapabilities() const
+{
+ return m_renderer->capabilities();
+}
+
+bool CCLayerTreeHostImpl::swapBuffers()
+{
+ ASSERT(m_renderer);
+
+ m_fpsCounter->markEndOfFrame();
+ return m_renderer->swapBuffers();
+}
+
+void CCLayerTreeHostImpl::didLoseContext()
+{
+ m_client->didLoseContextOnImplThread();
+}
+
+void CCLayerTreeHostImpl::onSwapBuffersComplete()
+{
+ m_client->onSwapBuffersCompleteOnImplThread();
+}
+
+void CCLayerTreeHostImpl::readback(void* pixels, const IntRect& rect)
+{
+ ASSERT(m_renderer);
+ m_renderer->getFramebufferPixels(pixels, rect);
+}
+
+static CCLayerImpl* findRootScrollLayer(CCLayerImpl* layer)
+{
+ if (!layer)
+ return 0;
+
+ if (layer->scrollable())
+ return layer;
+
+ for (size_t i = 0; i < layer->children().size(); ++i) {
+ CCLayerImpl* found = findRootScrollLayer(layer->children()[i].get());
+ if (found)
+ return found;
+ }
+
+ return 0;
+}
+
+// Content layers can be either directly scrollable or contained in an outer
+// scrolling layer which applies the scroll transform. Given a content layer,
+// this function returns the associated scroll layer if any.
+static CCLayerImpl* findScrollLayerForContentLayer(CCLayerImpl* layerImpl)
+{
+ if (!layerImpl)
+ return 0;
+
+ if (layerImpl->scrollable())
+ return layerImpl;
+
+ if (layerImpl->drawsContent() && layerImpl->parent() && layerImpl->parent()->scrollable())
+ return layerImpl->parent();
+
+ return 0;
+}
+
+void CCLayerTreeHostImpl::setRootLayer(PassOwnPtr<CCLayerImpl> layer)
+{
+ m_rootLayerImpl = layer;
+ m_rootScrollLayerImpl = findRootScrollLayer(m_rootLayerImpl.get());
+ m_currentlyScrollingLayerImpl = 0;
+
+ if (m_rootLayerImpl && m_scrollingLayerIdFromPreviousTree != -1)
+ m_currentlyScrollingLayerImpl = CCLayerTreeHostCommon::findLayerInSubtree(m_rootLayerImpl.get(), m_scrollingLayerIdFromPreviousTree);
+
+ m_scrollingLayerIdFromPreviousTree = -1;
+}
+
+PassOwnPtr<CCLayerImpl> CCLayerTreeHostImpl::detachLayerTree()
+{
+ // Clear all data structures that have direct references to the layer tree.
+ m_scrollingLayerIdFromPreviousTree = m_currentlyScrollingLayerImpl ? m_currentlyScrollingLayerImpl->id() : -1;
+ m_currentlyScrollingLayerImpl = 0;
+ m_renderSurfaceLayerList.clear();
+
+ return m_rootLayerImpl.release();
+}
+
+void CCLayerTreeHostImpl::setVisible(bool visible)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ if (m_visible == visible)
+ return;
+ m_visible = visible;
+ didVisibilityChange(this, m_visible);
+
+ if (!m_renderer)
+ return;
+
+ m_renderer->setVisible(visible);
+
+ setBackgroundTickingEnabled(!m_visible && m_needsAnimateLayers);
+}
+
+bool CCLayerTreeHostImpl::initializeRenderer(PassOwnPtr<CCGraphicsContext> context, TextureUploaderOption textureUploader)
+{
+ if (!context->bindToClient(this))
+ return false;
+
+ WebKit::WebGraphicsContext3D* context3d = context->context3D();
+
+ if (!context3d) {
+ // FIXME: Implement this path for software compositing.
+ return false;
+ }
+
+ OwnPtr<CCGraphicsContext> contextRef(context);
+ OwnPtr<CCResourceProvider> resourceProvider = CCResourceProvider::create(contextRef.get());
+ OwnPtr<CCRendererGL> renderer;
+ if (resourceProvider.get())
+ renderer = CCRendererGL::create(this, resourceProvider.get(), textureUploader);
+
+ // Since we now have a new context/renderer, we cannot continue to use the old
+ // resources (i.e. renderSurfaces and texture IDs).
+ if (m_rootLayerImpl) {
+ clearRenderSurfaces();
+ sendDidLoseContextRecursive(m_rootLayerImpl.get());
+ }
+
+ m_renderer = renderer.release();
+ m_resourceProvider = resourceProvider.release();
+ if (m_renderer)
+ m_context = contextRef.release();
+
+ if (!m_visible && m_renderer)
+ m_renderer->setVisible(m_visible);
+
+ return m_renderer;
+}
+
+void CCLayerTreeHostImpl::setViewportSize(const IntSize& layoutViewportSize, const IntSize& deviceViewportSize)
+{
+ if (layoutViewportSize == m_layoutViewportSize && deviceViewportSize == m_deviceViewportSize)
+ return;
+
+ m_layoutViewportSize = layoutViewportSize;
+ m_deviceViewportSize = deviceViewportSize;
+
+ updateMaxScrollPosition();
+
+ if (m_renderer)
+ m_renderer->viewportChanged();
+}
+
+static void adjustScrollsForPageScaleChange(CCLayerImpl* layerImpl, float pageScaleChange)
+{
+ if (!layerImpl)
+ return;
+
+ if (layerImpl->scrollable()) {
+ // We need to convert impl-side scroll deltas to pageScale space.
+ FloatSize scrollDelta = layerImpl->scrollDelta();
+ scrollDelta.scale(pageScaleChange);
+ layerImpl->setScrollDelta(scrollDelta);
+ }
+
+ for (size_t i = 0; i < layerImpl->children().size(); ++i)
+ adjustScrollsForPageScaleChange(layerImpl->children()[i].get(), pageScaleChange);
+}
+
+void CCLayerTreeHostImpl::setDeviceScaleFactor(float deviceScaleFactor)
+{
+ if (deviceScaleFactor == m_deviceScaleFactor)
+ return;
+ m_deviceScaleFactor = deviceScaleFactor;
+}
+
+
+void CCLayerTreeHostImpl::setPageScaleFactorAndLimits(float pageScale, float minPageScale, float maxPageScale)
+{
+ if (!pageScale)
+ return;
+
+ if (m_sentPageScaleDelta == 1 && pageScale == m_pageScale && minPageScale == m_minPageScale && maxPageScale == m_maxPageScale)
+ return;
+
+ m_minPageScale = minPageScale;
+ m_maxPageScale = maxPageScale;
+
+ float pageScaleChange = pageScale / m_pageScale;
+ m_pageScale = pageScale;
+
+ if (pageScaleChange != 1)
+ adjustScrollsForPageScaleChange(m_rootScrollLayerImpl, pageScaleChange);
+
+ // Clamp delta to limits and refresh display matrix.
+ setPageScaleDelta(m_pageScaleDelta / m_sentPageScaleDelta);
+ m_sentPageScaleDelta = 1;
+ if (m_rootScrollLayerImpl)
+ m_rootScrollLayerImpl->setPageScaleDelta(m_pageScaleDelta);
+}
+
+void CCLayerTreeHostImpl::setPageScaleDelta(float delta)
+{
+ // Clamp to the current min/max limits.
+ float finalMagnifyScale = m_pageScale * delta;
+ if (m_minPageScale && finalMagnifyScale < m_minPageScale)
+ delta = m_minPageScale / m_pageScale;
+ else if (m_maxPageScale && finalMagnifyScale > m_maxPageScale)
+ delta = m_maxPageScale / m_pageScale;
+
+ if (delta == m_pageScaleDelta)
+ return;
+
+ m_pageScaleDelta = delta;
+
+ updateMaxScrollPosition();
+ if (m_rootScrollLayerImpl)
+ m_rootScrollLayerImpl->setPageScaleDelta(m_pageScaleDelta);
+}
+
+void CCLayerTreeHostImpl::updateMaxScrollPosition()
+{
+ if (!m_rootScrollLayerImpl || !m_rootScrollLayerImpl->children().size())
+ return;
+
+ FloatSize viewBounds = m_deviceViewportSize;
+ if (CCLayerImpl* clipLayer = m_rootScrollLayerImpl->parent()) {
+ // Compensate for non-overlay scrollbars.
+ if (clipLayer->masksToBounds()) {
+ viewBounds = clipLayer->bounds();
+ viewBounds.scale(m_deviceScaleFactor);
+ }
+ }
+ viewBounds.scale(1 / m_pageScaleDelta);
+
+ // maxScroll is computed in physical pixels, but scroll positions are in layout pixels.
+ IntSize maxScroll = contentSize() - expandedIntSize(viewBounds);
+ maxScroll.scale(1 / m_deviceScaleFactor);
+ // The viewport may be larger than the contents in some cases, such as
+ // having a vertical scrollbar but no horizontal overflow.
+ maxScroll.clampNegativeToZero();
+
+ m_rootScrollLayerImpl->setMaxScrollPosition(maxScroll);
+}
+
+void CCLayerTreeHostImpl::setNeedsRedraw()
+{
+ m_client->setNeedsRedrawOnImplThread();
+}
+
+bool CCLayerTreeHostImpl::ensureRenderSurfaceLayerList()
+{
+ if (!m_rootLayerImpl)
+ return false;
+ if (!m_renderer)
+ return false;
+
+ // We need both a non-empty render surface layer list and a root render
+ // surface to be able to iterate over the visible layers.
+ if (m_renderSurfaceLayerList.size() && m_rootLayerImpl->renderSurface())
+ return true;
+
+ // If we are called after setRootLayer() but before prepareToDraw(), we need
+ // to recalculate the visible layers. This prevents being unable to scroll
+ // during part of a commit.
+ m_renderSurfaceLayerList.clear();
+ calculateRenderSurfaceLayerList(m_renderSurfaceLayerList);
+
+ return m_renderSurfaceLayerList.size();
+}
+
+CCInputHandlerClient::ScrollStatus CCLayerTreeHostImpl::scrollBegin(const IntPoint& viewportPoint, CCInputHandlerClient::ScrollInputType type)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::scrollBegin");
+
+ ASSERT(!m_currentlyScrollingLayerImpl);
+ clearCurrentlyScrollingLayer();
+
+ if (!ensureRenderSurfaceLayerList())
+ return ScrollIgnored;
+
+ IntPoint deviceViewportPoint = viewportPoint;
+ deviceViewportPoint.scale(m_deviceScaleFactor, m_deviceScaleFactor);
+
+ // First find out which layer was hit from the saved list of visible layers
+ // in the most recent frame.
+ CCLayerImpl* layerImpl = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(viewportPoint, m_renderSurfaceLayerList);
+
+ // Walk up the hierarchy and look for a scrollable layer.
+ CCLayerImpl* potentiallyScrollingLayerImpl = 0;
+ for (; layerImpl; layerImpl = layerImpl->parent()) {
+ // The content layer can also block attempts to scroll outside the main thread.
+ if (layerImpl->tryScroll(deviceViewportPoint, type) == ScrollOnMainThread)
+ return ScrollOnMainThread;
+
+ CCLayerImpl* scrollLayerImpl = findScrollLayerForContentLayer(layerImpl);
+ if (!scrollLayerImpl)
+ continue;
+
+ ScrollStatus status = scrollLayerImpl->tryScroll(viewportPoint, type);
+
+ // If any layer wants to divert the scroll event to the main thread, abort.
+ if (status == ScrollOnMainThread)
+ return ScrollOnMainThread;
+
+ if (status == ScrollStarted && !potentiallyScrollingLayerImpl)
+ potentiallyScrollingLayerImpl = scrollLayerImpl;
+ }
+
+ if (potentiallyScrollingLayerImpl) {
+ m_currentlyScrollingLayerImpl = potentiallyScrollingLayerImpl;
+ // Gesture events need to be transformed from screen coordinates to local layer coordinates
+ // so that the scrolling contents exactly follow the user's finger. In contrast, wheel
+ // events are already in local layer coordinates so we can just apply them directly.
+ m_scrollDeltaIsInScreenSpace = (type == Gesture);
+ return ScrollStarted;
+ }
+ return ScrollIgnored;
+}
+
+static FloatSize scrollLayerWithScreenSpaceDelta(CCLayerImpl& layerImpl, const FloatPoint& screenSpacePoint, const FloatSize& screenSpaceDelta)
+{
+ // Layers with non-invertible screen space transforms should not have passed the scroll hit
+ // test in the first place.
+ ASSERT(layerImpl.screenSpaceTransform().isInvertible());
+ WebTransformationMatrix inverseScreenSpaceTransform = layerImpl.screenSpaceTransform().inverse();
+
+ // First project the scroll start and end points to local layer space to find the scroll delta
+ // in layer coordinates.
+ bool startClipped, endClipped;
+ FloatPoint screenSpaceEndPoint = screenSpacePoint + screenSpaceDelta;
+ FloatPoint localStartPoint = CCMathUtil::projectPoint(inverseScreenSpaceTransform, screenSpacePoint, startClipped);
+ FloatPoint localEndPoint = CCMathUtil::projectPoint(inverseScreenSpaceTransform, screenSpaceEndPoint, endClipped);
+
+ // In general scroll point coordinates should not get clipped.
+ ASSERT(!startClipped);
+ ASSERT(!endClipped);
+ if (startClipped || endClipped)
+ return FloatSize();
+
+ // Apply the scroll delta.
+ FloatSize previousDelta(layerImpl.scrollDelta());
+ layerImpl.scrollBy(localEndPoint - localStartPoint);
+
+ // Calculate the applied scroll delta in screen space coordinates.
+ FloatPoint actualLocalEndPoint = localStartPoint + layerImpl.scrollDelta() - previousDelta;
+ FloatPoint actualScreenSpaceEndPoint = CCMathUtil::mapPoint(layerImpl.screenSpaceTransform(), actualLocalEndPoint, endClipped);
+ ASSERT(!endClipped);
+ if (endClipped)
+ return FloatSize();
+ return actualScreenSpaceEndPoint - screenSpacePoint;
+}
+
+static FloatSize scrollLayerWithLocalDelta(CCLayerImpl& layerImpl, const FloatSize& localDelta)
+{
+ FloatSize previousDelta(layerImpl.scrollDelta());
+ layerImpl.scrollBy(localDelta);
+ return layerImpl.scrollDelta() - previousDelta;
+}
+
+void CCLayerTreeHostImpl::scrollBy(const IntPoint& viewportPoint, const IntSize& scrollDelta)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::scrollBy");
+ if (!m_currentlyScrollingLayerImpl)
+ return;
+
+ FloatSize pendingDelta(scrollDelta);
+
+ pendingDelta.scale(m_deviceScaleFactor);
+
+ for (CCLayerImpl* layerImpl = m_currentlyScrollingLayerImpl; layerImpl; layerImpl = layerImpl->parent()) {
+ if (!layerImpl->scrollable())
+ continue;
+
+ FloatSize appliedDelta;
+ if (m_scrollDeltaIsInScreenSpace)
+ appliedDelta = scrollLayerWithScreenSpaceDelta(*layerImpl, viewportPoint, pendingDelta);
+ else
+ appliedDelta = scrollLayerWithLocalDelta(*layerImpl, pendingDelta);
+
+ // If the layer wasn't able to move, try the next one in the hierarchy.
+ float moveThresholdSquared = 0.1 * 0.1;
+ if (appliedDelta.diagonalLengthSquared() < moveThresholdSquared)
+ continue;
+
+ // If the applied delta is within 45 degrees of the input delta, bail out to make it easier
+ // to scroll just one layer in one direction without affecting any of its parents.
+ float angleThreshold = 45;
+ if (CCMathUtil::smallestAngleBetweenVectors(appliedDelta, pendingDelta) < angleThreshold) {
+ pendingDelta = FloatSize();
+ break;
+ }
+
+ // Allow further movement only on an axis perpendicular to the direction in which the layer
+ // moved.
+ FloatSize perpendicularAxis(-appliedDelta.height(), appliedDelta.width());
+ pendingDelta = CCMathUtil::projectVector(pendingDelta, perpendicularAxis);
+
+ if (flooredIntSize(pendingDelta).isZero())
+ break;
+ }
+
+ if (!scrollDelta.isZero() && flooredIntSize(pendingDelta).isEmpty()) {
+ m_client->setNeedsCommitOnImplThread();
+ m_client->setNeedsRedrawOnImplThread();
+ }
+}
+
+void CCLayerTreeHostImpl::clearCurrentlyScrollingLayer()
+{
+ m_currentlyScrollingLayerImpl = 0;
+ m_scrollingLayerIdFromPreviousTree = -1;
+}
+
+void CCLayerTreeHostImpl::scrollEnd()
+{
+ clearCurrentlyScrollingLayer();
+}
+
+void CCLayerTreeHostImpl::pinchGestureBegin()
+{
+ m_pinchGestureActive = true;
+ m_previousPinchAnchor = IntPoint();
+
+ if (m_rootScrollLayerImpl && m_rootScrollLayerImpl->scrollbarAnimationController())
+ m_rootScrollLayerImpl->scrollbarAnimationController()->didPinchGestureBegin();
+}
+
+void CCLayerTreeHostImpl::pinchGestureUpdate(float magnifyDelta,
+ const IntPoint& anchor)
+{
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::pinchGestureUpdate");
+
+ if (!m_rootScrollLayerImpl)
+ return;
+
+ if (m_previousPinchAnchor == IntPoint::zero())
+ m_previousPinchAnchor = anchor;
+
+ // Keep the center-of-pinch anchor specified by (x, y) in a stable
+ // position over the course of the magnify.
+ FloatPoint previousScaleAnchor(m_previousPinchAnchor.x() / m_pageScaleDelta, m_previousPinchAnchor.y() / m_pageScaleDelta);
+ setPageScaleDelta(m_pageScaleDelta * magnifyDelta);
+ FloatPoint newScaleAnchor(anchor.x() / m_pageScaleDelta, anchor.y() / m_pageScaleDelta);
+ FloatSize move = previousScaleAnchor - newScaleAnchor;
+
+ m_previousPinchAnchor = anchor;
+
+ m_rootScrollLayerImpl->scrollBy(roundedIntSize(move));
+
+ if (m_rootScrollLayerImpl->scrollbarAnimationController())
+ m_rootScrollLayerImpl->scrollbarAnimationController()->didPinchGestureUpdate();
+
+ m_client->setNeedsCommitOnImplThread();
+ m_client->setNeedsRedrawOnImplThread();
+}
+
+void CCLayerTreeHostImpl::pinchGestureEnd()
+{
+ m_pinchGestureActive = false;
+
+ if (m_rootScrollLayerImpl && m_rootScrollLayerImpl->scrollbarAnimationController())
+ m_rootScrollLayerImpl->scrollbarAnimationController()->didPinchGestureEnd();
+
+ m_client->setNeedsCommitOnImplThread();
+}
+
+void CCLayerTreeHostImpl::computeDoubleTapZoomDeltas(CCScrollAndScaleSet* scrollInfo)
+{
+ float pageScale = m_pageScaleAnimation->finalPageScale();
+ IntSize scrollOffset = m_pageScaleAnimation->finalScrollOffset();
+ scrollOffset.scale(m_pageScale / pageScale);
+ makeScrollAndScaleSet(scrollInfo, scrollOffset, pageScale);
+}
+
+void CCLayerTreeHostImpl::computePinchZoomDeltas(CCScrollAndScaleSet* scrollInfo)
+{
+ if (!m_rootScrollLayerImpl)
+ return;
+
+ // Only send fake scroll/zoom deltas if we're pinch zooming out by a
+ // significant amount. This also ensures only one fake delta set will be
+ // sent.
+ const float pinchZoomOutSensitivity = 0.95;
+ if (m_pageScaleDelta > pinchZoomOutSensitivity)
+ return;
+
+ // Compute where the scroll offset/page scale would be if fully pinch-zoomed
+ // out from the anchor point.
+ IntSize scrollBegin = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
+ scrollBegin.scale(m_pageScaleDelta);
+ float scaleBegin = m_pageScale * m_pageScaleDelta;
+ float pageScaleDeltaToSend = m_minPageScale / m_pageScale;
+ FloatSize scaledContentsSize = contentSize();
+ scaledContentsSize.scale(pageScaleDeltaToSend);
+
+ FloatSize anchor = toSize(m_previousPinchAnchor);
+ FloatSize scrollEnd = scrollBegin + anchor;
+ scrollEnd.scale(m_minPageScale / scaleBegin);
+ scrollEnd -= anchor;
+ scrollEnd = scrollEnd.shrunkTo(roundedIntSize(scaledContentsSize - m_deviceViewportSize)).expandedTo(FloatSize(0, 0));
+ scrollEnd.scale(1 / pageScaleDeltaToSend);
+ scrollEnd.scale(m_deviceScaleFactor);
+
+ makeScrollAndScaleSet(scrollInfo, roundedIntSize(scrollEnd), m_minPageScale);
+}
+
+void CCLayerTreeHostImpl::makeScrollAndScaleSet(CCScrollAndScaleSet* scrollInfo, const IntSize& scrollOffset, float pageScale)
+{
+ if (!m_rootScrollLayerImpl)
+ return;
+
+ CCLayerTreeHostCommon::ScrollUpdateInfo scroll;
+ scroll.layerId = m_rootScrollLayerImpl->id();
+ scroll.scrollDelta = scrollOffset - toSize(m_rootScrollLayerImpl->scrollPosition());
+ scrollInfo->scrolls.append(scroll);
+ m_rootScrollLayerImpl->setSentScrollDelta(scroll.scrollDelta);
+ m_sentPageScaleDelta = scrollInfo->pageScaleDelta = pageScale / m_pageScale;
+}
+
+static void collectScrollDeltas(CCScrollAndScaleSet* scrollInfo, CCLayerImpl* layerImpl)
+{
+ if (!layerImpl)
+ return;
+
+ if (!layerImpl->scrollDelta().isZero()) {
+ IntSize scrollDelta = flooredIntSize(layerImpl->scrollDelta());
+ CCLayerTreeHostCommon::ScrollUpdateInfo scroll;
+ scroll.layerId = layerImpl->id();
+ scroll.scrollDelta = scrollDelta;
+ scrollInfo->scrolls.append(scroll);
+ layerImpl->setSentScrollDelta(scrollDelta);
+ }
+
+ for (size_t i = 0; i < layerImpl->children().size(); ++i)
+ collectScrollDeltas(scrollInfo, layerImpl->children()[i].get());
+}
+
+PassOwnPtr<CCScrollAndScaleSet> CCLayerTreeHostImpl::processScrollDeltas()
+{
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = adoptPtr(new CCScrollAndScaleSet());
+
+ if (m_pinchGestureActive || m_pageScaleAnimation) {
+ m_sentPageScaleDelta = scrollInfo->pageScaleDelta = 1;
+ if (m_pinchGestureActive)
+ computePinchZoomDeltas(scrollInfo.get());
+ else if (m_pageScaleAnimation.get())
+ computeDoubleTapZoomDeltas(scrollInfo.get());
+ return scrollInfo.release();
+ }
+
+ collectScrollDeltas(scrollInfo.get(), m_rootLayerImpl.get());
+ m_sentPageScaleDelta = scrollInfo->pageScaleDelta = m_pageScaleDelta;
+
+ return scrollInfo.release();
+}
+
+void CCLayerTreeHostImpl::setFullRootLayerDamage()
+{
+ if (m_rootLayerImpl) {
+ CCRenderSurface* renderSurface = m_rootLayerImpl->renderSurface();
+ if (renderSurface)
+ renderSurface->damageTracker()->forceFullDamageNextUpdate();
+ }
+}
+
+void CCLayerTreeHostImpl::animatePageScale(double monotonicTime)
+{
+ if (!m_pageScaleAnimation || !m_rootScrollLayerImpl)
+ return;
+
+ IntSize scrollTotal = flooredIntSize(m_rootScrollLayerImpl->scrollPosition() + m_rootScrollLayerImpl->scrollDelta());
+
+ setPageScaleDelta(m_pageScaleAnimation->pageScaleAtTime(monotonicTime) / m_pageScale);
+ IntSize nextScroll = m_pageScaleAnimation->scrollOffsetAtTime(monotonicTime);
+ nextScroll.scale(1 / m_pageScaleDelta);
+ m_rootScrollLayerImpl->scrollBy(nextScroll - scrollTotal);
+ m_client->setNeedsRedrawOnImplThread();
+
+ if (m_pageScaleAnimation->isAnimationCompleteAtTime(monotonicTime)) {
+ m_pageScaleAnimation.clear();
+ m_client->setNeedsCommitOnImplThread();
+ }
+}
+
+void CCLayerTreeHostImpl::animateLayers(double monotonicTime, double wallClockTime)
+{
+ if (!CCSettings::acceleratedAnimationEnabled() || !m_needsAnimateLayers || !m_rootLayerImpl)
+ return;
+
+ TRACE_EVENT0("cc", "CCLayerTreeHostImpl::animateLayers");
+
+ OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector));
+
+ bool didAnimate = false;
+ animateLayersRecursive(m_rootLayerImpl.get(), monotonicTime, wallClockTime, events.get(), didAnimate, m_needsAnimateLayers);
+
+ if (!events->isEmpty())
+ m_client->postAnimationEventsToMainThreadOnImplThread(events.release(), wallClockTime);
+
+ if (didAnimate)
+ m_client->setNeedsRedrawOnImplThread();
+
+ setBackgroundTickingEnabled(!m_visible && m_needsAnimateLayers);
+}
+
+double CCLayerTreeHostImpl::lowFrequencyAnimationInterval() const
+{
+ return 1;
+}
+
+void CCLayerTreeHostImpl::sendDidLoseContextRecursive(CCLayerImpl* current)
+{
+ ASSERT(current);
+ current->didLoseContext();
+ if (current->maskLayer())
+ sendDidLoseContextRecursive(current->maskLayer());
+ if (current->replicaLayer())
+ sendDidLoseContextRecursive(current->replicaLayer());
+ for (size_t i = 0; i < current->children().size(); ++i)
+ sendDidLoseContextRecursive(current->children()[i].get());
+}
+
+static void clearRenderSurfacesOnCCLayerImplRecursive(CCLayerImpl* current)
+{
+ ASSERT(current);
+ for (size_t i = 0; i < current->children().size(); ++i)
+ clearRenderSurfacesOnCCLayerImplRecursive(current->children()[i].get());
+ current->clearRenderSurface();
+}
+
+void CCLayerTreeHostImpl::clearRenderSurfaces()
+{
+ clearRenderSurfacesOnCCLayerImplRecursive(m_rootLayerImpl.get());
+ m_renderSurfaceLayerList.clear();
+}
+
+String CCLayerTreeHostImpl::layerTreeAsText() const
+{
+ TextStream ts;
+ if (m_rootLayerImpl) {
+ ts << m_rootLayerImpl->layerTreeAsText();
+ ts << "RenderSurfaces:\n";
+ dumpRenderSurfaces(ts, 1, m_rootLayerImpl.get());
+ }
+ return ts.release();
+}
+
+void CCLayerTreeHostImpl::dumpRenderSurfaces(TextStream& ts, int indent, const CCLayerImpl* layer) const
+{
+ if (layer->renderSurface())
+ layer->renderSurface()->dumpSurface(ts, indent);
+
+ for (size_t i = 0; i < layer->children().size(); ++i)
+ dumpRenderSurfaces(ts, indent, layer->children()[i].get());
+}
+
+
+void CCLayerTreeHostImpl::animateGestures(double monotonicTime)
+{
+ if (!m_activeGestureAnimation)
+ return;
+
+ bool isContinuing = m_activeGestureAnimation->animate(monotonicTime);
+ if (isContinuing)
+ m_client->setNeedsRedrawOnImplThread();
+ else
+ m_activeGestureAnimation.clear();
+}
+
+int CCLayerTreeHostImpl::sourceAnimationFrameNumber() const
+{
+ return fpsCounter()->currentFrameNumber();
+}
+
+void CCLayerTreeHostImpl::renderingStats(CCRenderingStats& stats) const
+{
+ stats.numFramesSentToScreen = fpsCounter()->currentFrameNumber();
+ stats.droppedFrameCount = fpsCounter()->droppedFrameCount();
+}
+
+void CCLayerTreeHostImpl::animateScrollbars(double monotonicTime)
+{
+ animateScrollbarsRecursive(m_rootLayerImpl.get(), monotonicTime);
+}
+
+void CCLayerTreeHostImpl::animateScrollbarsRecursive(CCLayerImpl* layer, double monotonicTime)
+{
+ if (!layer)
+ return;
+
+ CCScrollbarAnimationController* scrollbarController = layer->scrollbarAnimationController();
+ if (scrollbarController && scrollbarController->animate(monotonicTime))
+ m_client->setNeedsRedrawOnImplThread();
+
+ for (size_t i = 0; i < layer->children().size(); ++i)
+ animateScrollbarsRecursive(layer->children()[i].get(), monotonicTime);
+}
+
+} // namespace WebCore
diff --git a/cc/CCLayerTreeHostImpl.h b/cc/CCLayerTreeHostImpl.h
new file mode 100644
index 0000000..f3e93dd
--- /dev/null
+++ b/cc/CCLayerTreeHostImpl.h
@@ -0,0 +1,295 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerTreeHostImpl_h
+#define CCLayerTreeHostImpl_h
+
+#include "CCAnimationEvents.h"
+#include "CCInputHandler.h"
+#include "CCLayerSorter.h"
+#include "CCRenderPass.h"
+#include "CCRenderer.h"
+#include "SkColor.h"
+#include <public/WebCompositorOutputSurfaceClient.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class CCActiveGestureAnimation;
+class CCCompletionEvent;
+class CCDebugRectHistory;
+class CCFrameRateCounter;
+class CCHeadsUpDisplayLayerImpl;
+class CCLayerImpl;
+class CCLayerTreeHostImplTimeSourceAdapter;
+class CCPageScaleAnimation;
+class CCRenderPassDrawQuad;
+class CCResourceProvider;
+struct RendererCapabilities;
+struct CCRenderingStats;
+
+// CCLayerTreeHost->CCProxy callback interface.
+class CCLayerTreeHostImplClient {
+public:
+ virtual void didLoseContextOnImplThread() = 0;
+ virtual void onSwapBuffersCompleteOnImplThread() = 0;
+ virtual void onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds) = 0;
+ virtual void setNeedsRedrawOnImplThread() = 0;
+ virtual void setNeedsCommitOnImplThread() = 0;
+ virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) = 0;
+};
+
+// CCLayerTreeHostImpl owns the CCLayerImpl tree as well as associated rendering state
+class CCLayerTreeHostImpl : public CCInputHandlerClient,
+ public CCRendererClient,
+ public WebKit::WebCompositorOutputSurfaceClient {
+ WTF_MAKE_NONCOPYABLE(CCLayerTreeHostImpl);
+ typedef Vector<CCLayerImpl*> CCLayerList;
+
+public:
+ static PassOwnPtr<CCLayerTreeHostImpl> create(const CCLayerTreeSettings&, CCLayerTreeHostImplClient*);
+ virtual ~CCLayerTreeHostImpl();
+
+ // CCInputHandlerClient implementation
+ virtual CCInputHandlerClient::ScrollStatus scrollBegin(const IntPoint&, CCInputHandlerClient::ScrollInputType) OVERRIDE;
+ virtual void scrollBy(const IntPoint&, const IntSize&) OVERRIDE;
+ virtual void scrollEnd() OVERRIDE;
+ virtual void pinchGestureBegin() OVERRIDE;
+ virtual void pinchGestureUpdate(float, const IntPoint&) OVERRIDE;
+ virtual void pinchGestureEnd() OVERRIDE;
+ virtual void startPageScaleAnimation(const IntSize& targetPosition, bool anchorPoint, float pageScale, double startTime, double duration) OVERRIDE;
+ virtual CCActiveGestureAnimation* activeGestureAnimation() OVERRIDE { return m_activeGestureAnimation.get(); }
+ // To clear an active animation, pass nullptr.
+ virtual void setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAnimation>) OVERRIDE;
+ virtual void scheduleAnimation() OVERRIDE;
+
+ struct FrameData {
+ Vector<IntRect> occludingScreenSpaceRects;
+ CCRenderPassList renderPasses;
+ CCRenderPassIdHashMap renderPassesById;
+ CCLayerList* renderSurfaceLayerList;
+ CCLayerList willDrawLayers;
+ };
+
+ // Virtual for testing.
+ virtual void beginCommit();
+ virtual void commitComplete();
+ virtual void animate(double monotonicTime, double wallClockTime);
+
+ // Returns false if problems occured preparing the frame, and we should try
+ // to avoid displaying the frame. If prepareToDraw is called,
+ // didDrawAllLayers must also be called, regardless of whether drawLayers is
+ // called between the two.
+ virtual bool prepareToDraw(FrameData&);
+ virtual void drawLayers(const FrameData&);
+ // Must be called if and only if prepareToDraw was called.
+ void didDrawAllLayers(const FrameData&);
+
+ // CCRendererClient implementation
+ virtual const IntSize& deviceViewportSize() const OVERRIDE { return m_deviceViewportSize; }
+ virtual const CCLayerTreeSettings& settings() const OVERRIDE { return m_settings; }
+ virtual void didLoseContext() OVERRIDE;
+ virtual void onSwapBuffersComplete() OVERRIDE;
+ virtual void setFullRootLayerDamage() OVERRIDE;
+ virtual void releaseContentsTextures() OVERRIDE;
+ virtual void setMemoryAllocationLimitBytes(size_t) OVERRIDE;
+
+ // WebCompositorOutputSurfaceClient implementation.
+ virtual void onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds) OVERRIDE;
+
+ // Implementation
+ bool canDraw();
+ CCGraphicsContext* context() const;
+
+ String layerTreeAsText() const;
+
+ void finishAllRendering();
+ int sourceAnimationFrameNumber() const;
+
+ bool initializeRenderer(PassOwnPtr<CCGraphicsContext>, TextureUploaderOption);
+ bool isContextLost();
+ CCRenderer* renderer() { return m_renderer.get(); }
+ const RendererCapabilities& rendererCapabilities() const;
+
+ bool swapBuffers();
+
+ void readback(void* pixels, const IntRect&);
+
+ void setRootLayer(PassOwnPtr<CCLayerImpl>);
+ CCLayerImpl* rootLayer() { return m_rootLayerImpl.get(); }
+
+ void setHudLayer(CCHeadsUpDisplayLayerImpl* layerImpl) { m_hudLayerImpl = layerImpl; }
+ CCHeadsUpDisplayLayerImpl* hudLayer() { return m_hudLayerImpl; }
+
+ // Release ownership of the current layer tree and replace it with an empty
+ // tree. Returns the root layer of the detached tree.
+ PassOwnPtr<CCLayerImpl> detachLayerTree();
+
+ CCLayerImpl* rootScrollLayer() const { return m_rootScrollLayerImpl; }
+
+ bool visible() const { return m_visible; }
+ void setVisible(bool);
+
+ int sourceFrameNumber() const { return m_sourceFrameNumber; }
+ void setSourceFrameNumber(int frameNumber) { m_sourceFrameNumber = frameNumber; }
+
+ bool contentsTexturesPurged() const { return m_contentsTexturesPurged; }
+ void resetContentsTexturesPurged() { m_contentsTexturesPurged = false; }
+ size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBytes; }
+
+ void setViewportSize(const IntSize& layoutViewportSize, const IntSize& deviceViewportSize);
+ const IntSize& layoutViewportSize() const { return m_layoutViewportSize; }
+
+ float deviceScaleFactor() const { return m_deviceScaleFactor; }
+ void setDeviceScaleFactor(float);
+
+ float pageScale() const { return m_pageScale; }
+ void setPageScaleFactorAndLimits(float pageScale, float minPageScale, float maxPageScale);
+
+ PassOwnPtr<CCScrollAndScaleSet> processScrollDeltas();
+
+ void startPageScaleAnimation(const IntSize& tragetPosition, bool useAnchor, float scale, double durationSec);
+
+ SkColor backgroundColor() const { return m_backgroundColor; }
+ void setBackgroundColor(SkColor color) { m_backgroundColor = color; }
+
+ bool hasTransparentBackground() const { return m_hasTransparentBackground; }
+ void setHasTransparentBackground(bool transparent) { m_hasTransparentBackground = transparent; }
+
+ bool needsAnimateLayers() const { return m_needsAnimateLayers; }
+ void setNeedsAnimateLayers() { m_needsAnimateLayers = true; }
+
+ void setNeedsRedraw();
+
+ void renderingStats(CCRenderingStats&) const;
+
+ CCFrameRateCounter* fpsCounter() const { return m_fpsCounter.get(); }
+ CCDebugRectHistory* debugRectHistory() const { return m_debugRectHistory.get(); }
+ CCResourceProvider* resourceProvider() const { return m_resourceProvider.get(); }
+
+ class CullRenderPassesWithCachedTextures {
+ public:
+ bool shouldRemoveRenderPass(const CCRenderPassDrawQuad&, const FrameData&) const;
+
+ // Iterates from the root first, in order to remove the surfaces closest
+ // to the root with cached textures, and all surfaces that draw into
+ // them.
+ size_t renderPassListBegin(const CCRenderPassList& list) const { return list.size() - 1; }
+ size_t renderPassListEnd(const CCRenderPassList&) const { return 0 - 1; }
+ size_t renderPassListNext(size_t it) const { return it - 1; }
+
+ CullRenderPassesWithCachedTextures(CCRenderer& renderer) : m_renderer(renderer) { }
+ private:
+ CCRenderer& m_renderer;
+ };
+
+ class CullRenderPassesWithNoQuads {
+ public:
+ bool shouldRemoveRenderPass(const CCRenderPassDrawQuad&, const FrameData&) const;
+
+ // Iterates in draw order, so that when a surface is removed, and its
+ // target becomes empty, then its target can be removed also.
+ size_t renderPassListBegin(const CCRenderPassList&) const { return 0; }
+ size_t renderPassListEnd(const CCRenderPassList& list) const { return list.size(); }
+ size_t renderPassListNext(size_t it) const { return it + 1; }
+ };
+
+ template<typename RenderPassCuller>
+ static void removeRenderPasses(RenderPassCuller, FrameData&);
+
+protected:
+ CCLayerTreeHostImpl(const CCLayerTreeSettings&, CCLayerTreeHostImplClient*);
+
+ void animatePageScale(double monotonicTime);
+ void animateGestures(double monotonicTime);
+ void animateScrollbars(double monotonicTime);
+
+ // Exposed for testing.
+ void calculateRenderSurfaceLayerList(CCLayerList&);
+
+ // Virtual for testing.
+ virtual void animateLayers(double monotonicTime, double wallClockTime);
+
+ // Virtual for testing. Measured in seconds.
+ virtual double lowFrequencyAnimationInterval() const;
+
+ CCLayerTreeHostImplClient* m_client;
+ int m_sourceFrameNumber;
+
+private:
+ void computeDoubleTapZoomDeltas(CCScrollAndScaleSet* scrollInfo);
+ void computePinchZoomDeltas(CCScrollAndScaleSet* scrollInfo);
+ void makeScrollAndScaleSet(CCScrollAndScaleSet* scrollInfo, const IntSize& scrollOffset, float pageScale);
+
+ void setPageScaleDelta(float);
+ void updateMaxScrollPosition();
+ void trackDamageForAllSurfaces(CCLayerImpl* rootDrawLayer, const CCLayerList& renderSurfaceLayerList);
+
+ // Returns false if the frame should not be displayed. This function should
+ // only be called from prepareToDraw, as didDrawAllLayers must be called
+ // if this helper function is called.
+ bool calculateRenderPasses(FrameData&);
+ void animateLayersRecursive(CCLayerImpl*, double monotonicTime, double wallClockTime, CCAnimationEventsVector*, bool& didAnimate, bool& needsAnimateLayers);
+ void setBackgroundTickingEnabled(bool);
+ IntSize contentSize() const;
+
+ void sendDidLoseContextRecursive(CCLayerImpl*);
+ void clearRenderSurfaces();
+ bool ensureRenderSurfaceLayerList();
+ void clearCurrentlyScrollingLayer();
+
+ void animateScrollbarsRecursive(CCLayerImpl*, double monotonicTime);
+
+ void dumpRenderSurfaces(TextStream&, int indent, const CCLayerImpl*) const;
+
+ OwnPtr<CCGraphicsContext> m_context;
+ OwnPtr<CCResourceProvider> m_resourceProvider;
+ OwnPtr<CCRenderer> m_renderer;
+ OwnPtr<CCLayerImpl> m_rootLayerImpl;
+ CCLayerImpl* m_rootScrollLayerImpl;
+ CCLayerImpl* m_currentlyScrollingLayerImpl;
+ CCHeadsUpDisplayLayerImpl* m_hudLayerImpl;
+ int m_scrollingLayerIdFromPreviousTree;
+ bool m_scrollDeltaIsInScreenSpace;
+ CCLayerTreeSettings m_settings;
+ IntSize m_layoutViewportSize;
+ IntSize m_deviceViewportSize;
+ float m_deviceScaleFactor;
+ bool m_visible;
+ bool m_contentsTexturesPurged;
+ size_t m_memoryAllocationLimitBytes;
+
+ float m_pageScale;
+ float m_pageScaleDelta;
+ float m_sentPageScaleDelta;
+ float m_minPageScale, m_maxPageScale;
+
+ SkColor m_backgroundColor;
+ bool m_hasTransparentBackground;
+
+ // If this is true, it is necessary to traverse the layer tree ticking the animators.
+ bool m_needsAnimateLayers;
+ bool m_pinchGestureActive;
+ IntPoint m_previousPinchAnchor;
+
+ OwnPtr<CCPageScaleAnimation> m_pageScaleAnimation;
+ OwnPtr<CCActiveGestureAnimation> m_activeGestureAnimation;
+
+ // This is used for ticking animations slowly when hidden.
+ OwnPtr<CCLayerTreeHostImplTimeSourceAdapter> m_timeSourceClientAdapter;
+
+ CCLayerSorter m_layerSorter;
+
+ // List of visible layers for the most recently prepared frame. Used for
+ // rendering and input event hit testing.
+ CCLayerList m_renderSurfaceLayerList;
+
+ OwnPtr<CCFrameRateCounter> m_fpsCounter;
+ OwnPtr<CCDebugRectHistory> m_debugRectHistory;
+};
+
+};
+
+#endif
diff --git a/cc/CCLayerTreeHostImplTest.cpp b/cc/CCLayerTreeHostImplTest.cpp
new file mode 100644
index 0000000..ebbcf8f
--- /dev/null
+++ b/cc/CCLayerTreeHostImplTest.cpp
@@ -0,0 +1,4117 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTreeHostImpl.h"
+
+#include "CCAnimationTestCommon.h"
+#include "CCHeadsUpDisplayLayerImpl.h"
+#include "CCIOSurfaceLayerImpl.h"
+#include "CCLayerImpl.h"
+#include "CCLayerTestCommon.h"
+#include "CCLayerTilingData.h"
+#include "CCLayerTreeTestCommon.h"
+#include "CCQuadCuller.h"
+#include "CCRenderPassDrawQuad.h"
+#include "CCRendererGL.h"
+#include "CCScrollbarLayerImpl.h"
+#include "CCSettings.h"
+#include "CCSingleThreadProxy.h"
+#include "CCSolidColorDrawQuad.h"
+#include "CCTestCommon.h"
+#include "CCTextureLayerImpl.h"
+#include "CCTileDrawQuad.h"
+#include "CCTiledLayerImpl.h"
+#include "CCVideoLayerImpl.h"
+#include "FakeWebCompositorOutputSurface.h"
+#include "FakeWebGraphicsContext3D.h"
+#include "FakeWebScrollbarThemeGeometry.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebVideoFrame.h>
+#include <public/WebVideoFrameProvider.h>
+
+using namespace CCLayerTestCommon;
+using namespace WebCore;
+using namespace WebKit;
+using namespace WebKitTests;
+
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::_;
+
+namespace {
+
+class CCLayerTreeHostImplTest : public testing::Test, public CCLayerTreeHostImplClient {
+public:
+ CCLayerTreeHostImplTest()
+ : m_didRequestCommit(false)
+ , m_didRequestRedraw(false)
+ {
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+
+ m_hostImpl = CCLayerTreeHostImpl::create(settings, this);
+ m_hostImpl->initializeRenderer(createContext(), UnthrottledUploader);
+ m_hostImpl->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+ }
+
+ virtual void didLoseContextOnImplThread() OVERRIDE { }
+ virtual void onSwapBuffersCompleteOnImplThread() OVERRIDE { }
+ virtual void onVSyncParametersChanged(double, double) OVERRIDE { }
+ virtual void setNeedsRedrawOnImplThread() OVERRIDE { m_didRequestRedraw = true; }
+ virtual void setNeedsCommitOnImplThread() OVERRIDE { m_didRequestCommit = true; }
+ virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) OVERRIDE { }
+
+ PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHost(bool partialSwap, PassOwnPtr<CCGraphicsContext> graphicsContext, PassOwnPtr<CCLayerImpl> rootPtr)
+ {
+ CCSettings::setPartialSwapEnabled(partialSwap);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ myHostImpl->initializeRenderer(graphicsContext, UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+
+ OwnPtr<CCLayerImpl> root = rootPtr;
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(IntSize(10, 10));
+ root->setContentBounds(IntSize(10, 10));
+ root->setVisibleContentRect(IntRect(0, 0, 10, 10));
+ root->setDrawsContent(true);
+ myHostImpl->setRootLayer(root.release());
+ return myHostImpl.release();
+ }
+
+ static void expectClearedScrollDeltasRecursive(CCLayerImpl* layer)
+ {
+ ASSERT_EQ(layer->scrollDelta(), IntSize());
+ for (size_t i = 0; i < layer->children().size(); ++i)
+ expectClearedScrollDeltasRecursive(layer->children()[i].get());
+ }
+
+ static void expectContains(const CCScrollAndScaleSet& scrollInfo, int id, const IntSize& scrollDelta)
+ {
+ int timesEncountered = 0;
+
+ for (size_t i = 0; i < scrollInfo.scrolls.size(); ++i) {
+ if (scrollInfo.scrolls[i].layerId != id)
+ continue;
+ EXPECT_EQ(scrollDelta.width(), scrollInfo.scrolls[i].scrollDelta.width());
+ EXPECT_EQ(scrollDelta.height(), scrollInfo.scrolls[i].scrollDelta.height());
+ timesEncountered++;
+ }
+
+ ASSERT_EQ(timesEncountered, 1);
+ }
+
+ void setupScrollAndContentsLayers(const IntSize& contentSize)
+ {
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setScrollable(true);
+ root->setScrollPosition(IntPoint(0, 0));
+ root->setMaxScrollPosition(contentSize);
+ root->setBounds(contentSize);
+ root->setContentBounds(contentSize);
+ root->setPosition(FloatPoint(0, 0));
+ root->setAnchorPoint(FloatPoint(0, 0));
+
+ OwnPtr<CCLayerImpl> contents = CCLayerImpl::create(2);
+ contents->setDrawsContent(true);
+ contents->setBounds(contentSize);
+ contents->setContentBounds(contentSize);
+ contents->setPosition(FloatPoint(0, 0));
+ contents->setAnchorPoint(FloatPoint(0, 0));
+ root->addChild(contents.release());
+ m_hostImpl->setRootLayer(root.release());
+ }
+
+ static PassOwnPtr<CCLayerImpl> createScrollableLayer(int id, const IntSize& size)
+ {
+ OwnPtr<CCLayerImpl> layer = CCLayerImpl::create(id);
+ layer->setScrollable(true);
+ layer->setDrawsContent(true);
+ layer->setBounds(size);
+ layer->setContentBounds(size);
+ layer->setMaxScrollPosition(IntSize(size.width() * 2, size.height() * 2));
+ return layer.release();
+ }
+
+ void initializeRendererAndDrawFrame()
+ {
+ m_hostImpl->initializeRenderer(createContext(), UnthrottledUploader);
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ }
+
+protected:
+ PassOwnPtr<CCGraphicsContext> createContext()
+ {
+ return FakeWebCompositorOutputSurface::create(adoptPtr(new FakeWebGraphicsContext3D));
+ }
+
+ DebugScopedSetImplThread m_alwaysImplThread;
+ DebugScopedSetMainThreadBlocked m_alwaysMainThreadBlocked;
+
+ OwnPtr<CCLayerTreeHostImpl> m_hostImpl;
+ bool m_didRequestCommit;
+ bool m_didRequestRedraw;
+ CCScopedSettings m_scopedSettings;
+};
+
+class FakeWebGraphicsContext3DMakeCurrentFails : public FakeWebGraphicsContext3D {
+public:
+ virtual bool makeContextCurrent() { return false; }
+};
+
+TEST_F(CCLayerTreeHostImplTest, scrollDeltaNoLayers)
+{
+ ASSERT_FALSE(m_hostImpl->rootLayer());
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ ASSERT_EQ(scrollInfo->scrolls.size(), 0u);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollDeltaTreeButNoChanges)
+{
+ {
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->addChild(CCLayerImpl::create(2));
+ root->addChild(CCLayerImpl::create(3));
+ root->children()[1]->addChild(CCLayerImpl::create(4));
+ root->children()[1]->addChild(CCLayerImpl::create(5));
+ root->children()[1]->children()[0]->addChild(CCLayerImpl::create(6));
+ m_hostImpl->setRootLayer(root.release());
+ }
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ expectClearedScrollDeltasRecursive(root);
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo;
+
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ ASSERT_EQ(scrollInfo->scrolls.size(), 0u);
+ expectClearedScrollDeltasRecursive(root);
+
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ ASSERT_EQ(scrollInfo->scrolls.size(), 0u);
+ expectClearedScrollDeltasRecursive(root);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollDeltaRepeatedScrolls)
+{
+ IntPoint scrollPosition(20, 30);
+ IntSize scrollDelta(11, -15);
+ {
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setScrollPosition(scrollPosition);
+ root->setScrollable(true);
+ root->setMaxScrollPosition(IntSize(100, 100));
+ root->scrollBy(scrollDelta);
+ m_hostImpl->setRootLayer(root.release());
+ }
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo;
+
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ ASSERT_EQ(scrollInfo->scrolls.size(), 1u);
+ EXPECT_EQ(root->sentScrollDelta(), scrollDelta);
+ expectContains(*scrollInfo, root->id(), scrollDelta);
+
+ IntSize scrollDelta2(-5, 27);
+ root->scrollBy(scrollDelta2);
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ ASSERT_EQ(scrollInfo->scrolls.size(), 1u);
+ EXPECT_EQ(root->sentScrollDelta(), scrollDelta + scrollDelta2);
+ expectContains(*scrollInfo, root->id(), scrollDelta + scrollDelta2);
+
+ root->scrollBy(IntSize());
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(root->sentScrollDelta(), scrollDelta + scrollDelta2);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollRootCallsCommitAndRedraw)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 10));
+ m_hostImpl->scrollEnd();
+ EXPECT_TRUE(m_didRequestRedraw);
+ EXPECT_TRUE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollWithoutRootLayer)
+{
+ // We should not crash when trying to scroll an empty layer tree.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollWithoutRenderer)
+{
+ CCLayerTreeSettings settings;
+ m_hostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Initialization will fail here.
+ m_hostImpl->initializeRenderer(FakeWebCompositorOutputSurface::create(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails)), UnthrottledUploader);
+ m_hostImpl->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+
+ setupScrollAndContentsLayers(IntSize(100, 100));
+
+ // We should not crash when trying to scroll after the renderer initialization fails.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
+}
+
+TEST_F(CCLayerTreeHostImplTest, replaceTreeWhileScrolling)
+{
+ const int scrollLayerId = 1;
+
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ // We should not crash if the tree is replaced while we are scrolling.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->detachLayerTree();
+
+ setupScrollAndContentsLayers(IntSize(100, 100));
+
+ // We should still be scrolling, because the scrolled layer also exists in the new tree.
+ IntSize scrollDelta(0, 10);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo, scrollLayerId, scrollDelta);
+}
+
+TEST_F(CCLayerTreeHostImplTest, clearRootRenderSurfaceAndScroll)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ // We should be able to scroll even if the root layer loses its render surface after the most
+ // recent render.
+ m_hostImpl->rootLayer()->clearRenderSurface();
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+}
+
+TEST_F(CCLayerTreeHostImplTest, wheelEventHandlers)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ root->setHaveWheelEventHandlers(true);
+
+ // With registered event handlers, wheel scrolls have to go to the main thread.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
+
+ // But gesture scrolls can still be handled.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+}
+
+TEST_F(CCLayerTreeHostImplTest, shouldScrollOnMainThread)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ root->setShouldScrollOnMainThread(true);
+
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread);
+}
+
+TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionBasic)
+{
+ setupScrollAndContentsLayers(IntSize(200, 200));
+ m_hostImpl->setViewportSize(IntSize(100, 100), IntSize(100, 100));
+ initializeRendererAndDrawFrame();
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50));
+
+ // All scroll types inside the non-fast scrollable region should fail.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread);
+
+ // All scroll types outside this region should succeed.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(75, 75), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 10));
+ m_hostImpl->scrollEnd();
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(75, 75), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 10));
+ m_hostImpl->scrollEnd();
+}
+
+TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionWithOffset)
+{
+ setupScrollAndContentsLayers(IntSize(200, 200));
+ m_hostImpl->setViewportSize(IntSize(100, 100), IntSize(100, 100));
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50));
+ root->setPosition(FloatPoint(-25, 0));
+ initializeRendererAndDrawFrame();
+
+ // This point would fall into the non-fast scrollable region except that we've moved the layer down by 25 pixels.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(40, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 1));
+ m_hostImpl->scrollEnd();
+
+ // This point is still inside the non-fast region.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(10, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
+}
+
+TEST_F(CCLayerTreeHostImplTest, pinchGesture)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
+ ASSERT(scrollLayer);
+
+ const float minPageScale = 0.5, maxPageScale = 4;
+
+ // Basic pinch zoom in gesture
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollDelta(IntSize());
+
+ float pageScaleDelta = 2;
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(50, 50));
+ m_hostImpl->pinchGestureEnd();
+ EXPECT_TRUE(m_didRequestRedraw);
+ EXPECT_TRUE(m_didRequestCommit);
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
+ }
+
+ // Zoom-in clamping
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollDelta(IntSize());
+ float pageScaleDelta = 10;
+
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(50, 50));
+ m_hostImpl->pinchGestureEnd();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, maxPageScale);
+ }
+
+ // Zoom-out clamping
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollDelta(IntSize());
+ scrollLayer->setScrollPosition(IntPoint(50, 50));
+
+ float pageScaleDelta = 0.1f;
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(0, 0));
+ m_hostImpl->pinchGestureEnd();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, minPageScale);
+
+ // Pushed to (0,0) via clamping against contents layer size.
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(-50, -50));
+ }
+
+ // Two-finger panning
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollDelta(IntSize());
+ scrollLayer->setScrollPosition(IntPoint(20, 20));
+
+ float pageScaleDelta = 1;
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(10, 10));
+ m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(20, 20));
+ m_hostImpl->pinchGestureEnd();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(-10, -10));
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, pageScaleAnimation)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
+ ASSERT(scrollLayer);
+
+ const float minPageScale = 0.5, maxPageScale = 4;
+ const double startTime = 1;
+ const double duration = 0.1;
+ const double halfwayThroughAnimation = startTime + duration / 2;
+ const double endTime = startTime + duration;
+
+ // Non-anchor zoom-in
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollPosition(IntPoint(50, 50));
+
+ m_hostImpl->startPageScaleAnimation(IntSize(0, 0), false, 2, startTime, duration);
+ m_hostImpl->animate(halfwayThroughAnimation, halfwayThroughAnimation);
+ EXPECT_TRUE(m_didRequestRedraw);
+ m_hostImpl->animate(endTime, endTime);
+ EXPECT_TRUE(m_didRequestCommit);
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, 2);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(-50, -50));
+ }
+
+ // Anchor zoom-out
+ {
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ scrollLayer->setPageScaleDelta(1);
+ scrollLayer->setScrollPosition(IntPoint(50, 50));
+
+ m_hostImpl->startPageScaleAnimation(IntSize(25, 25), true, minPageScale, startTime, duration);
+ m_hostImpl->animate(endTime, endTime);
+ EXPECT_TRUE(m_didRequestRedraw);
+ EXPECT_TRUE(m_didRequestCommit);
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, minPageScale);
+ // Pushed to (0,0) via clamping against contents layer size.
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(-50, -50));
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhilePinchZooming)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
+ ASSERT(scrollLayer);
+
+ const float minPageScale = 0.5, maxPageScale = 4;
+
+ // Pinch zoom in.
+ {
+ // Start a pinch in gesture at the bottom right corner of the viewport.
+ const float zoomInDelta = 2;
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(zoomInDelta, IntPoint(50, 50));
+
+ // Because we are pinch zooming in, we shouldn't get any scroll or page
+ // scale deltas.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, 1);
+ EXPECT_EQ(scrollInfo->scrolls.size(), 0u);
+
+ // Once the gesture ends, we get the final scroll and page scale values.
+ m_hostImpl->pinchGestureEnd();
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, zoomInDelta);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
+ }
+
+ // Pinch zoom out.
+ {
+ // Start a pinch out gesture at the bottom right corner of the viewport.
+ const float zoomOutDelta = 0.75;
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(zoomOutDelta, IntPoint(50, 50));
+
+ // Since we are pinch zooming out, we should get an update to zoom all
+ // the way out to the minimum page scale.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, minPageScale);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(0, 0));
+
+ // Once the gesture ends, we get the final scroll and page scale values.
+ m_hostImpl->pinchGestureEnd();
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, zoomOutDelta);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(8, 8));
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhileAnimatingPageScale)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ m_hostImpl->setViewportSize(IntSize(50, 50), IntSize(50, 50));
+ initializeRendererAndDrawFrame();
+
+ CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer();
+ ASSERT(scrollLayer);
+
+ const float minPageScale = 0.5, maxPageScale = 4;
+ const double startTime = 1;
+ const double duration = 0.1;
+ const double halfwayThroughAnimation = startTime + duration / 2;
+ const double endTime = startTime + duration;
+
+ // Start a page scale animation.
+ const float pageScaleDelta = 2;
+ m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale);
+ m_hostImpl->startPageScaleAnimation(IntSize(50, 50), false, pageScaleDelta, startTime, duration);
+
+ // We should immediately get the final zoom and scroll values for the
+ // animation.
+ m_hostImpl->animate(halfwayThroughAnimation, halfwayThroughAnimation);
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
+
+ // Scrolling during the animation is ignored.
+ const IntSize scrollDelta(0, 10);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The final page scale and scroll deltas should match what we got
+ // earlier.
+ m_hostImpl->animate(endTime, endTime);
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta);
+ expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25));
+}
+
+class DidDrawCheckLayer : public CCTiledLayerImpl {
+public:
+ static PassOwnPtr<DidDrawCheckLayer> create(int id) { return adoptPtr(new DidDrawCheckLayer(id)); }
+
+ virtual void didDraw(CCResourceProvider*) OVERRIDE
+ {
+ m_didDrawCalled = true;
+ }
+
+ virtual void willDraw(CCResourceProvider*) OVERRIDE
+ {
+ m_willDrawCalled = true;
+ }
+
+ bool didDrawCalled() const { return m_didDrawCalled; }
+ bool willDrawCalled() const { return m_willDrawCalled; }
+
+ void clearDidDrawCheck()
+ {
+ m_didDrawCalled = false;
+ m_willDrawCalled = false;
+ }
+
+protected:
+ explicit DidDrawCheckLayer(int id)
+ : CCTiledLayerImpl(id)
+ , m_didDrawCalled(false)
+ , m_willDrawCalled(false)
+ {
+ setAnchorPoint(FloatPoint(0, 0));
+ setBounds(IntSize(10, 10));
+ setContentBounds(IntSize(10, 10));
+ setDrawsContent(true);
+ setSkipsDraw(false);
+ setVisibleContentRect(IntRect(0, 0, 10, 10));
+
+ OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(IntSize(100, 100), CCLayerTilingData::HasBorderTexels);
+ tiler->setBounds(contentBounds());
+ setTilingData(*tiler.get());
+ }
+
+private:
+ bool m_didDrawCalled;
+ bool m_willDrawCalled;
+};
+
+TEST_F(CCLayerTreeHostImplTest, didDrawNotCalledOnHiddenLayer)
+{
+ // The root layer is always drawn, so run this test on a child layer that
+ // will be masked out by the root layer's bounds.
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+ root->setMasksToBounds(true);
+
+ root->addChild(DidDrawCheckLayer::create(2));
+ DidDrawCheckLayer* layer = static_cast<DidDrawCheckLayer*>(root->children()[0].get());
+ // Ensure visibleContentRect for layer is empty
+ layer->setPosition(FloatPoint(100, 100));
+ layer->setBounds(IntSize(10, 10));
+ layer->setContentBounds(IntSize(10, 10));
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ EXPECT_FALSE(layer->willDrawCalled());
+ EXPECT_FALSE(layer->didDrawCalled());
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ EXPECT_FALSE(layer->willDrawCalled());
+ EXPECT_FALSE(layer->didDrawCalled());
+
+ EXPECT_TRUE(layer->visibleContentRect().isEmpty());
+
+ // Ensure visibleContentRect for layer layer is not empty
+ layer->setPosition(FloatPoint(0, 0));
+
+ EXPECT_FALSE(layer->willDrawCalled());
+ EXPECT_FALSE(layer->didDrawCalled());
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ EXPECT_TRUE(layer->willDrawCalled());
+ EXPECT_TRUE(layer->didDrawCalled());
+
+ EXPECT_FALSE(layer->visibleContentRect().isEmpty());
+}
+
+TEST_F(CCLayerTreeHostImplTest, willDrawNotCalledOnOccludedLayer)
+{
+ IntSize bigSize(1000, 1000);
+ m_hostImpl->setViewportSize(bigSize, bigSize);
+
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+
+ root->addChild(DidDrawCheckLayer::create(2));
+ DidDrawCheckLayer* occludedLayer = static_cast<DidDrawCheckLayer*>(root->children()[0].get());
+
+ root->addChild(DidDrawCheckLayer::create(3));
+ DidDrawCheckLayer* topLayer = static_cast<DidDrawCheckLayer*>(root->children()[1].get());
+ // This layer covers the occludedLayer above. Make this layer large so it can occlude.
+ topLayer->setBounds(bigSize);
+ topLayer->setContentBounds(bigSize);
+ topLayer->setOpaque(true);
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ EXPECT_FALSE(occludedLayer->willDrawCalled());
+ EXPECT_FALSE(occludedLayer->didDrawCalled());
+ EXPECT_FALSE(topLayer->willDrawCalled());
+ EXPECT_FALSE(topLayer->didDrawCalled());
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ EXPECT_FALSE(occludedLayer->willDrawCalled());
+ EXPECT_FALSE(occludedLayer->didDrawCalled());
+ EXPECT_TRUE(topLayer->willDrawCalled());
+ EXPECT_TRUE(topLayer->didDrawCalled());
+}
+
+TEST_F(CCLayerTreeHostImplTest, didDrawCalledOnAllLayers)
+{
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+
+ root->addChild(DidDrawCheckLayer::create(2));
+ DidDrawCheckLayer* layer1 = static_cast<DidDrawCheckLayer*>(root->children()[0].get());
+
+ layer1->addChild(DidDrawCheckLayer::create(3));
+ DidDrawCheckLayer* layer2 = static_cast<DidDrawCheckLayer*>(layer1->children()[0].get());
+
+ layer1->setOpacity(0.3f);
+ layer1->setPreserves3D(false);
+
+ EXPECT_FALSE(root->didDrawCalled());
+ EXPECT_FALSE(layer1->didDrawCalled());
+ EXPECT_FALSE(layer2->didDrawCalled());
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ EXPECT_TRUE(root->didDrawCalled());
+ EXPECT_TRUE(layer1->didDrawCalled());
+ EXPECT_TRUE(layer2->didDrawCalled());
+
+ EXPECT_NE(root->renderSurface(), layer1->renderSurface());
+ EXPECT_TRUE(!!layer1->renderSurface());
+}
+
+class MissingTextureAnimatingLayer : public DidDrawCheckLayer {
+public:
+ static PassOwnPtr<MissingTextureAnimatingLayer> create(int id, bool tileMissing, bool skipsDraw, bool animating, CCResourceProvider* resourceProvider) { return adoptPtr(new MissingTextureAnimatingLayer(id, tileMissing, skipsDraw, animating, resourceProvider)); }
+
+private:
+ explicit MissingTextureAnimatingLayer(int id, bool tileMissing, bool skipsDraw, bool animating, CCResourceProvider* resourceProvider)
+ : DidDrawCheckLayer(id)
+ {
+ OwnPtr<CCLayerTilingData> tilingData = CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels);
+ tilingData->setBounds(bounds());
+ setTilingData(*tilingData.get());
+ setSkipsDraw(skipsDraw);
+ if (!tileMissing) {
+ CCResourceProvider::ResourceId resource = resourceProvider->createResource(CCRenderer::ContentPool, IntSize(), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+ pushTileProperties(0, 0, resource, IntRect());
+ }
+ if (animating)
+ addAnimatedTransformToLayer(*this, 10, 3, 0);
+ }
+};
+
+TEST_F(CCLayerTreeHostImplTest, prepareToDrawFailsWhenAnimationUsesCheckerboard)
+{
+ // When the texture is not missing, we draw as usual.
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+ root->addChild(MissingTextureAnimatingLayer::create(2, false, false, true, m_hostImpl->resourceProvider()));
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // When a texture is missing and we're not animating, we draw as usual with checkerboarding.
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+ root->addChild(MissingTextureAnimatingLayer::create(2, true, false, false, m_hostImpl->resourceProvider()));
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // When a texture is missing and we're animating, we don't want to draw anything.
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+ root->addChild(MissingTextureAnimatingLayer::create(2, true, false, true, m_hostImpl->resourceProvider()));
+
+ EXPECT_FALSE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // When the layer skips draw and we're animating, we still draw the frame.
+ m_hostImpl->setRootLayer(DidDrawCheckLayer::create(1));
+ root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer());
+ root->addChild(MissingTextureAnimatingLayer::create(2, false, true, true, m_hostImpl->resourceProvider()));
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollRootIgnored)
+{
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setScrollable(false);
+ m_hostImpl->setRootLayer(root.release());
+ initializeRendererAndDrawFrame();
+
+ // Scroll event is ignored because layer is not scrollable.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
+ EXPECT_FALSE(m_didRequestRedraw);
+ EXPECT_FALSE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollNonCompositedRoot)
+{
+ // Test the configuration where a non-composited root layer is embedded in a
+ // scrollable outer layer.
+ IntSize surfaceSize(10, 10);
+
+ OwnPtr<CCLayerImpl> contentLayer = CCLayerImpl::create(1);
+ contentLayer->setUseLCDText(true);
+ contentLayer->setDrawsContent(true);
+ contentLayer->setPosition(FloatPoint(0, 0));
+ contentLayer->setAnchorPoint(FloatPoint(0, 0));
+ contentLayer->setBounds(surfaceSize);
+ contentLayer->setContentBounds(IntSize(surfaceSize.width() * 2, surfaceSize.height() * 2));
+
+ OwnPtr<CCLayerImpl> scrollLayer = CCLayerImpl::create(2);
+ scrollLayer->setScrollable(true);
+ scrollLayer->setMaxScrollPosition(surfaceSize);
+ scrollLayer->setBounds(surfaceSize);
+ scrollLayer->setContentBounds(surfaceSize);
+ scrollLayer->setPosition(FloatPoint(0, 0));
+ scrollLayer->setAnchorPoint(FloatPoint(0, 0));
+ scrollLayer->addChild(contentLayer.release());
+
+ m_hostImpl->setRootLayer(scrollLayer.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 10));
+ m_hostImpl->scrollEnd();
+ EXPECT_TRUE(m_didRequestRedraw);
+ EXPECT_TRUE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollChildCallsCommitAndRedraw)
+{
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setBounds(surfaceSize);
+ root->setContentBounds(surfaceSize);
+ root->addChild(createScrollableLayer(2, surfaceSize));
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), IntSize(0, 10));
+ m_hostImpl->scrollEnd();
+ EXPECT_TRUE(m_didRequestRedraw);
+ EXPECT_TRUE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollMissesChild)
+{
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->addChild(createScrollableLayer(2, surfaceSize));
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ // Scroll event is ignored because the input coordinate is outside the layer boundaries.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(15, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
+ EXPECT_FALSE(m_didRequestRedraw);
+ EXPECT_FALSE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollMissesBackfacingChild)
+{
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> child = createScrollableLayer(2, surfaceSize);
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+
+ WebTransformationMatrix matrix;
+ matrix.rotate3d(180, 0, 0);
+ child->setTransform(matrix);
+ child->setDoubleSided(false);
+
+ root->addChild(child.release());
+ m_hostImpl->setRootLayer(root.release());
+ initializeRendererAndDrawFrame();
+
+ // Scroll event is ignored because the scrollable layer is not facing the viewer and there is
+ // nothing scrollable behind it.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored);
+ EXPECT_FALSE(m_didRequestRedraw);
+ EXPECT_FALSE(m_didRequestCommit);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollBlockedByContentLayer)
+{
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> contentLayer = createScrollableLayer(1, surfaceSize);
+ contentLayer->setShouldScrollOnMainThread(true);
+ contentLayer->setScrollable(false);
+
+ OwnPtr<CCLayerImpl> scrollLayer = createScrollableLayer(2, surfaceSize);
+ scrollLayer->addChild(contentLayer.release());
+
+ m_hostImpl->setRootLayer(scrollLayer.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ // Scrolling fails because the content layer is asking to be scrolled on the main thread.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnMainThread)
+{
+ IntSize surfaceSize(10, 10);
+ float pageScale = 2;
+ OwnPtr<CCLayerImpl> root = createScrollableLayer(1, surfaceSize);
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ IntSize scrollDelta(0, 10);
+ IntSize expectedScrollDelta(scrollDelta);
+ IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition());
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // Set new page scale from main thread.
+ m_hostImpl->setPageScaleFactorAndLimits(pageScale, pageScale, pageScale);
+
+ // The scale should apply to the scroll delta.
+ expectedScrollDelta.scale(pageScale);
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), expectedScrollDelta);
+
+ // The scroll range should also have been updated.
+ EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll);
+
+ // The page scale delta remains constant because the impl thread did not scale.
+ EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), 1);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnImplThread)
+{
+ IntSize surfaceSize(10, 10);
+ float pageScale = 2;
+ OwnPtr<CCLayerImpl> root = createScrollableLayer(1, surfaceSize);
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ m_hostImpl->setPageScaleFactorAndLimits(1, 1, pageScale);
+ initializeRendererAndDrawFrame();
+
+ IntSize scrollDelta(0, 10);
+ IntSize expectedScrollDelta(scrollDelta);
+ IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition());
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // Set new page scale on impl thread by pinching.
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(pageScale, IntPoint());
+ m_hostImpl->pinchGestureEnd();
+
+ // The scroll delta is not scaled because the main thread did not scale.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), expectedScrollDelta);
+
+ // The scroll range should also have been updated.
+ EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll);
+
+ // The page scale delta should match the new scale on the impl side.
+ EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), pageScale);
+}
+
+TEST_F(CCLayerTreeHostImplTest, pageScaleDeltaAppliedToRootScrollLayerOnly)
+{
+ IntSize surfaceSize(10, 10);
+ float defaultPageScale = 1;
+ float newPageScale = 2;
+
+ // Create a normal scrollable root layer and another scrollable child layer.
+ setupScrollAndContentsLayers(surfaceSize);
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+ CCLayerImpl* child = root->children()[0].get();
+
+ OwnPtr<CCLayerImpl> scrollableChild = createScrollableLayer(3, surfaceSize);
+ child->addChild(scrollableChild.release());
+ CCLayerImpl* grandChild = child->children()[0].get();
+
+ // Set new page scale on impl thread by pinching.
+ m_hostImpl->pinchGestureBegin();
+ m_hostImpl->pinchGestureUpdate(newPageScale, IntPoint());
+ m_hostImpl->pinchGestureEnd();
+
+ // The page scale delta should only be applied to the scrollable root layer.
+ EXPECT_EQ(root->pageScaleDelta(), newPageScale);
+ EXPECT_EQ(child->pageScaleDelta(), defaultPageScale);
+ EXPECT_EQ(grandChild->pageScaleDelta(), defaultPageScale);
+
+ // Make sure all the layers are drawn with the page scale delta applied, i.e., the page scale
+ // delta on the root layer is applied hierarchically.
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+
+ EXPECT_EQ(root->drawTransform().m11(), newPageScale);
+ EXPECT_EQ(root->drawTransform().m22(), newPageScale);
+ EXPECT_EQ(child->drawTransform().m11(), newPageScale);
+ EXPECT_EQ(child->drawTransform().m22(), newPageScale);
+ EXPECT_EQ(grandChild->drawTransform().m11(), newPageScale);
+ EXPECT_EQ(grandChild->drawTransform().m22(), newPageScale);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollChildAndChangePageScaleOnMainThread)
+{
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setBounds(surfaceSize);
+ root->setContentBounds(surfaceSize);
+ // Also mark the root scrollable so it becomes the root scroll layer.
+ root->setScrollable(true);
+ int scrollLayerId = 2;
+ root->addChild(createScrollableLayer(scrollLayerId, surfaceSize));
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get();
+
+ IntSize scrollDelta(0, 10);
+ IntSize expectedScrollDelta(scrollDelta);
+ IntSize expectedMaxScroll(child->maxScrollPosition());
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ float pageScale = 2;
+ m_hostImpl->setPageScaleFactorAndLimits(pageScale, 1, pageScale);
+
+ // The scale should apply to the scroll delta.
+ expectedScrollDelta.scale(pageScale);
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), scrollLayerId, expectedScrollDelta);
+
+ // The scroll range should not have changed.
+ EXPECT_EQ(child->maxScrollPosition(), expectedMaxScroll);
+
+ // The page scale delta remains constant because the impl thread did not scale.
+ EXPECT_EQ(child->pageScaleDelta(), 1);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollChildBeyondLimit)
+{
+ // Scroll a child layer beyond its maximum scroll range and make sure the
+ // parent layer is scrolled on the axis on which the child was unable to
+ // scroll.
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = createScrollableLayer(1, surfaceSize);
+
+ OwnPtr<CCLayerImpl> grandChild = createScrollableLayer(3, surfaceSize);
+ grandChild->setScrollPosition(IntPoint(0, 5));
+
+ OwnPtr<CCLayerImpl> child = createScrollableLayer(2, surfaceSize);
+ child->setScrollPosition(IntPoint(3, 0));
+ child->addChild(grandChild.release());
+
+ root->addChild(child.release());
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+ {
+ IntSize scrollDelta(-8, -7);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+
+ // The grand child should have scrolled up to its limit.
+ CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get();
+ CCLayerImpl* grandChild = child->children()[0].get();
+ expectContains(*scrollInfo.get(), grandChild->id(), IntSize(0, -5));
+
+ // The child should have only scrolled on the other axis.
+ expectContains(*scrollInfo.get(), child->id(), IntSize(-3, 0));
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollEventBubbling)
+{
+ // When we try to scroll a non-scrollable child layer, the scroll delta
+ // should be applied to one of its ancestors if possible.
+ IntSize surfaceSize(10, 10);
+ OwnPtr<CCLayerImpl> root = createScrollableLayer(1, surfaceSize);
+ OwnPtr<CCLayerImpl> child = createScrollableLayer(2, surfaceSize);
+
+ child->setScrollable(false);
+ root->addChild(child.release());
+
+ m_hostImpl->setRootLayer(root.release());
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+ {
+ IntSize scrollDelta(0, 4);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+
+ // Only the root should have scrolled.
+ ASSERT_EQ(scrollInfo->scrolls.size(), 1u);
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), scrollDelta);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollBeforeRedraw)
+{
+ IntSize surfaceSize(10, 10);
+ m_hostImpl->setRootLayer(createScrollableLayer(1, surfaceSize));
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+
+ // Draw one frame and then immediately rebuild the layer tree to mimic a tree synchronization.
+ initializeRendererAndDrawFrame();
+ m_hostImpl->detachLayerTree();
+ m_hostImpl->setRootLayer(createScrollableLayer(2, surfaceSize));
+
+ // Scrolling should still work even though we did not draw yet.
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollAxisAlignedRotatedLayer)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+
+ // Rotate the root layer 90 degrees counter-clockwise about its center.
+ WebTransformationMatrix rotateTransform;
+ rotateTransform.rotate(-90);
+ m_hostImpl->rootLayer()->setTransform(rotateTransform);
+
+ IntSize surfaceSize(50, 50);
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ // Scroll to the right in screen coordinates with a gesture.
+ IntSize gestureScrollDelta(10, 0);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), gestureScrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The layer should have scrolled down in its local coordinates.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), IntSize(0, gestureScrollDelta.width()));
+
+ // Reset and scroll down with the wheel.
+ m_hostImpl->rootLayer()->setScrollDelta(FloatSize());
+ IntSize wheelScrollDelta(0, 10);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), wheelScrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The layer should have scrolled down in its local coordinates.
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), wheelScrollDelta);
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollNonAxisAlignedRotatedLayer)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+ int childLayerId = 3;
+ float childLayerAngle = -20;
+
+ // Create a child layer that is rotated to a non-axis-aligned angle.
+ OwnPtr<CCLayerImpl> child = createScrollableLayer(childLayerId, m_hostImpl->rootLayer()->contentBounds());
+ WebTransformationMatrix rotateTransform;
+ rotateTransform.translate(-50, -50);
+ rotateTransform.rotate(childLayerAngle);
+ rotateTransform.translate(50, 50);
+ child->setTransform(rotateTransform);
+
+ // Only allow vertical scrolling.
+ child->setMaxScrollPosition(IntSize(0, child->contentBounds().height()));
+ m_hostImpl->rootLayer()->addChild(child.release());
+
+ IntSize surfaceSize(50, 50);
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ {
+ // Scroll down in screen coordinates with a gesture.
+ IntSize gestureScrollDelta(0, 10);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), gestureScrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The child layer should have scrolled down in its local coordinates an amount proportional to
+ // the angle between it and the input scroll delta.
+ IntSize expectedScrollDelta(0, gestureScrollDelta.height() * cosf(deg2rad(childLayerAngle)));
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), childLayerId, expectedScrollDelta);
+
+ // The root layer should not have scrolled, because the input delta was close to the layer's
+ // axis of movement.
+ EXPECT_EQ(scrollInfo->scrolls.size(), 1u);
+ }
+
+ {
+ // Now reset and scroll the same amount horizontally.
+ m_hostImpl->rootLayer()->children()[1]->setScrollDelta(FloatSize());
+ IntSize gestureScrollDelta(10, 0);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), gestureScrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The child layer should have scrolled down in its local coordinates an amount proportional to
+ // the angle between it and the input scroll delta.
+ IntSize expectedScrollDelta(0, -gestureScrollDelta.width() * sinf(deg2rad(childLayerAngle)));
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), childLayerId, expectedScrollDelta);
+
+ // The root layer should have scrolled more, since the input scroll delta was mostly
+ // orthogonal to the child layer's vertical scroll axis.
+ IntSize expectedRootScrollDelta(gestureScrollDelta.width() * pow(cosf(deg2rad(childLayerAngle)), 2), 0);
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), expectedRootScrollDelta);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, scrollScaledLayer)
+{
+ setupScrollAndContentsLayers(IntSize(100, 100));
+
+ // Scale the layer to twice its normal size.
+ int scale = 2;
+ WebTransformationMatrix scaleTransform;
+ scaleTransform.scale(scale);
+ m_hostImpl->rootLayer()->setTransform(scaleTransform);
+
+ IntSize surfaceSize(50, 50);
+ m_hostImpl->setViewportSize(surfaceSize, surfaceSize);
+ initializeRendererAndDrawFrame();
+
+ // Scroll down in screen coordinates with a gesture.
+ IntSize scrollDelta(0, 10);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), scrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The layer should have scrolled down in its local coordinates, but half he amount.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), IntSize(0, scrollDelta.height() / scale));
+
+ // Reset and scroll down with the wheel.
+ m_hostImpl->rootLayer()->setScrollDelta(FloatSize());
+ IntSize wheelScrollDelta(0, 10);
+ EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ m_hostImpl->scrollBy(IntPoint(), wheelScrollDelta);
+ m_hostImpl->scrollEnd();
+
+ // The scale should not have been applied to the scroll delta.
+ scrollInfo = m_hostImpl->processScrollDeltas();
+ expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), wheelScrollDelta);
+}
+
+class BlendStateTrackerContext: public FakeWebGraphicsContext3D {
+public:
+ BlendStateTrackerContext() : m_blend(false) { }
+
+ virtual void enable(WGC3Denum cap)
+ {
+ if (cap == GraphicsContext3D::BLEND)
+ m_blend = true;
+ }
+
+ virtual void disable(WGC3Denum cap)
+ {
+ if (cap == GraphicsContext3D::BLEND)
+ m_blend = false;
+ }
+
+ bool blend() const { return m_blend; }
+
+private:
+ bool m_blend;
+};
+
+class BlendStateCheckLayer : public CCLayerImpl {
+public:
+ static PassOwnPtr<BlendStateCheckLayer> create(int id, CCResourceProvider* resourceProvider) { return adoptPtr(new BlendStateCheckLayer(id, resourceProvider)); }
+
+ virtual void appendQuads(CCQuadSink& quadSink, bool&) OVERRIDE
+ {
+ m_quadsAppended = true;
+
+ IntRect opaqueRect;
+ if (opaque() || m_opaqueContents)
+ opaqueRect = m_quadRect;
+ else
+ opaqueRect = m_opaqueContentRect;
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ OwnPtr<CCDrawQuad> testBlendingDrawQuad = CCTileDrawQuad::create(sharedQuadState, m_quadRect, opaqueRect, m_resourceId, IntPoint(), IntSize(1, 1), 0, false, false, false, false, false);
+ testBlendingDrawQuad->setQuadVisibleRect(m_quadVisibleRect);
+ EXPECT_EQ(m_blend, testBlendingDrawQuad->needsBlending());
+ EXPECT_EQ(m_hasRenderSurface, !!renderSurface());
+ quadSink.append(testBlendingDrawQuad.release());
+ }
+
+ void setExpectation(bool blend, bool hasRenderSurface)
+ {
+ m_blend = blend;
+ m_hasRenderSurface = hasRenderSurface;
+ m_quadsAppended = false;
+ }
+
+ bool quadsAppended() const { return m_quadsAppended; }
+
+ void setQuadRect(const IntRect& rect) { m_quadRect = rect; }
+ void setQuadVisibleRect(const IntRect& rect) { m_quadVisibleRect = rect; }
+ void setOpaqueContents(bool opaque) { m_opaqueContents = opaque; }
+ void setOpaqueContentRect(const IntRect& rect) { m_opaqueContentRect = rect; }
+
+private:
+ explicit BlendStateCheckLayer(int id, CCResourceProvider* resourceProvider)
+ : CCLayerImpl(id)
+ , m_blend(false)
+ , m_hasRenderSurface(false)
+ , m_quadsAppended(false)
+ , m_opaqueContents(false)
+ , m_quadRect(5, 5, 5, 5)
+ , m_quadVisibleRect(5, 5, 5, 5)
+ , m_resourceId(resourceProvider->createResource(CCRenderer::ContentPool, IntSize(1, 1), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny))
+ {
+ setAnchorPoint(FloatPoint(0, 0));
+ setBounds(IntSize(10, 10));
+ setContentBounds(IntSize(10, 10));
+ setDrawsContent(true);
+ }
+
+ bool m_blend;
+ bool m_hasRenderSurface;
+ bool m_quadsAppended;
+ bool m_opaqueContents;
+ IntRect m_quadRect;
+ IntRect m_opaqueContentRect;
+ IntRect m_quadVisibleRect;
+ CCResourceProvider::ResourceId m_resourceId;
+};
+
+TEST_F(CCLayerTreeHostImplTest, blendingOffWhenDrawingOpaqueLayers)
+{
+ {
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setBounds(IntSize(10, 10));
+ root->setContentBounds(root->bounds());
+ root->setDrawsContent(false);
+ m_hostImpl->setRootLayer(root.release());
+ }
+ CCLayerImpl* root = m_hostImpl->rootLayer();
+
+ root->addChild(BlendStateCheckLayer::create(2, m_hostImpl->resourceProvider()));
+ BlendStateCheckLayer* layer1 = static_cast<BlendStateCheckLayer*>(root->children()[0].get());
+ layer1->setPosition(FloatPoint(2, 2));
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ // Opaque layer, drawn without blending.
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(true);
+ layer1->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with translucent content, but opaque content, so drawn without blending.
+ layer1->setOpaque(false);
+ layer1->setOpaqueContents(true);
+ layer1->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with translucent content and painting, so drawn with blending.
+ layer1->setOpaque(false);
+ layer1->setOpaqueContents(false);
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with translucent opacity, drawn with blending.
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(true);
+ layer1->setOpacity(0.5);
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with translucent opacity and painting, drawn with blending.
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(false);
+ layer1->setOpacity(0.5);
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ layer1->addChild(BlendStateCheckLayer::create(3, m_hostImpl->resourceProvider()));
+ BlendStateCheckLayer* layer2 = static_cast<BlendStateCheckLayer*>(layer1->children()[0].get());
+ layer2->setPosition(FloatPoint(4, 4));
+
+ // 2 opaque layers, drawn without blending.
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(true);
+ layer1->setOpacity(1);
+ layer1->setExpectation(false, false);
+ layer2->setOpaque(true);
+ layer2->setOpaqueContents(true);
+ layer2->setOpacity(1);
+ layer2->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Parent layer with translucent content, drawn with blending.
+ // Child layer with opaque content, drawn without blending.
+ layer1->setOpaque(false);
+ layer1->setOpaqueContents(false);
+ layer1->setExpectation(true, false);
+ layer2->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Parent layer with translucent content but opaque painting, drawn without blending.
+ // Child layer with opaque content, drawn without blending.
+ layer1->setOpaque(false);
+ layer1->setOpaqueContents(true);
+ layer1->setExpectation(false, false);
+ layer2->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Parent layer with translucent opacity and opaque content. Since it has a
+ // drawing child, it's drawn to a render surface which carries the opacity,
+ // so it's itself drawn without blending.
+ // Child layer with opaque content, drawn without blending (parent surface
+ // carries the inherited opacity).
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(true);
+ layer1->setOpacity(0.5);
+ layer1->setExpectation(false, true);
+ layer2->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Draw again, but with child non-opaque, to make sure
+ // layer1 not culled.
+ layer1->setOpaque(true);
+ layer1->setOpaqueContents(true);
+ layer1->setOpacity(1);
+ layer1->setExpectation(false, false);
+ layer2->setOpaque(true);
+ layer2->setOpaqueContents(true);
+ layer2->setOpacity(0.5);
+ layer2->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // A second way of making the child non-opaque.
+ layer1->setOpaque(true);
+ layer1->setOpacity(1);
+ layer1->setExpectation(false, false);
+ layer2->setOpaque(false);
+ layer2->setOpaqueContents(false);
+ layer2->setOpacity(1);
+ layer2->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // And when the layer says its not opaque but is painted opaque, it is not blended.
+ layer1->setOpaque(true);
+ layer1->setOpacity(1);
+ layer1->setExpectation(false, false);
+ layer2->setOpaque(false);
+ layer2->setOpaqueContents(true);
+ layer2->setOpacity(1);
+ layer2->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ EXPECT_TRUE(layer2->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with partially opaque contents, drawn with blending.
+ layer1->setOpaque(false);
+ layer1->setQuadRect(IntRect(5, 5, 5, 5));
+ layer1->setQuadVisibleRect(IntRect(5, 5, 5, 5));
+ layer1->setOpaqueContents(false);
+ layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5));
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with partially opaque contents partially culled, drawn with blending.
+ layer1->setOpaque(false);
+ layer1->setQuadRect(IntRect(5, 5, 5, 5));
+ layer1->setQuadVisibleRect(IntRect(5, 5, 5, 2));
+ layer1->setOpaqueContents(false);
+ layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5));
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with partially opaque contents culled, drawn with blending.
+ layer1->setOpaque(false);
+ layer1->setQuadRect(IntRect(5, 5, 5, 5));
+ layer1->setQuadVisibleRect(IntRect(7, 5, 3, 5));
+ layer1->setOpaqueContents(false);
+ layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5));
+ layer1->setExpectation(true, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ // Layer with partially opaque contents and translucent contents culled, drawn without blending.
+ layer1->setOpaque(false);
+ layer1->setQuadRect(IntRect(5, 5, 5, 5));
+ layer1->setQuadVisibleRect(IntRect(5, 5, 2, 5));
+ layer1->setOpaqueContents(false);
+ layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5));
+ layer1->setExpectation(false, false);
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(layer1->quadsAppended());
+ m_hostImpl->didDrawAllLayers(frame);
+
+}
+
+TEST_F(CCLayerTreeHostImplTest, viewportCovered)
+{
+ m_hostImpl->initializeRenderer(createContext(), UnthrottledUploader);
+ m_hostImpl->setBackgroundColor(SK_ColorGRAY);
+
+ IntSize viewportSize(1000, 1000);
+ m_hostImpl->setViewportSize(viewportSize, viewportSize);
+
+ m_hostImpl->setRootLayer(BlendStateCheckLayer::create(1, m_hostImpl->resourceProvider()));
+ BlendStateCheckLayer* root = static_cast<BlendStateCheckLayer*>(m_hostImpl->rootLayer());
+ root->setExpectation(false, true);
+ root->setOpaque(true);
+
+ // No gutter rects
+ {
+ IntRect layerRect(0, 0, 1000, 1000);
+ root->setPosition(layerRect.location());
+ root->setBounds(layerRect.size());
+ root->setContentBounds(layerRect.size());
+ root->setQuadRect(IntRect(IntPoint(), layerRect.size()));
+ root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size()));
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ ASSERT_EQ(1u, frame.renderPasses.size());
+
+ size_t numGutterQuads = 0;
+ for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i)
+ numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0;
+ EXPECT_EQ(0u, numGutterQuads);
+ EXPECT_EQ(1u, frame.renderPasses[0]->quadList().size());
+
+ verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize));
+ m_hostImpl->didDrawAllLayers(frame);
+ }
+
+ // Empty visible content area (fullscreen gutter rect)
+ {
+ IntRect layerRect(0, 0, 0, 0);
+ root->setPosition(layerRect.location());
+ root->setBounds(layerRect.size());
+ root->setContentBounds(layerRect.size());
+ root->setQuadRect(IntRect(IntPoint(), layerRect.size()));
+ root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size()));
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ ASSERT_EQ(1u, frame.renderPasses.size());
+ m_hostImpl->didDrawAllLayers(frame);
+
+ size_t numGutterQuads = 0;
+ for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i)
+ numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0;
+ EXPECT_EQ(1u, numGutterQuads);
+ EXPECT_EQ(1u, frame.renderPasses[0]->quadList().size());
+
+ verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize));
+ m_hostImpl->didDrawAllLayers(frame);
+ }
+
+ // Content area in middle of clip rect (four surrounding gutter rects)
+ {
+ IntRect layerRect(500, 500, 200, 200);
+ root->setPosition(layerRect.location());
+ root->setBounds(layerRect.size());
+ root->setContentBounds(layerRect.size());
+ root->setQuadRect(IntRect(IntPoint(), layerRect.size()));
+ root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size()));
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ ASSERT_EQ(1u, frame.renderPasses.size());
+
+ size_t numGutterQuads = 0;
+ for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i)
+ numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0;
+ EXPECT_EQ(4u, numGutterQuads);
+ EXPECT_EQ(5u, frame.renderPasses[0]->quadList().size());
+
+ verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize));
+ m_hostImpl->didDrawAllLayers(frame);
+ }
+
+}
+
+
+class ReshapeTrackerContext: public FakeWebGraphicsContext3D {
+public:
+ ReshapeTrackerContext() : m_reshapeCalled(false) { }
+
+ virtual void reshape(int width, int height)
+ {
+ m_reshapeCalled = true;
+ }
+
+ bool reshapeCalled() const { return m_reshapeCalled; }
+
+private:
+ bool m_reshapeCalled;
+};
+
+class FakeDrawableCCLayerImpl: public CCLayerImpl {
+public:
+ explicit FakeDrawableCCLayerImpl(int id) : CCLayerImpl(id) { }
+};
+
+// Only reshape when we know we are going to draw. Otherwise, the reshape
+// can leave the window at the wrong size if we never draw and the proper
+// viewport size is never set.
+TEST_F(CCLayerTreeHostImplTest, reshapeNotCalledUntilDraw)
+{
+ OwnPtr<CCGraphicsContext> ccContext = FakeWebCompositorOutputSurface::create(adoptPtr(new ReshapeTrackerContext));
+ ReshapeTrackerContext* reshapeTracker = static_cast<ReshapeTrackerContext*>(ccContext->context3D());
+ m_hostImpl->initializeRenderer(ccContext.release(), UnthrottledUploader);
+
+ CCLayerImpl* root = new FakeDrawableCCLayerImpl(1);
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setBounds(IntSize(10, 10));
+ root->setDrawsContent(true);
+ m_hostImpl->setRootLayer(adoptPtr(root));
+ EXPECT_FALSE(reshapeTracker->reshapeCalled());
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ EXPECT_TRUE(reshapeTracker->reshapeCalled());
+ m_hostImpl->didDrawAllLayers(frame);
+}
+
+class PartialSwapTrackerContext : public FakeWebGraphicsContext3D {
+public:
+ virtual void postSubBufferCHROMIUM(int x, int y, int width, int height)
+ {
+ m_partialSwapRect = IntRect(x, y, width, height);
+ }
+
+ virtual WebString getString(WGC3Denum name)
+ {
+ if (name == GraphicsContext3D::EXTENSIONS)
+ return WebString("GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_set_visibility");
+
+ return WebString();
+ }
+
+ IntRect partialSwapRect() const { return m_partialSwapRect; }
+
+private:
+ IntRect m_partialSwapRect;
+};
+
+// Make sure damage tracking propagates all the way to the graphics context,
+// where it should request to swap only the subBuffer that is damaged.
+TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect)
+{
+ OwnPtr<CCGraphicsContext> ccContext = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapTrackerContext));
+ PartialSwapTrackerContext* partialSwapTracker = static_cast<PartialSwapTrackerContext*>(ccContext->context3D());
+
+ // This test creates its own CCLayerTreeHostImpl, so
+ // that we can force partial swap enabled.
+ CCLayerTreeSettings settings;
+ CCSettings::setPartialSwapEnabled(true);
+ OwnPtr<CCLayerTreeHostImpl> layerTreeHostImpl = CCLayerTreeHostImpl::create(settings, this);
+ layerTreeHostImpl->initializeRenderer(ccContext.release(), UnthrottledUploader);
+ layerTreeHostImpl->setViewportSize(IntSize(500, 500), IntSize(500, 500));
+
+ CCLayerImpl* root = new FakeDrawableCCLayerImpl(1);
+ CCLayerImpl* child = new FakeDrawableCCLayerImpl(2);
+ child->setPosition(FloatPoint(12, 13));
+ child->setAnchorPoint(FloatPoint(0, 0));
+ child->setBounds(IntSize(14, 15));
+ child->setContentBounds(IntSize(14, 15));
+ child->setDrawsContent(true);
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setBounds(IntSize(500, 500));
+ root->setContentBounds(IntSize(500, 500));
+ root->setDrawsContent(true);
+ root->addChild(adoptPtr(child));
+ layerTreeHostImpl->setRootLayer(adoptPtr(root));
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ // First frame, the entire screen should get swapped.
+ EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame));
+ layerTreeHostImpl->drawLayers(frame);
+ layerTreeHostImpl->didDrawAllLayers(frame);
+ layerTreeHostImpl->swapBuffers();
+ IntRect actualSwapRect = partialSwapTracker->partialSwapRect();
+ IntRect expectedSwapRect = IntRect(IntPoint::zero(), IntSize(500, 500));
+ EXPECT_EQ(expectedSwapRect.x(), actualSwapRect.x());
+ EXPECT_EQ(expectedSwapRect.y(), actualSwapRect.y());
+ EXPECT_EQ(expectedSwapRect.width(), actualSwapRect.width());
+ EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height());
+
+ // Second frame, only the damaged area should get swapped. Damage should be the union
+ // of old and new child rects.
+ // expected damage rect: IntRect(IntPoint::zero(), IntSize(26, 28));
+ // expected swap rect: vertically flipped, with origin at bottom left corner.
+ child->setPosition(FloatPoint(0, 0));
+ EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame));
+ layerTreeHostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ layerTreeHostImpl->swapBuffers();
+ actualSwapRect = partialSwapTracker->partialSwapRect();
+ expectedSwapRect = IntRect(IntPoint(0, 500-28), IntSize(26, 28));
+ EXPECT_EQ(expectedSwapRect.x(), actualSwapRect.x());
+ EXPECT_EQ(expectedSwapRect.y(), actualSwapRect.y());
+ EXPECT_EQ(expectedSwapRect.width(), actualSwapRect.width());
+ EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height());
+
+ // Make sure that partial swap is constrained to the viewport dimensions
+ // expected damage rect: IntRect(IntPoint::zero(), IntSize(500, 500));
+ // expected swap rect: flipped damage rect, but also clamped to viewport
+ layerTreeHostImpl->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+ root->setOpacity(0.7f); // this will damage everything
+ EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame));
+ layerTreeHostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ layerTreeHostImpl->swapBuffers();
+ actualSwapRect = partialSwapTracker->partialSwapRect();
+ expectedSwapRect = IntRect(IntPoint::zero(), IntSize(10, 10));
+ EXPECT_EQ(expectedSwapRect.x(), actualSwapRect.x());
+ EXPECT_EQ(expectedSwapRect.y(), actualSwapRect.y());
+ EXPECT_EQ(expectedSwapRect.width(), actualSwapRect.width());
+ EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height());
+}
+
+TEST_F(CCLayerTreeHostImplTest, rootLayerDoesntCreateExtraSurface)
+{
+ CCLayerImpl* root = new FakeDrawableCCLayerImpl(1);
+ CCLayerImpl* child = new FakeDrawableCCLayerImpl(2);
+ child->setAnchorPoint(FloatPoint(0, 0));
+ child->setBounds(IntSize(10, 10));
+ child->setContentBounds(IntSize(10, 10));
+ child->setDrawsContent(true);
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setBounds(IntSize(10, 10));
+ root->setContentBounds(IntSize(10, 10));
+ root->setDrawsContent(true);
+ root->setOpacity(0.7f);
+ root->addChild(adoptPtr(child));
+
+ m_hostImpl->setRootLayer(adoptPtr(root));
+
+ CCLayerTreeHostImpl::FrameData frame;
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ EXPECT_EQ(1u, frame.renderSurfaceLayerList->size());
+ EXPECT_EQ(1u, frame.renderPasses.size());
+ m_hostImpl->didDrawAllLayers(frame);
+}
+
+} // namespace
+
+class FakeLayerWithQuads : public CCLayerImpl {
+public:
+ static PassOwnPtr<FakeLayerWithQuads> create(int id) { return adoptPtr(new FakeLayerWithQuads(id)); }
+
+ virtual void appendQuads(CCQuadSink& quadSink, bool&) OVERRIDE
+ {
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+
+ SkColor gray = SkColorSetRGB(100, 100, 100);
+ IntRect quadRect(IntPoint(0, 0), contentBounds());
+ OwnPtr<CCDrawQuad> myQuad = CCSolidColorDrawQuad::create(sharedQuadState, quadRect, gray);
+ quadSink.append(myQuad.release());
+ }
+
+private:
+ FakeLayerWithQuads(int id)
+ : CCLayerImpl(id)
+ {
+ }
+};
+
+namespace {
+
+class MockContext : public FakeWebGraphicsContext3D {
+public:
+ MOCK_METHOD1(useProgram, void(WebGLId program));
+ MOCK_METHOD5(uniform4f, void(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z, WGC3Dfloat w));
+ MOCK_METHOD4(uniformMatrix4fv, void(WGC3Dint location, WGC3Dsizei count, WGC3Dboolean transpose, const WGC3Dfloat* value));
+ MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset));
+ MOCK_METHOD1(getString, WebString(WGC3Denum name));
+ MOCK_METHOD0(getRequestableExtensionsCHROMIUM, WebString());
+ MOCK_METHOD1(enable, void(WGC3Denum cap));
+ MOCK_METHOD1(disable, void(WGC3Denum cap));
+ MOCK_METHOD4(scissor, void(WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height));
+};
+
+class MockContextHarness {
+private:
+ MockContext* m_context;
+public:
+ MockContextHarness(MockContext* context)
+ : m_context(context)
+ {
+ // Catch "uninteresting" calls
+ EXPECT_CALL(*m_context, useProgram(_))
+ .Times(0);
+
+ EXPECT_CALL(*m_context, drawElements(_, _, _, _))
+ .Times(0);
+
+ // These are not asserted
+ EXPECT_CALL(*m_context, uniformMatrix4fv(_, _, _, _))
+ .WillRepeatedly(Return());
+
+ EXPECT_CALL(*m_context, uniform4f(_, _, _, _, _))
+ .WillRepeatedly(Return());
+
+ // Any other strings are empty
+ EXPECT_CALL(*m_context, getString(_))
+ .WillRepeatedly(Return(WebString()));
+
+ // Support for partial swap, if needed
+ EXPECT_CALL(*m_context, getString(GraphicsContext3D::EXTENSIONS))
+ .WillRepeatedly(Return(WebString("GL_CHROMIUM_post_sub_buffer")));
+
+ EXPECT_CALL(*m_context, getRequestableExtensionsCHROMIUM())
+ .WillRepeatedly(Return(WebString("GL_CHROMIUM_post_sub_buffer")));
+
+ // Any un-sanctioned calls to enable() are OK
+ EXPECT_CALL(*m_context, enable(_))
+ .WillRepeatedly(Return());
+
+ // Any un-sanctioned calls to disable() are OK
+ EXPECT_CALL(*m_context, disable(_))
+ .WillRepeatedly(Return());
+ }
+
+ void mustDrawSolidQuad()
+ {
+ EXPECT_CALL(*m_context, drawElements(GraphicsContext3D::TRIANGLES, 6, GraphicsContext3D::UNSIGNED_SHORT, 0))
+ .WillOnce(Return())
+ .RetiresOnSaturation();
+
+ // 1 is hardcoded return value of fake createProgram()
+ EXPECT_CALL(*m_context, useProgram(1))
+ .WillOnce(Return())
+ .RetiresOnSaturation();
+
+ }
+
+ void mustSetScissor(int x, int y, int width, int height)
+ {
+ EXPECT_CALL(*m_context, enable(GraphicsContext3D::SCISSOR_TEST))
+ .WillRepeatedly(Return());
+
+ EXPECT_CALL(*m_context, scissor(x, y, width, height))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return());
+ }
+
+ void mustSetNoScissor()
+ {
+ EXPECT_CALL(*m_context, disable(GraphicsContext3D::SCISSOR_TEST))
+ .WillRepeatedly(Return());
+
+ EXPECT_CALL(*m_context, enable(GraphicsContext3D::SCISSOR_TEST))
+ .Times(0);
+
+ EXPECT_CALL(*m_context, scissor(_, _, _, _))
+ .Times(0);
+ }
+};
+
+TEST_F(CCLayerTreeHostImplTest, noPartialSwap)
+{
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new MockContext));
+ MockContext* mockContext = static_cast<MockContext*>(context->context3D());
+ MockContextHarness harness(mockContext);
+
+ harness.mustDrawSolidQuad();
+ harness.mustSetScissor(0, 0, 10, 10);
+
+ // Run test case
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(false, context.release(), FakeLayerWithQuads::create(1));
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ Mock::VerifyAndClearExpectations(&mockContext);
+}
+
+TEST_F(CCLayerTreeHostImplTest, partialSwap)
+{
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new MockContext));
+ MockContext* mockContext = static_cast<MockContext*>(context->context3D());
+ MockContextHarness harness(mockContext);
+
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(true, context.release(), FakeLayerWithQuads::create(1));
+
+ // The first frame is not a partially-swapped one.
+ harness.mustSetScissor(0, 0, 10, 10);
+ harness.mustDrawSolidQuad();
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+ Mock::VerifyAndClearExpectations(&mockContext);
+
+ // Damage a portion of the frame.
+ myHostImpl->rootLayer()->setUpdateRect(IntRect(0, 0, 2, 3));
+
+ // The second frame will be partially-swapped (the y coordinates are flipped).
+ harness.mustSetScissor(0, 7, 2, 3);
+ harness.mustDrawSolidQuad();
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+ Mock::VerifyAndClearExpectations(&mockContext);
+}
+
+class PartialSwapContext : public FakeWebGraphicsContext3D {
+public:
+ WebString getString(WGC3Denum name)
+ {
+ if (name == GraphicsContext3D::EXTENSIONS)
+ return WebString("GL_CHROMIUM_post_sub_buffer");
+ return WebString();
+ }
+
+ WebString getRequestableExtensionsCHROMIUM()
+ {
+ return WebString("GL_CHROMIUM_post_sub_buffer");
+ }
+
+ // Unlimited texture size.
+ virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value)
+ {
+ if (pname == WebCore::GraphicsContext3D::MAX_TEXTURE_SIZE)
+ *value = 8192;
+ }
+};
+
+static PassOwnPtr<CCLayerTreeHostImpl> setupLayersForOpacity(bool partialSwap, CCLayerTreeHostImplClient* client)
+{
+ CCSettings::setPartialSwapEnabled(partialSwap);
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ CCLayerTreeSettings settings;
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, client);
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(100, 100), IntSize(100, 100));
+
+ /*
+ Layers are created as follows:
+
+ +--------------------+
+ | 1 |
+ | +-----------+ |
+ | | 2 | |
+ | | +-------------------+
+ | | | 3 |
+ | | +-------------------+
+ | | | |
+ | +-----------+ |
+ | |
+ | |
+ +--------------------+
+
+ Layers 1, 2 have render surfaces
+ */
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> grandChild = FakeLayerWithQuads::create(3);
+
+ IntRect rootRect(0, 0, 100, 100);
+ IntRect childRect(10, 10, 50, 50);
+ IntRect grandChildRect(5, 5, 150, 150);
+
+ root->createRenderSurface();
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(rootRect.x(), rootRect.y()));
+ root->setBounds(IntSize(rootRect.width(), rootRect.height()));
+ root->setContentBounds(root->bounds());
+ root->setVisibleContentRect(rootRect);
+ root->setDrawsContent(false);
+ root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(rootRect.width(), rootRect.height())));
+
+ child->setAnchorPoint(FloatPoint(0, 0));
+ child->setPosition(FloatPoint(childRect.x(), childRect.y()));
+ child->setOpacity(0.5f);
+ child->setBounds(IntSize(childRect.width(), childRect.height()));
+ child->setContentBounds(child->bounds());
+ child->setVisibleContentRect(childRect);
+ child->setDrawsContent(false);
+
+ grandChild->setAnchorPoint(FloatPoint(0, 0));
+ grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y()));
+ grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height()));
+ grandChild->setContentBounds(grandChild->bounds());
+ grandChild->setVisibleContentRect(grandChildRect);
+ grandChild->setDrawsContent(true);
+
+ child->addChild(grandChild.release());
+ root->addChild(child.release());
+
+ myHostImpl->setRootLayer(root.release());
+ return myHostImpl.release();
+}
+
+TEST_F(CCLayerTreeHostImplTest, contributingLayerEmptyScissorPartialSwap)
+{
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = setupLayersForOpacity(true, this);
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Just for consistency, the most interesting stuff already happened
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+
+ // Verify all quads have been computed
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ ASSERT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, contributingLayerEmptyScissorNoPartialSwap)
+{
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = setupLayersForOpacity(false, this);
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Just for consistency, the most interesting stuff already happened
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+
+ // Verify all quads have been computed
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ ASSERT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ }
+}
+
+// Make sure that context lost notifications are propagated through the tree.
+class ContextLostNotificationCheckLayer : public CCLayerImpl {
+public:
+ static PassOwnPtr<ContextLostNotificationCheckLayer> create(int id) { return adoptPtr(new ContextLostNotificationCheckLayer(id)); }
+
+ virtual void didLoseContext() OVERRIDE
+ {
+ m_didLoseContextCalled = true;
+ }
+
+ bool didLoseContextCalled() const { return m_didLoseContextCalled; }
+
+private:
+ explicit ContextLostNotificationCheckLayer(int id)
+ : CCLayerImpl(id)
+ , m_didLoseContextCalled(false)
+ {
+ }
+
+ bool m_didLoseContextCalled;
+};
+
+TEST_F(CCLayerTreeHostImplTest, contextLostAndRestoredNotificationSentToAllLayers)
+{
+ m_hostImpl->setRootLayer(ContextLostNotificationCheckLayer::create(1));
+ ContextLostNotificationCheckLayer* root = static_cast<ContextLostNotificationCheckLayer*>(m_hostImpl->rootLayer());
+
+ root->addChild(ContextLostNotificationCheckLayer::create(1));
+ ContextLostNotificationCheckLayer* layer1 = static_cast<ContextLostNotificationCheckLayer*>(root->children()[0].get());
+
+ layer1->addChild(ContextLostNotificationCheckLayer::create(2));
+ ContextLostNotificationCheckLayer* layer2 = static_cast<ContextLostNotificationCheckLayer*>(layer1->children()[0].get());
+
+ EXPECT_FALSE(root->didLoseContextCalled());
+ EXPECT_FALSE(layer1->didLoseContextCalled());
+ EXPECT_FALSE(layer2->didLoseContextCalled());
+
+ m_hostImpl->initializeRenderer(createContext(), UnthrottledUploader);
+
+ EXPECT_TRUE(root->didLoseContextCalled());
+ EXPECT_TRUE(layer1->didLoseContextCalled());
+ EXPECT_TRUE(layer2->didLoseContextCalled());
+}
+
+TEST_F(CCLayerTreeHostImplTest, finishAllRenderingAfterContextLost)
+{
+ CCLayerTreeSettings settings;
+ m_hostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // The context initialization will fail, but we should still be able to call finishAllRendering() without any ill effects.
+ m_hostImpl->initializeRenderer(FakeWebCompositorOutputSurface::create(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails)), UnthrottledUploader);
+ m_hostImpl->finishAllRendering();
+}
+
+// Fake WebGraphicsContext3D that will cause a failure if trying to use a
+// resource that wasn't created by it (resources created by
+// FakeWebGraphicsContext3D have an id of 1).
+class StrictWebGraphicsContext3D : public FakeWebGraphicsContext3D {
+public:
+ StrictWebGraphicsContext3D()
+ : FakeWebGraphicsContext3D()
+ {
+ m_nextTextureId = 7; // Start allocating texture ids larger than any other resource IDs so we can tell if someone's mixing up their resource types.
+ }
+
+ virtual WebGLId createBuffer() { return 2; }
+ virtual WebGLId createFramebuffer() { return 3; }
+ virtual WebGLId createProgram() { return 4; }
+ virtual WebGLId createRenderbuffer() { return 5; }
+ virtual WebGLId createShader(WGC3Denum) { return 6; }
+
+ virtual void deleteBuffer(WebGLId id)
+ {
+ if (id != 2)
+ ADD_FAILURE() << "Trying to delete buffer id " << id;
+ }
+
+ virtual void deleteFramebuffer(WebGLId id)
+ {
+ if (id != 3)
+ ADD_FAILURE() << "Trying to delete framebuffer id " << id;
+ }
+
+ virtual void deleteProgram(WebGLId id)
+ {
+ if (id != 4)
+ ADD_FAILURE() << "Trying to delete program id " << id;
+ }
+
+ virtual void deleteRenderbuffer(WebGLId id)
+ {
+ if (id != 5)
+ ADD_FAILURE() << "Trying to delete renderbuffer id " << id;
+ }
+
+ virtual void deleteShader(WebGLId id)
+ {
+ if (id != 6)
+ ADD_FAILURE() << "Trying to delete shader id " << id;
+ }
+
+ virtual WebGLId createTexture()
+ {
+ unsigned textureId = FakeWebGraphicsContext3D::createTexture();
+ m_allocatedTextureIds.add(textureId);
+ return textureId;
+ }
+ virtual void deleteTexture(WebGLId id)
+ {
+ if (!m_allocatedTextureIds.contains(id))
+ ADD_FAILURE() << "Trying to delete texture id " << id;
+ m_allocatedTextureIds.remove(id);
+ }
+
+ virtual void bindBuffer(WGC3Denum, WebGLId id)
+ {
+ if (id != 2 && id)
+ ADD_FAILURE() << "Trying to bind buffer id " << id;
+ }
+
+ virtual void bindFramebuffer(WGC3Denum, WebGLId id)
+ {
+ if (id != 3 && id)
+ ADD_FAILURE() << "Trying to bind framebuffer id " << id;
+ }
+
+ virtual void useProgram(WebGLId id)
+ {
+ if (id != 4)
+ ADD_FAILURE() << "Trying to use program id " << id;
+ }
+
+ virtual void bindRenderbuffer(WGC3Denum, WebGLId id)
+ {
+ if (id != 5 && id)
+ ADD_FAILURE() << "Trying to bind renderbuffer id " << id;
+ }
+
+ virtual void attachShader(WebGLId program, WebGLId shader)
+ {
+ if ((program != 4) || (shader != 6))
+ ADD_FAILURE() << "Trying to attach shader id " << shader << " to program id " << program;
+ }
+
+ virtual void bindTexture(WGC3Denum, WebGLId id)
+ {
+ if (id && !m_allocatedTextureIds.contains(id))
+ ADD_FAILURE() << "Trying to bind texture id " << id;
+ }
+
+private:
+ HashSet<unsigned> m_allocatedTextureIds;
+};
+
+// Fake video frame that represents a 4x4 YUV video frame.
+class FakeVideoFrame: public WebVideoFrame {
+public:
+ FakeVideoFrame() : m_textureId(0) { memset(m_data, 0x80, sizeof(m_data)); }
+ virtual ~FakeVideoFrame() { }
+ virtual Format format() const { return m_textureId ? FormatNativeTexture : FormatYV12; }
+ virtual unsigned width() const { return 4; }
+ virtual unsigned height() const { return 4; }
+ virtual unsigned planes() const { return 3; }
+ virtual int stride(unsigned plane) const { return 4; }
+ virtual const void* data(unsigned plane) const { return m_data; }
+ virtual unsigned textureId() const { return m_textureId; }
+ virtual unsigned textureTarget() const { return m_textureId ? GraphicsContext3D::TEXTURE_2D : 0; }
+
+ void setTextureId(unsigned id) { m_textureId = id; }
+
+private:
+ char m_data[16];
+ unsigned m_textureId;
+};
+
+// Fake video frame provider that always provides the same FakeVideoFrame.
+class FakeVideoFrameProvider: public WebVideoFrameProvider {
+public:
+ FakeVideoFrameProvider() : m_frame(0), m_client(0) { }
+ virtual ~FakeVideoFrameProvider()
+ {
+ if (m_client)
+ m_client->stopUsingProvider();
+ }
+
+ virtual void setVideoFrameProviderClient(Client* client) { m_client = client; }
+ virtual WebVideoFrame* getCurrentFrame() { return m_frame; }
+ virtual void putCurrentFrame(WebVideoFrame*) { }
+
+ void setFrame(WebVideoFrame* frame) { m_frame = frame; }
+
+private:
+ WebVideoFrame* m_frame;
+ Client* m_client;
+};
+
+class StrictWebGraphicsContext3DWithIOSurface : public StrictWebGraphicsContext3D {
+public:
+ virtual WebString getString(WGC3Denum name) OVERRIDE
+ {
+ if (name == WebCore::GraphicsContext3D::EXTENSIONS)
+ return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
+
+ return WebString();
+ }
+};
+
+class FakeWebGraphicsContext3DWithIOSurface : public FakeWebGraphicsContext3D {
+public:
+ virtual WebString getString(WGC3Denum name) OVERRIDE
+ {
+ if (name == WebCore::GraphicsContext3D::EXTENSIONS)
+ return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
+
+ return WebString();
+ }
+};
+
+class FakeWebScrollbarThemeGeometryNonEmpty : public FakeWebScrollbarThemeGeometry {
+ virtual WebRect trackRect(WebScrollbar*) OVERRIDE { return WebRect(0, 0, 10, 10); }
+ virtual WebRect thumbRect(WebScrollbar*) OVERRIDE { return WebRect(0, 5, 5, 2); }
+ virtual void splitTrack(WebScrollbar*, const WebRect& track, WebRect& startTrack, WebRect& thumb, WebRect& endTrack) OVERRIDE
+ {
+ thumb = WebRect(0, 5, 5, 2);
+ startTrack = WebRect(0, 5, 0, 5);
+ endTrack = WebRect(0, 0, 0, 5);
+ }
+};
+
+class FakeScrollbarLayerImpl : public CCScrollbarLayerImpl {
+public:
+ static PassOwnPtr<FakeScrollbarLayerImpl> create(int id)
+ {
+ return adoptPtr(new FakeScrollbarLayerImpl(id));
+ }
+
+ void createResources(CCResourceProvider* provider)
+ {
+ ASSERT(provider);
+ int pool = 0;
+ IntSize size(10, 10);
+ GC3Denum format = GraphicsContext3D::RGBA;
+ CCResourceProvider::TextureUsageHint hint = CCResourceProvider::TextureUsageAny;
+ setScrollbarGeometry(FakeWebScrollbarThemeGeometryNonEmpty::create());
+
+ setBackTrackResourceId(provider->createResource(pool, size, format, hint));
+ setForeTrackResourceId(provider->createResource(pool, size, format, hint));
+ setThumbResourceId(provider->createResource(pool, size, format, hint));
+ }
+
+protected:
+ explicit FakeScrollbarLayerImpl(int id)
+ : CCScrollbarLayerImpl(id)
+ {
+ }
+};
+
+TEST_F(CCLayerTreeHostImplTest, dontUseOldResourcesAfterLostContext)
+{
+ OwnPtr<CCLayerImpl> rootLayer(CCLayerImpl::create(1));
+ rootLayer->setBounds(IntSize(10, 10));
+ rootLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ OwnPtr<CCTiledLayerImpl> tileLayer = CCTiledLayerImpl::create(2);
+ tileLayer->setBounds(IntSize(10, 10));
+ tileLayer->setAnchorPoint(FloatPoint(0, 0));
+ tileLayer->setContentBounds(IntSize(10, 10));
+ tileLayer->setDrawsContent(true);
+ tileLayer->setSkipsDraw(false);
+ OwnPtr<CCLayerTilingData> tilingData(CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels));
+ tilingData->setBounds(IntSize(10, 10));
+ tileLayer->setTilingData(*tilingData);
+ tileLayer->pushTileProperties(0, 0, 1, IntRect(0, 0, 10, 10));
+ rootLayer->addChild(tileLayer.release());
+
+ OwnPtr<CCTextureLayerImpl> textureLayer = CCTextureLayerImpl::create(3);
+ textureLayer->setBounds(IntSize(10, 10));
+ textureLayer->setAnchorPoint(FloatPoint(0, 0));
+ textureLayer->setContentBounds(IntSize(10, 10));
+ textureLayer->setDrawsContent(true);
+ textureLayer->setTextureId(1);
+ rootLayer->addChild(textureLayer.release());
+
+ FakeVideoFrame videoFrame;
+ FakeVideoFrameProvider provider;
+ provider.setFrame(&videoFrame);
+ OwnPtr<CCVideoLayerImpl> videoLayer = CCVideoLayerImpl::create(4, &provider);
+ videoLayer->setBounds(IntSize(10, 10));
+ videoLayer->setAnchorPoint(FloatPoint(0, 0));
+ videoLayer->setContentBounds(IntSize(10, 10));
+ videoLayer->setDrawsContent(true);
+ videoLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(videoLayer.release());
+
+ FakeVideoFrame hwVideoFrame;
+ FakeVideoFrameProvider hwProvider;
+ hwProvider.setFrame(&hwVideoFrame);
+ OwnPtr<CCVideoLayerImpl> hwVideoLayer = CCVideoLayerImpl::create(5, &hwProvider);
+ hwVideoLayer->setBounds(IntSize(10, 10));
+ hwVideoLayer->setAnchorPoint(FloatPoint(0, 0));
+ hwVideoLayer->setContentBounds(IntSize(10, 10));
+ hwVideoLayer->setDrawsContent(true);
+ hwVideoLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(hwVideoLayer.release());
+
+ OwnPtr<CCIOSurfaceLayerImpl> ioSurfaceLayer = CCIOSurfaceLayerImpl::create(6);
+ ioSurfaceLayer->setBounds(IntSize(10, 10));
+ ioSurfaceLayer->setAnchorPoint(FloatPoint(0, 0));
+ ioSurfaceLayer->setContentBounds(IntSize(10, 10));
+ ioSurfaceLayer->setDrawsContent(true);
+ ioSurfaceLayer->setIOSurfaceProperties(1, IntSize(10, 10));
+ ioSurfaceLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(ioSurfaceLayer.release());
+
+ OwnPtr<CCHeadsUpDisplayLayerImpl> hudLayer = CCHeadsUpDisplayLayerImpl::create(7);
+ hudLayer->setBounds(IntSize(10, 10));
+ hudLayer->setAnchorPoint(FloatPoint(0, 0));
+ hudLayer->setContentBounds(IntSize(10, 10));
+ hudLayer->setDrawsContent(true);
+ hudLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(hudLayer.release());
+
+ OwnPtr<FakeScrollbarLayerImpl> scrollbarLayer(FakeScrollbarLayerImpl::create(8));
+ scrollbarLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ scrollbarLayer->setBounds(IntSize(10, 10));
+ scrollbarLayer->setContentBounds(IntSize(10, 10));
+ scrollbarLayer->setDrawsContent(true);
+ scrollbarLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ scrollbarLayer->createResources(m_hostImpl->resourceProvider());
+ rootLayer->addChild(scrollbarLayer.release());
+
+ // Use a context that supports IOSurfaces
+ m_hostImpl->initializeRenderer(FakeWebCompositorOutputSurface::create(adoptPtr(new FakeWebGraphicsContext3DWithIOSurface)), UnthrottledUploader);
+
+ hwVideoFrame.setTextureId(m_hostImpl->resourceProvider()->graphicsContext3D()->createTexture());
+
+ m_hostImpl->setRootLayer(rootLayer.release());
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ m_hostImpl->swapBuffers();
+
+ unsigned numResources = m_hostImpl->resourceProvider()->numResources();
+
+ // Lose the context, replacing it with a StrictWebGraphicsContext3DWithIOSurface,
+ // that will warn if any resource from the previous context gets used.
+ m_hostImpl->initializeRenderer(FakeWebCompositorOutputSurface::create(adoptPtr(new StrictWebGraphicsContext3DWithIOSurface)), UnthrottledUploader);
+
+ // Create dummy resources so that looking up an old resource will get an
+ // invalid texture id mapping.
+ for (unsigned i = 0; i < numResources; ++i)
+ m_hostImpl->resourceProvider()->createResourceFromExternalTexture(1);
+
+ // The WebVideoFrameProvider is expected to recreate its textures after a
+ // lost context (or not serve a frame).
+ hwProvider.setFrame(0);
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ m_hostImpl->swapBuffers();
+
+ hwVideoFrame.setTextureId(m_hostImpl->resourceProvider()->graphicsContext3D()->createTexture());
+ hwProvider.setFrame(&hwVideoFrame);
+
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ m_hostImpl->swapBuffers();
+}
+
+// Fake WebGraphicsContext3D that tracks the number of textures in use.
+class TrackingWebGraphicsContext3D : public FakeWebGraphicsContext3D {
+public:
+ TrackingWebGraphicsContext3D()
+ : FakeWebGraphicsContext3D()
+ , m_numTextures(0)
+ { }
+
+ virtual WebGLId createTexture() OVERRIDE
+ {
+ WebGLId id = FakeWebGraphicsContext3D::createTexture();
+
+ m_textures.set(id, true);
+ ++m_numTextures;
+ return id;
+ }
+
+ virtual void deleteTexture(WebGLId id) OVERRIDE
+ {
+ if (!m_textures.get(id))
+ return;
+
+ m_textures.set(id, false);
+ --m_numTextures;
+ }
+
+ virtual WebString getString(WGC3Denum name) OVERRIDE
+ {
+ if (name == WebCore::GraphicsContext3D::EXTENSIONS)
+ return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
+
+ return WebString();
+ }
+
+ unsigned numTextures() const { return m_numTextures; }
+
+private:
+ HashMap<WebGLId, bool> m_textures;
+ unsigned m_numTextures;
+};
+
+TEST_F(CCLayerTreeHostImplTest, layersFreeTextures)
+{
+ OwnPtr<CCLayerImpl> rootLayer(CCLayerImpl::create(1));
+ rootLayer->setBounds(IntSize(10, 10));
+ rootLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ OwnPtr<CCTiledLayerImpl> tileLayer = CCTiledLayerImpl::create(2);
+ tileLayer->setBounds(IntSize(10, 10));
+ tileLayer->setAnchorPoint(FloatPoint(0, 0));
+ tileLayer->setContentBounds(IntSize(10, 10));
+ tileLayer->setDrawsContent(true);
+ tileLayer->setSkipsDraw(false);
+ OwnPtr<CCLayerTilingData> tilingData(CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels));
+ tilingData->setBounds(IntSize(10, 10));
+ tileLayer->setTilingData(*tilingData);
+ tileLayer->pushTileProperties(0, 0, 1, IntRect(0, 0, 10, 10));
+ rootLayer->addChild(tileLayer.release());
+
+ OwnPtr<CCTextureLayerImpl> textureLayer = CCTextureLayerImpl::create(3);
+ textureLayer->setBounds(IntSize(10, 10));
+ textureLayer->setAnchorPoint(FloatPoint(0, 0));
+ textureLayer->setContentBounds(IntSize(10, 10));
+ textureLayer->setDrawsContent(true);
+ textureLayer->setTextureId(1);
+ rootLayer->addChild(textureLayer.release());
+
+ FakeVideoFrameProvider provider;
+ OwnPtr<CCVideoLayerImpl> videoLayer = CCVideoLayerImpl::create(4, &provider);
+ videoLayer->setBounds(IntSize(10, 10));
+ videoLayer->setAnchorPoint(FloatPoint(0, 0));
+ videoLayer->setContentBounds(IntSize(10, 10));
+ videoLayer->setDrawsContent(true);
+ videoLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(videoLayer.release());
+
+ OwnPtr<CCIOSurfaceLayerImpl> ioSurfaceLayer = CCIOSurfaceLayerImpl::create(5);
+ ioSurfaceLayer->setBounds(IntSize(10, 10));
+ ioSurfaceLayer->setAnchorPoint(FloatPoint(0, 0));
+ ioSurfaceLayer->setContentBounds(IntSize(10, 10));
+ ioSurfaceLayer->setDrawsContent(true);
+ ioSurfaceLayer->setIOSurfaceProperties(1, IntSize(10, 10));
+ ioSurfaceLayer->setLayerTreeHostImpl(m_hostImpl.get());
+ rootLayer->addChild(ioSurfaceLayer.release());
+
+ // Lose the context, replacing it with a TrackingWebGraphicsContext3D (which the CCLayerTreeHostImpl takes ownership of).
+ OwnPtr<CCGraphicsContext> ccContext(FakeWebCompositorOutputSurface::create(adoptPtr(new TrackingWebGraphicsContext3D)));
+ TrackingWebGraphicsContext3D* trackingWebGraphicsContext = static_cast<TrackingWebGraphicsContext3D*>(ccContext->context3D());
+ m_hostImpl->initializeRenderer(ccContext.release(), UnthrottledUploader);
+
+ m_hostImpl->setRootLayer(rootLayer.release());
+
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(m_hostImpl->prepareToDraw(frame));
+ m_hostImpl->drawLayers(frame);
+ m_hostImpl->didDrawAllLayers(frame);
+ m_hostImpl->swapBuffers();
+
+ EXPECT_GT(trackingWebGraphicsContext->numTextures(), 0u);
+
+ // Kill the layer tree.
+ m_hostImpl->setRootLayer(CCLayerImpl::create(100));
+ // There should be no textures left in use after.
+ EXPECT_EQ(0u, trackingWebGraphicsContext->numTextures());
+}
+
+class MockDrawQuadsToFillScreenContext : public FakeWebGraphicsContext3D {
+public:
+ MOCK_METHOD1(useProgram, void(WebGLId program));
+ MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset));
+};
+
+TEST_F(CCLayerTreeHostImplTest, hasTransparentBackground)
+{
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new MockDrawQuadsToFillScreenContext));
+ MockDrawQuadsToFillScreenContext* mockContext = static_cast<MockDrawQuadsToFillScreenContext*>(context->context3D());
+
+ // Run test case
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(false, context.release(), CCLayerImpl::create(1));
+ myHostImpl->setBackgroundColor(SK_ColorWHITE);
+
+ // Verify one quad is drawn when transparent background set is not set.
+ myHostImpl->setHasTransparentBackground(false);
+ EXPECT_CALL(*mockContext, useProgram(_))
+ .Times(1);
+ EXPECT_CALL(*mockContext, drawElements(_, _, _, _))
+ .Times(1);
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ Mock::VerifyAndClearExpectations(&mockContext);
+
+ // Verify no quads are drawn when transparent background is set.
+ myHostImpl->setHasTransparentBackground(true);
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ Mock::VerifyAndClearExpectations(&mockContext);
+}
+
+static void addDrawingLayerTo(CCLayerImpl* parent, int id, const IntRect& layerRect, CCLayerImpl** result)
+{
+ OwnPtr<CCLayerImpl> layer = FakeLayerWithQuads::create(id);
+ CCLayerImpl* layerPtr = layer.get();
+ layerPtr->setAnchorPoint(FloatPoint(0, 0));
+ layerPtr->setPosition(FloatPoint(layerRect.location()));
+ layerPtr->setBounds(layerRect.size());
+ layerPtr->setContentBounds(layerRect.size());
+ layerPtr->setDrawsContent(true); // only children draw content
+ layerPtr->setOpaque(true);
+ parent->addChild(layer.release());
+ if (result)
+ *result = layerPtr;
+}
+
+static void setupLayersForTextureCaching(CCLayerTreeHostImpl* layerTreeHostImpl, CCLayerImpl*& rootPtr, CCLayerImpl*& intermediateLayerPtr, CCLayerImpl*& surfaceLayerPtr, CCLayerImpl*& childPtr, const IntSize& rootSize)
+{
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ layerTreeHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ layerTreeHostImpl->setViewportSize(rootSize, rootSize);
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ layerTreeHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(10, 10, rootSize.width(), rootSize.height()), &intermediateLayerPtr);
+ intermediateLayerPtr->setDrawsContent(false); // only children draw content
+
+ // Surface layer is the layer that changes its opacity
+ // It will contain other layers that draw content.
+ addDrawingLayerTo(intermediateLayerPtr, 3, IntRect(10, 10, rootSize.width(), rootSize.height()), &surfaceLayerPtr);
+ surfaceLayerPtr->setDrawsContent(false); // only children draw content
+ surfaceLayerPtr->setOpacity(0.5f); // This will cause it to have a surface
+
+ // Child of the surface layer will produce some quads
+ addDrawingLayerTo(surfaceLayerPtr, 4, IntRect(5, 5, rootSize.width() - 25, rootSize.height() - 25), &childPtr);
+}
+
+class CCRendererGLWithReleaseTextures : public CCRendererGL {
+public:
+ using CCRendererGL::releaseRenderPassTextures;
+};
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithClipping)
+{
+ CCSettings::setPartialSwapEnabled(true);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* surfaceLayerPtr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(100, 100);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 3, IntRect(0, 0, rootSize.width(), rootSize.height()), &surfaceLayerPtr);
+ surfaceLayerPtr->setDrawsContent(false);
+
+ // Surface layer is the layer that changes its opacity
+ // It will contain other layers that draw content.
+ surfaceLayerPtr->setOpacity(0.5f); // This will cause it to have a surface
+
+ addDrawingLayerTo(surfaceLayerPtr, 4, IntRect(0, 0, 100, 3), 0);
+ addDrawingLayerTo(surfaceLayerPtr, 5, IntRect(0, 97, 100, 3), 0);
+
+ // Rotation will put part of the child ouside the bounds of the root layer.
+ // Nevertheless, the child layers should be drawn.
+ WebTransformationMatrix transform = surfaceLayerPtr->transform();
+ transform.translate(50, 50);
+ transform.rotate(35);
+ transform.translate(-50, -50);
+ surfaceLayerPtr->setTransform(transform);
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ // Verify that the child layers are being clipped.
+ IntRect quadVisibleRect = frame.renderPasses[0]->quadList()[0]->quadVisibleRect();
+ EXPECT_LT(quadVisibleRect.width(), 100);
+
+ quadVisibleRect = frame.renderPasses[0]->quadList()[1]->quadVisibleRect();
+ EXPECT_LT(quadVisibleRect.width(), 100);
+
+ // Verify that the render surface texture is *not* clipped.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), frame.renderPasses[0]->outputRect());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ EXPECT_FALSE(quad->contentsChangedSinceLastFrame().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ transform = surfaceLayerPtr->transform();
+ transform.translate(50, 50);
+ transform.rotate(-35);
+ transform.translate(-50, -50);
+ surfaceLayerPtr->setTransform(transform);
+
+ // The surface is now aligned again, and the clipped parts are exposed.
+ // Since the layers were clipped, even though the render surface size
+ // was not changed, the texture should not be saved.
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithOcclusion)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Layers are structure as follows:
+ //
+ // R +-- S1 +- L10 (owning)
+ // | +- L11
+ // | +- L12
+ // |
+ // +-- S2 +- L20 (owning)
+ // +- L21
+ //
+ // Occlusion:
+ // L12 occludes L11 (internal)
+ // L20 occludes L10 (external)
+ // L21 occludes L20 (internal)
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* layerS1Ptr;
+ CCLayerImpl* layerS2Ptr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(1000, 1000);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(300, 300, 300, 300), &layerS1Ptr);
+ layerS1Ptr->setForceRenderSurface(true);
+
+ addDrawingLayerTo(layerS1Ptr, 3, IntRect(10, 10, 10, 10), 0); // L11
+ addDrawingLayerTo(layerS1Ptr, 4, IntRect(0, 0, 30, 30), 0); // L12
+
+ addDrawingLayerTo(rootPtr, 5, IntRect(550, 250, 300, 400), &layerS2Ptr);
+ layerS2Ptr->setForceRenderSurface(true);
+
+ addDrawingLayerTo(layerS2Ptr, 6, IntRect(20, 20, 5, 5), 0); // L21
+
+ // Initial draw - must receive all quads
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 3 render passes.
+ // For Root, there are 2 quads; for S1, there are 2 quads (1 is occluded); for S2, there is 2 quads.
+ ASSERT_EQ(3U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[2]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Unocclude" surface S1 and repeat draw.
+ // Must remove S2's render pass since it's cached;
+ // Must keep S1 quads because texture contained external occlusion.
+ WebTransformationMatrix transform = layerS2Ptr->transform();
+ transform.translate(150, 150);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 2 render passes.
+ // For Root, there are 2 quads
+ // For S1, the number of quads depends on what got unoccluded, so not asserted beyond being positive.
+ // For S2, there is no render pass
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_GT(frame.renderPasses[0]->quadList().size(), 0U);
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Re-occlude" surface S1 and repeat draw.
+ // Must remove S1's render pass since it is now available in full.
+ // S2 has no change so must also be removed.
+ transform = layerS2Ptr->transform();
+ transform.translate(-15, -15);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 1 render pass - for the root.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithOcclusionEarlyOut)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Layers are structure as follows:
+ //
+ // R +-- S1 +- L10 (owning, non drawing)
+ // | +- L11 (corner, unoccluded)
+ // | +- L12 (corner, unoccluded)
+ // | +- L13 (corner, unoccluded)
+ // | +- L14 (corner, entirely occluded)
+ // |
+ // +-- S2 +- L20 (owning, drawing)
+ //
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* layerS1Ptr;
+ CCLayerImpl* layerS2Ptr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(1000, 1000);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(0, 0, 800, 800), &layerS1Ptr);
+ layerS1Ptr->setForceRenderSurface(true);
+ layerS1Ptr->setDrawsContent(false);
+
+ addDrawingLayerTo(layerS1Ptr, 3, IntRect(0, 0, 300, 300), 0); // L11
+ addDrawingLayerTo(layerS1Ptr, 4, IntRect(0, 500, 300, 300), 0); // L12
+ addDrawingLayerTo(layerS1Ptr, 5, IntRect(500, 0, 300, 300), 0); // L13
+ addDrawingLayerTo(layerS1Ptr, 6, IntRect(500, 500, 300, 300), 0); // L14
+ addDrawingLayerTo(layerS1Ptr, 9, IntRect(500, 500, 300, 300), 0); // L14
+
+ addDrawingLayerTo(rootPtr, 7, IntRect(450, 450, 450, 450), &layerS2Ptr);
+ layerS2Ptr->setForceRenderSurface(true);
+
+ // Initial draw - must receive all quads
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 3 render passes.
+ // For Root, there are 2 quads; for S1, there are 3 quads; for S2, there is 1 quad.
+ ASSERT_EQ(3U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ // L14 is culled, so only 3 quads.
+ EXPECT_EQ(3U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[2]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Unocclude" surface S1 and repeat draw.
+ // Must remove S2's render pass since it's cached;
+ // Must keep S1 quads because texture contained external occlusion.
+ WebTransformationMatrix transform = layerS2Ptr->transform();
+ transform.translate(100, 100);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 2 render passes.
+ // For Root, there are 2 quads
+ // For S1, the number of quads depends on what got unoccluded, so not asserted beyond being positive.
+ // For S2, there is no render pass
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_GT(frame.renderPasses[0]->quadList().size(), 0U);
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Re-occlude" surface S1 and repeat draw.
+ // Must remove S1's render pass since it is now available in full.
+ // S2 has no change so must also be removed.
+ transform = layerS2Ptr->transform();
+ transform.translate(-15, -15);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 1 render pass - for the root.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithOcclusionExternalOverInternal)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Layers are structured as follows:
+ //
+ // R +-- S1 +- L10 (owning, drawing)
+ // | +- L11 (corner, occluded by L12)
+ // | +- L12 (opposite corner)
+ // |
+ // +-- S2 +- L20 (owning, drawing)
+ //
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* layerS1Ptr;
+ CCLayerImpl* layerS2Ptr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(1000, 1000);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(0, 0, 400, 400), &layerS1Ptr);
+ layerS1Ptr->setForceRenderSurface(true);
+
+ addDrawingLayerTo(layerS1Ptr, 3, IntRect(0, 0, 300, 300), 0); // L11
+ addDrawingLayerTo(layerS1Ptr, 4, IntRect(100, 0, 300, 300), 0); // L12
+
+ addDrawingLayerTo(rootPtr, 7, IntRect(200, 0, 300, 300), &layerS2Ptr);
+ layerS2Ptr->setForceRenderSurface(true);
+
+ // Initial draw - must receive all quads
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 3 render passes.
+ // For Root, there are 2 quads; for S1, there are 3 quads; for S2, there is 1 quad.
+ ASSERT_EQ(3U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(3U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[2]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Unocclude" surface S1 and repeat draw.
+ // Must remove S2's render pass since it's cached;
+ // Must keep S1 quads because texture contained external occlusion.
+ WebTransformationMatrix transform = layerS2Ptr->transform();
+ transform.translate(300, 0);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 2 render passes.
+ // For Root, there are 2 quads
+ // For S1, the number of quads depends on what got unoccluded, so not asserted beyond being positive.
+ // For S2, there is no render pass
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_GT(frame.renderPasses[0]->quadList().size(), 0U);
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithOcclusionExternalNotAligned)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Layers are structured as follows:
+ //
+ // R +-- S1 +- L10 (rotated, drawing)
+ // +- L11 (occupies half surface)
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* layerS1Ptr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(1000, 1000);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(0, 0, 400, 400), &layerS1Ptr);
+ layerS1Ptr->setForceRenderSurface(true);
+ WebTransformationMatrix transform = layerS1Ptr->transform();
+ transform.translate(200, 200);
+ transform.rotate(45);
+ transform.translate(-200, -200);
+ layerS1Ptr->setTransform(transform);
+
+ addDrawingLayerTo(layerS1Ptr, 3, IntRect(200, 0, 200, 400), 0); // L11
+
+ // Initial draw - must receive all quads
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 2 render passes.
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity and draw. Verify we used cached texture.
+ layerS1Ptr->setOpacity(0.2f);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // One render pass must be gone due to cached texture.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithOcclusionPartialSwap)
+{
+ CCSettings::setPartialSwapEnabled(true);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ // Layers are structure as follows:
+ //
+ // R +-- S1 +- L10 (owning)
+ // | +- L11
+ // | +- L12
+ // |
+ // +-- S2 +- L20 (owning)
+ // +- L21
+ //
+ // Occlusion:
+ // L12 occludes L11 (internal)
+ // L20 occludes L10 (external)
+ // L21 occludes L20 (internal)
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* layerS1Ptr;
+ CCLayerImpl* layerS2Ptr;
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+
+ IntSize rootSize(1000, 1000);
+
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ myHostImpl->setViewportSize(IntSize(rootSize.width(), rootSize.height()), IntSize(rootSize.width(), rootSize.height()));
+
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ rootPtr = root.get();
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(0, 0));
+ root->setBounds(rootSize);
+ root->setContentBounds(rootSize);
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+ myHostImpl->setRootLayer(root.release());
+
+ addDrawingLayerTo(rootPtr, 2, IntRect(300, 300, 300, 300), &layerS1Ptr);
+ layerS1Ptr->setForceRenderSurface(true);
+
+ addDrawingLayerTo(layerS1Ptr, 3, IntRect(10, 10, 10, 10), 0); // L11
+ addDrawingLayerTo(layerS1Ptr, 4, IntRect(0, 0, 30, 30), 0); // L12
+
+ addDrawingLayerTo(rootPtr, 5, IntRect(550, 250, 300, 400), &layerS2Ptr);
+ layerS2Ptr->setForceRenderSurface(true);
+
+ addDrawingLayerTo(layerS2Ptr, 6, IntRect(20, 20, 5, 5), 0); // L21
+
+ // Initial draw - must receive all quads
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 3 render passes.
+ // For Root, there are 2 quads; for S1, there are 2 quads (one is occluded); for S2, there is 2 quads.
+ ASSERT_EQ(3U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[2]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Unocclude" surface S1 and repeat draw.
+ // Must remove S2's render pass since it's cached;
+ // Must keep S1 quads because texture contained external occlusion.
+ WebTransformationMatrix transform = layerS2Ptr->transform();
+ transform.translate(150, 150);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive 2 render passes.
+ // For Root, there are 2 quads.
+ // For S1, there are 2 quads.
+ // For S2, there is no render pass
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_EQ(2U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(2U, frame.renderPasses[1]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // "Re-occlude" surface S1 and repeat draw.
+ // Must remove S1's render pass since it is now available in full.
+ // S2 has no change so must also be removed.
+ transform = layerS2Ptr->transform();
+ transform.translate(-15, -15);
+ layerS2Ptr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Root render pass only.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, textureCachingWithScissor)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ /*
+ Layers are created as follows:
+
+ +--------------------+
+ | 1 |
+ | +-----------+ |
+ | | 2 | |
+ | | +-------------------+
+ | | | 3 |
+ | | +-------------------+
+ | | | |
+ | +-----------+ |
+ | |
+ | |
+ +--------------------+
+
+ Layers 1, 2 have render surfaces
+ */
+ OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1);
+ OwnPtr<CCTiledLayerImpl> child = CCTiledLayerImpl::create(2);
+ OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3);
+
+ IntRect rootRect(0, 0, 100, 100);
+ IntRect childRect(10, 10, 50, 50);
+ IntRect grandChildRect(5, 5, 150, 150);
+
+ OwnPtr<CCGraphicsContext> context = FakeWebCompositorOutputSurface::create(adoptPtr(new PartialSwapContext));
+ myHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+
+ root->setAnchorPoint(FloatPoint(0, 0));
+ root->setPosition(FloatPoint(rootRect.x(), rootRect.y()));
+ root->setBounds(IntSize(rootRect.width(), rootRect.height()));
+ root->setContentBounds(root->bounds());
+ root->setDrawsContent(true);
+ root->setMasksToBounds(true);
+
+ child->setAnchorPoint(FloatPoint(0, 0));
+ child->setPosition(FloatPoint(childRect.x(), childRect.y()));
+ child->setOpacity(0.5);
+ child->setBounds(IntSize(childRect.width(), childRect.height()));
+ child->setContentBounds(child->bounds());
+ child->setDrawsContent(true);
+ child->setSkipsDraw(false);
+
+ // child layer has 10x10 tiles.
+ OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::HasBorderTexels);
+ tiler->setBounds(child->contentBounds());
+ child->setTilingData(*tiler.get());
+
+ grandChild->setAnchorPoint(FloatPoint(0, 0));
+ grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y()));
+ grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height()));
+ grandChild->setContentBounds(grandChild->bounds());
+ grandChild->setDrawsContent(true);
+
+ CCTiledLayerImpl* childPtr = child.get();
+
+ child->addChild(grandChild.release());
+ root->addChild(child.release());
+ myHostImpl->setRootLayer(root.release());
+ myHostImpl->setViewportSize(rootRect.size(), rootRect.size());
+
+ EXPECT_FALSE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(childPtr->id()));
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // We should have cached textures for surface 2.
+ EXPECT_TRUE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(childPtr->id()));
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // We should still have cached textures for surface 2 after drawing with no damage.
+ EXPECT_TRUE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(childPtr->id()));
+
+ // Damage a single tile of surface 2.
+ childPtr->setUpdateRect(IntRect(10, 10, 10, 10));
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // We should have a cached texture for surface 2 again even though it was damaged.
+ EXPECT_TRUE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(childPtr->id()));
+}
+
+TEST_F(CCLayerTreeHostImplTest, surfaceTextureCaching)
+{
+ CCSettings::setPartialSwapEnabled(true);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* intermediateLayerPtr;
+ CCLayerImpl* surfaceLayerPtr;
+ CCLayerImpl* childPtr;
+
+ setupLayersForTextureCaching(myHostImpl.get(), rootPtr, intermediateLayerPtr, surfaceLayerPtr, childPtr, IntSize(100, 100));
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_FALSE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Draw without any change
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity and draw
+ surfaceLayerPtr->setOpacity(0.6f);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change less benign property and draw - should have contents changed flag
+ surfaceLayerPtr->setStackingOrderChanged(true);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_FALSE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity again, and evict the cached surface texture.
+ surfaceLayerPtr->setOpacity(0.5f);
+ static_cast<CCRendererGLWithReleaseTextures*>(myHostImpl->renderer())->releaseRenderPassTextures();
+
+ // Change opacity and draw
+ surfaceLayerPtr->setOpacity(0.6f);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ // Even though not enough properties changed, the entire thing must be
+ // redrawn as we don't have cached textures
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ // Was our surface evicted?
+ EXPECT_FALSE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(targetPass->id()));
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Draw without any change, to make sure the state is clear
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity on the intermediate layer
+ WebTransformationMatrix transform = intermediateLayerPtr->transform();
+ transform.setM11(1.0001);
+ intermediateLayerPtr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, surfaceTextureCachingNoPartialSwap)
+{
+ CCSettings::setPartialSwapEnabled(false);
+
+ CCLayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = IntSize();
+ OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this);
+
+ CCLayerImpl* rootPtr;
+ CCLayerImpl* intermediateLayerPtr;
+ CCLayerImpl* surfaceLayerPtr;
+ CCLayerImpl* childPtr;
+
+ setupLayersForTextureCaching(myHostImpl.get(), rootPtr, intermediateLayerPtr, surfaceLayerPtr, childPtr, IntSize(100, 100));
+
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_FALSE(targetPass->damageRect().isEmpty());
+
+ EXPECT_FALSE(frame.renderPasses[0]->damageRect().isEmpty());
+ EXPECT_FALSE(frame.renderPasses[1]->damageRect().isEmpty());
+
+ EXPECT_FALSE(frame.renderPasses[0]->hasOcclusionFromOutsideTargetSurface());
+ EXPECT_FALSE(frame.renderPasses[1]->hasOcclusionFromOutsideTargetSurface());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Draw without any change
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Even though there was no change, we set the damage to entire viewport.
+ // One of the passes should be culled as a result, since contents didn't change
+ // and we have cached texture.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ EXPECT_TRUE(frame.renderPasses[0]->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity and draw
+ surfaceLayerPtr->setOpacity(0.6f);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled
+ ASSERT_EQ(1U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change less benign property and draw - should have contents changed flag
+ surfaceLayerPtr->setStackingOrderChanged(true);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes, each with one quad
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_FALSE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity again, and evict the cached surface texture.
+ surfaceLayerPtr->setOpacity(0.5f);
+ static_cast<CCRendererGLWithReleaseTextures*>(myHostImpl->renderer())->releaseRenderPassTextures();
+
+ // Change opacity and draw
+ surfaceLayerPtr->setOpacity(0.6f);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive two render passes
+ ASSERT_EQ(2U, frame.renderPasses.size());
+
+ // Even though not enough properties changed, the entire thing must be
+ // redrawn as we don't have cached textures
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+ EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ // Was our surface evicted?
+ EXPECT_FALSE(myHostImpl->renderer()->haveCachedResourcesForRenderPassId(targetPass->id()));
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Draw without any change, to make sure the state is clear
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Even though there was no change, we set the damage to entire viewport.
+ // One of the passes should be culled as a result, since contents didn't change
+ // and we have cached texture.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+
+ // Change opacity on the intermediate layer
+ WebTransformationMatrix transform = intermediateLayerPtr->transform();
+ transform.setM11(1.0001);
+ intermediateLayerPtr->setTransform(transform);
+ {
+ CCLayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+
+ // Must receive one render pass, as the other one should be culled.
+ ASSERT_EQ(1U, frame.renderPasses.size());
+ EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size());
+
+ EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material());
+ CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get());
+ CCRenderPass* targetPass = frame.renderPassesById.get(quad->renderPassId());
+ EXPECT_TRUE(targetPass->damageRect().isEmpty());
+
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+}
+
+TEST_F(CCLayerTreeHostImplTest, releaseContentsTextureShouldTriggerCommit)
+{
+ m_hostImpl->releaseContentsTextures();
+ EXPECT_TRUE(m_didRequestCommit);
+}
+
+struct RenderPassCacheEntry {
+ mutable OwnPtr<CCRenderPass> renderPassPtr;
+ CCRenderPass* renderPass;
+
+ RenderPassCacheEntry(PassOwnPtr<CCRenderPass> r)
+ : renderPassPtr(r),
+ renderPass(renderPassPtr.get())
+ {
+ }
+
+ RenderPassCacheEntry()
+ {
+ }
+
+ RenderPassCacheEntry(const RenderPassCacheEntry& entry)
+ : renderPassPtr(entry.renderPassPtr.release()),
+ renderPass(entry.renderPass)
+ {
+ }
+
+ RenderPassCacheEntry& operator=(const RenderPassCacheEntry& entry)
+ {
+ renderPassPtr = entry.renderPassPtr.release();
+ renderPass = entry.renderPass;
+ return *this;
+ }
+};
+
+struct RenderPassRemovalTestData : public CCLayerTreeHostImpl::FrameData {
+ std::map<char, RenderPassCacheEntry> renderPassCache;
+ OwnPtr<CCSharedQuadState> sharedQuadState;
+};
+
+class CCTestRenderPass: public CCRenderPass {
+public:
+ static PassOwnPtr<CCRenderPass> create(int id, IntRect outputRect, const WebTransformationMatrix& rootTransform) { return adoptPtr(new CCTestRenderPass(id, outputRect, rootTransform)); }
+
+ void appendQuad(PassOwnPtr<CCDrawQuad> quad) { m_quadList.append(quad); }
+
+protected:
+ CCTestRenderPass(int id, IntRect outputRect, const WebTransformationMatrix& rootTransform) : CCRenderPass(id, outputRect, rootTransform) { }
+};
+
+class CCTestRenderer : public CCRendererGL, public CCRendererClient {
+public:
+ static PassOwnPtr<CCTestRenderer> create(CCResourceProvider* resourceProvider)
+ {
+ OwnPtr<CCTestRenderer> renderer(adoptPtr(new CCTestRenderer(resourceProvider)));
+ if (!renderer->initialize())
+ return nullptr;
+
+ return renderer.release();
+ }
+
+ void clearCachedTextures() { m_textures.clear(); }
+ void setHaveCachedResourcesForRenderPassId(int id) { m_textures.add(id); }
+
+ virtual bool haveCachedResourcesForRenderPassId(int id) const OVERRIDE { return m_textures.contains(id); }
+
+ // CCRendererClient implementation.
+ virtual const IntSize& deviceViewportSize() const OVERRIDE { return m_viewportSize; }
+ virtual const CCLayerTreeSettings& settings() const OVERRIDE { return m_settings; }
+ virtual void didLoseContext() OVERRIDE { }
+ virtual void onSwapBuffersComplete() OVERRIDE { }
+ virtual void setFullRootLayerDamage() OVERRIDE { }
+ virtual void releaseContentsTextures() OVERRIDE { }
+ virtual void setMemoryAllocationLimitBytes(size_t) OVERRIDE { }
+
+protected:
+ CCTestRenderer(CCResourceProvider* resourceProvider) : CCRendererGL(this, resourceProvider, UnthrottledUploader) { }
+
+private:
+ CCLayerTreeSettings m_settings;
+ IntSize m_viewportSize;
+ HashSet<int> m_textures;
+};
+
+static void configureRenderPassTestData(const char* testScript, RenderPassRemovalTestData& testData, CCTestRenderer* renderer)
+{
+ renderer->clearCachedTextures();
+
+ // One shared state for all quads - we don't need the correct details
+ testData.sharedQuadState = CCSharedQuadState::create(WebTransformationMatrix(), IntRect(), IntRect(), 1.0, true);
+
+ const char* currentChar = testScript;
+
+ // Pre-create root pass
+ char rootRenderPassId = testScript[0];
+ OwnPtr<CCRenderPass> rootRenderPass = CCTestRenderPass::create(rootRenderPassId, IntRect(), WebTransformationMatrix());
+ testData.renderPassCache.insert(std::pair<char, RenderPassCacheEntry>(rootRenderPassId, RenderPassCacheEntry(rootRenderPass.release())));
+ while (*currentChar) {
+ char renderPassId = currentChar[0];
+ currentChar++;
+
+ OwnPtr<CCRenderPass> renderPass;
+
+ bool isReplica = false;
+ if (!testData.renderPassCache[renderPassId].renderPassPtr.get())
+ isReplica = true;
+
+ renderPass = testData.renderPassCache[renderPassId].renderPassPtr.release();
+
+ // Cycle through quad data and create all quads
+ while (*currentChar && *currentChar != '\n') {
+ if (*currentChar == 's') {
+ // Solid color draw quad
+ OwnPtr<CCDrawQuad> quad = CCSolidColorDrawQuad::create(testData.sharedQuadState.get(), IntRect(0, 0, 10, 10), SK_ColorWHITE);
+
+ static_cast<CCTestRenderPass*>(renderPass.get())->appendQuad(quad.release());
+ currentChar++;
+ } else if ((*currentChar >= 'A') && (*currentChar <= 'Z')) {
+ // RenderPass draw quad
+ char newRenderPassId = *currentChar;
+ ASSERT_NE(rootRenderPassId, newRenderPassId);
+ currentChar++;
+ bool hasTexture = false;
+ bool contentsChanged = true;
+
+ if (*currentChar == '[') {
+ currentChar++;
+ while (*currentChar && *currentChar != ']') {
+ switch (*currentChar) {
+ case 'c':
+ contentsChanged = false;
+ break;
+ case 't':
+ hasTexture = true;
+ break;
+ }
+ currentChar++;
+ }
+ if (*currentChar == ']')
+ currentChar++;
+ }
+
+ if (testData.renderPassCache.find(newRenderPassId) == testData.renderPassCache.end()) {
+ if (hasTexture)
+ renderer->setHaveCachedResourcesForRenderPassId(newRenderPassId);
+
+ OwnPtr<CCRenderPass> renderPass = CCTestRenderPass::create(newRenderPassId, IntRect(), WebTransformationMatrix());
+ testData.renderPassCache.insert(std::pair<char, RenderPassCacheEntry>(newRenderPassId, RenderPassCacheEntry(renderPass.release())));
+ }
+
+ IntRect quadRect = IntRect(0, 0, 1, 1);
+ IntRect contentsChangedRect = contentsChanged ? quadRect : IntRect();
+ OwnPtr<CCRenderPassDrawQuad> quad = CCRenderPassDrawQuad::create(testData.sharedQuadState.get(), quadRect, newRenderPassId, isReplica, 1, contentsChangedRect, 1, 1, 0, 0);
+ static_cast<CCTestRenderPass*>(renderPass.get())->appendQuad(quad.release());
+ }
+ }
+ testData.renderPasses.insert(0, renderPass.get());
+ testData.renderPassesById.add(renderPassId, renderPass.release());
+ if (*currentChar)
+ currentChar++;
+ }
+}
+
+void dumpRenderPassTestData(const RenderPassRemovalTestData& testData, char* buffer)
+{
+ char* pos = buffer;
+ for (CCRenderPassList::const_reverse_iterator it = testData.renderPasses.rbegin(); it != testData.renderPasses.rend(); ++it) {
+ const CCRenderPass* currentPass = *it;
+ *pos = currentPass->id();
+ pos++;
+
+ CCQuadList::const_iterator quadListIterator = currentPass->quadList().begin();
+ while (quadListIterator != currentPass->quadList().end()) {
+ CCDrawQuad* currentQuad = (*quadListIterator).get();
+ switch (currentQuad->material()) {
+ case CCDrawQuad::SolidColor:
+ *pos = 's';
+ pos++;
+ break;
+ case CCDrawQuad::RenderPass:
+ *pos = CCRenderPassDrawQuad::materialCast(currentQuad)->renderPassId();
+ pos++;
+ break;
+ default:
+ *pos = 'x';
+ pos++;
+ break;
+ }
+
+ quadListIterator++;
+ }
+ *pos = '\n';
+ pos++;
+ }
+ *pos = '\0';
+}
+
+// Each CCRenderPassList is represented by a string which describes the configuration.
+// The syntax of the string is as follows:
+//
+// RsssssX[c]ssYsssZ[t]ssW[ct]
+// Identifies the render pass---------------------------^ ^^^ ^ ^ ^ ^ ^
+// These are solid color quads-----------------------------+ | | | | |
+// Identifies RenderPassDrawQuad's RenderPass-----------------+ | | | |
+// This quad's contents didn't change---------------------------+ | | |
+// This quad's contents changed and it has no texture---------------+ | |
+// This quad has texture but its contents changed-------------------------+ |
+// This quad's contents didn't change and it has texture - will be removed------+
+//
+// Expected results have exactly the same syntax, except they do not use square brackets,
+// since we only check the structure, not attributes.
+//
+// Test case configuration consists of initialization script and expected results,
+// all in the same format.
+struct TestCase {
+ const char* name;
+ const char* initScript;
+ const char* expectedResult;
+};
+
+TestCase removeRenderPassesCases[] =
+ {
+ {
+ "Single root pass",
+ "Rssss\n",
+ "Rssss\n"
+ }, {
+ "Single pass - no quads",
+ "R\n",
+ "R\n"
+ }, {
+ "Two passes, no removal",
+ "RssssAsss\n"
+ "Assss\n",
+ "RssssAsss\n"
+ "Assss\n"
+ }, {
+ "Two passes, remove last",
+ "RssssA[ct]sss\n"
+ "Assss\n",
+ "RssssAsss\n"
+ }, {
+ "Have texture but contents changed - leave pass",
+ "RssssA[t]sss\n"
+ "Assss\n",
+ "RssssAsss\n"
+ "Assss\n"
+ }, {
+ "Contents didn't change but no texture - leave pass",
+ "RssssA[c]sss\n"
+ "Assss\n",
+ "RssssAsss\n"
+ "Assss\n"
+ }, {
+ "Replica: two quads reference the same pass; remove",
+ "RssssA[ct]A[ct]sss\n"
+ "Assss\n",
+ "RssssAAsss\n"
+ }, {
+ "Replica: two quads reference the same pass; leave",
+ "RssssA[c]A[c]sss\n"
+ "Assss\n",
+ "RssssAAsss\n"
+ "Assss\n",
+ }, {
+ "Many passes, remove all",
+ "RssssA[ct]sss\n"
+ "AsssB[ct]C[ct]s\n"
+ "BsssD[ct]ssE[ct]F[ct]\n"
+ "Essssss\n"
+ "CG[ct]\n"
+ "Dsssssss\n"
+ "Fsssssss\n"
+ "Gsss\n",
+
+ "RssssAsss\n"
+ }, {
+ "Deep recursion, remove all",
+
+ "RsssssA[ct]ssss\n"
+ "AssssBsss\n"
+ "BC\n"
+ "CD\n"
+ "DE\n"
+ "EF\n"
+ "FG\n"
+ "GH\n"
+ "HsssIsss\n"
+ "IJ\n"
+ "Jssss\n",
+
+ "RsssssAssss\n"
+ }, {
+ "Wide recursion, remove all",
+ "RA[ct]B[ct]C[ct]D[ct]E[ct]F[ct]G[ct]H[ct]I[ct]J[ct]\n"
+ "As\n"
+ "Bs\n"
+ "Cssss\n"
+ "Dssss\n"
+ "Es\n"
+ "F\n"
+ "Gs\n"
+ "Hs\n"
+ "Is\n"
+ "Jssss\n",
+
+ "RABCDEFGHIJ\n"
+ }, {
+ "Remove passes regardless of cache state",
+ "RssssA[ct]sss\n"
+ "AsssBCs\n"
+ "BsssD[c]ssE[t]F\n"
+ "Essssss\n"
+ "CG\n"
+ "Dsssssss\n"
+ "Fsssssss\n"
+ "Gsss\n",
+
+ "RssssAsss\n"
+ }, {
+ "Leave some passes, remove others",
+
+ "RssssA[c]sss\n"
+ "AsssB[t]C[ct]s\n"
+ "BsssD[c]ss\n"
+ "CG\n"
+ "Dsssssss\n"
+ "Gsss\n",
+
+ "RssssAsss\n"
+ "AsssBCs\n"
+ "BsssDss\n"
+ "Dsssssss\n"
+ }, {
+ 0, 0, 0
+ }
+ };
+
+static void verifyRenderPassTestData(TestCase& testCase, RenderPassRemovalTestData& testData)
+{
+ char actualResult[1024];
+ dumpRenderPassTestData(testData, actualResult);
+ EXPECT_STREQ(testCase.expectedResult, actualResult) << "In test case: " << testCase.name;
+}
+
+TEST_F(CCLayerTreeHostImplTest, testRemoveRenderPasses)
+{
+ OwnPtr<CCGraphicsContext> context(createContext());
+ ASSERT_TRUE(context->context3D());
+ OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get()));
+
+ OwnPtr<CCTestRenderer> renderer(CCTestRenderer::create(resourceProvider.get()));
+
+ int testCaseIndex = 0;
+ while (removeRenderPassesCases[testCaseIndex].name) {
+ RenderPassRemovalTestData testData;
+ configureRenderPassTestData(removeRenderPassesCases[testCaseIndex].initScript, testData, renderer.get());
+ CCLayerTreeHostImpl::removeRenderPasses(CCLayerTreeHostImpl::CullRenderPassesWithCachedTextures(*renderer), testData);
+ verifyRenderPassTestData(removeRenderPassesCases[testCaseIndex], testData);
+ testCaseIndex++;
+ }
+}
+
+} // namespace
diff --git a/cc/CCLayerTreeHostTest.cpp b/cc/CCLayerTreeHostTest.cpp
new file mode 100644
index 0000000..820088f
--- /dev/null
+++ b/cc/CCLayerTreeHostTest.cpp
@@ -0,0 +1,2671 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTreeHost.h"
+
+#include "CCGraphicsContext.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCOcclusionTrackerTestCommon.h"
+#include "CCSettings.h"
+#include "CCSingleThreadProxy.h"
+#include "CCTextureUpdateQueue.h"
+#include "CCThreadedTest.h"
+#include "CCTimingFunction.h"
+#include "ContentLayerChromium.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/Platform.h>
+#include <public/WebThread.h>
+#include <wtf/MainThread.h>
+#include <wtf/OwnArrayPtr.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WebKitTests;
+using namespace WTF;
+
+#define EXPECT_EQ_RECT(a, b) \
+ EXPECT_EQ(a.x(), b.x()); \
+ EXPECT_EQ(a.y(), b.y()); \
+ EXPECT_EQ(a.width(), b.width()); \
+ EXPECT_EQ(a.height(), b.height());
+
+namespace {
+
+class CCLayerTreeHostTest : public CCThreadedTest { };
+
+// Shortlived layerTreeHosts shouldn't die.
+class CCLayerTreeHostTestShortlived1 : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestShortlived1() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+// Shortlived layerTreeHosts shouldn't die with a commit in flight.
+class CCLayerTreeHostTestShortlived2 : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestShortlived2() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived2)
+
+// Shortlived layerTreeHosts shouldn't die with a redraw in flight.
+class CCLayerTreeHostTestShortlived3 : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestShortlived3() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsRedrawToMainThread();
+
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived3)
+
+// Test interleaving of redraws and commits
+class CCLayerTreeHostTestCommitingWithContinuousRedraw : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestCommitingWithContinuousRedraw()
+ : m_numCompleteCommits(0)
+ , m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ m_numCompleteCommits++;
+ if (m_numCompleteCommits == 2)
+ endTest();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ if (m_numDraws == 1)
+ postSetNeedsCommitToMainThread();
+ m_numDraws++;
+ postSetNeedsRedrawToMainThread();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numCompleteCommits;
+ int m_numDraws;
+};
+
+TEST_F(CCLayerTreeHostTestCommitingWithContinuousRedraw, runMultiThread)
+{
+ runTest(true);
+}
+
+// Two setNeedsCommits in a row should lead to at least 1 commit and at least 1
+// draw with frame 0.
+class CCLayerTreeHostTestSetNeedsCommit1 : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetNeedsCommit1()
+ : m_numCommits(0)
+ , m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ m_numDraws++;
+ if (!impl->sourceFrameNumber())
+ endTest();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ m_numCommits++;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_GE(1, m_numCommits);
+ EXPECT_GE(1, m_numDraws);
+ }
+
+private:
+ int m_numCommits;
+ int m_numDraws;
+};
+
+TEST_F(CCLayerTreeHostTestSetNeedsCommit1, DISABLED_runMultiThread)
+{
+ runTest(true);
+}
+
+// A setNeedsCommit should lead to 1 commit. Issuing a second commit after that
+// first committed frame draws should lead to another commit.
+class CCLayerTreeHostTestSetNeedsCommit2 : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetNeedsCommit2()
+ : m_numCommits(0)
+ , m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ if (!impl->sourceFrameNumber())
+ postSetNeedsCommitToMainThread();
+ else if (impl->sourceFrameNumber() == 1)
+ endTest();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ m_numCommits++;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(2, m_numCommits);
+ EXPECT_GE(2, m_numDraws);
+ }
+
+private:
+ int m_numCommits;
+ int m_numDraws;
+};
+
+#if OS(WINDOWS)
+// http://webkit.org/b/74623
+TEST_F(CCLayerTreeHostTestSetNeedsCommit2, FLAKY_runMultiThread)
+#else
+TEST_F(CCLayerTreeHostTestSetNeedsCommit2, runMultiThread)
+#endif
+{
+ runTest(true);
+}
+
+// 1 setNeedsRedraw after the first commit has completed should lead to 1
+// additional draw.
+class CCLayerTreeHostTestSetNeedsRedraw : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetNeedsRedraw()
+ : m_numCommits(0)
+ , m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ EXPECT_EQ(0, impl->sourceFrameNumber());
+ if (!m_numDraws)
+ postSetNeedsRedrawToMainThread(); // Redraw again to verify that the second redraw doesn't commit.
+ else
+ endTest();
+ m_numDraws++;
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ EXPECT_EQ(0, m_numDraws);
+ m_numCommits++;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_GE(2, m_numDraws);
+ EXPECT_EQ(1, m_numCommits);
+ }
+
+private:
+ int m_numCommits;
+ int m_numDraws;
+};
+
+TEST_F(CCLayerTreeHostTestSetNeedsRedraw, runMultiThread)
+{
+ runTest(true);
+}
+
+// If the layerTreeHost says it can't draw, then we should not try to draw.
+class CCLayerTreeHostTestCanDrawBlocksDrawing : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestCanDrawBlocksDrawing()
+ : m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ // Only the initial draw should bring us here.
+ EXPECT_TRUE(impl->canDraw());
+ EXPECT_EQ(0, impl->sourceFrameNumber());
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ if (m_numCommits >= 1) {
+ // After the first commit, we should not be able to draw.
+ EXPECT_FALSE(impl->canDraw());
+ }
+ }
+
+ virtual void didCommit() OVERRIDE
+ {
+ m_numCommits++;
+ if (m_numCommits == 1) {
+ // Make the viewport empty so the host says it can't draw.
+ m_layerTreeHost->setViewportSize(IntSize(0, 0), IntSize(0, 0));
+
+ OwnArrayPtr<char> pixels(adoptArrayPtr(new char[4]));
+ m_layerTreeHost->compositeAndReadback(static_cast<void*>(pixels.get()), IntRect(0, 0, 1, 1));
+ } else if (m_numCommits == 2) {
+ m_layerTreeHost->setNeedsRedraw();
+ m_layerTreeHost->setNeedsCommit();
+ } else
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numCommits;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestCanDrawBlocksDrawing)
+
+// beginLayerWrite should prevent draws from executing until a commit occurs
+class CCLayerTreeHostTestWriteLayersRedraw : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestWriteLayersRedraw()
+ : m_numCommits(0)
+ , m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAcquireLayerTextures();
+ postSetNeedsRedrawToMainThread(); // should be inhibited without blocking
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ m_numDraws++;
+ EXPECT_EQ(m_numDraws, m_numCommits);
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ m_numCommits++;
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(1, m_numCommits);
+ }
+
+private:
+ int m_numCommits;
+ int m_numDraws;
+};
+
+TEST_F(CCLayerTreeHostTestWriteLayersRedraw, runMultiThread)
+{
+ runTest(true);
+}
+
+// Verify that when resuming visibility, requesting layer write permission
+// will not deadlock the main thread even though there are not yet any
+// scheduled redraws. This behavior is critical for reliably surviving tab
+// switching. There are no failure conditions to this test, it just passes
+// by not timing out.
+class CCLayerTreeHostTestWriteLayersAfterVisible : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestWriteLayersAfterVisible()
+ : m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ m_numCommits++;
+ if (m_numCommits == 2)
+ endTest();
+ else {
+ postSetVisibleToMainThread(false);
+ postSetVisibleToMainThread(true);
+ postAcquireLayerTextures();
+ postSetNeedsCommitToMainThread();
+ }
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numCommits;
+};
+
+TEST_F(CCLayerTreeHostTestWriteLayersAfterVisible, runMultiThread)
+{
+ runTest(true);
+}
+
+// A compositeAndReadback while invisible should force a normal commit without assertion.
+class CCLayerTreeHostTestCompositeAndReadbackWhileInvisible : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestCompositeAndReadbackWhileInvisible()
+ : m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ }
+
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ m_numCommits++;
+ if (m_numCommits == 1) {
+ m_layerTreeHost->setVisible(false);
+ m_layerTreeHost->setNeedsCommit();
+ m_layerTreeHost->setNeedsCommit();
+ OwnArrayPtr<char> pixels(adoptArrayPtr(new char[4]));
+ m_layerTreeHost->compositeAndReadback(static_cast<void*>(pixels.get()), IntRect(0, 0, 1, 1));
+ } else
+ endTest();
+
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numCommits;
+};
+
+TEST_F(CCLayerTreeHostTestCompositeAndReadbackWhileInvisible, runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestAbortFrameWhenInvisible : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAbortFrameWhenInvisible()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ // Request a commit (from the main thread), which will trigger the commit flow from the impl side.
+ m_layerTreeHost->setNeedsCommit();
+ // Then mark ourselves as not visible before processing any more messages on the main thread.
+ m_layerTreeHost->setVisible(false);
+ // If we make it without kicking a frame, we pass!
+ endTestAfterDelay(1);
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ ASSERT_FALSE(true);
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+};
+
+TEST_F(CCLayerTreeHostTestAbortFrameWhenInvisible, runMultiThread)
+{
+ runTest(true);
+}
+
+
+// Trigger a frame with setNeedsCommit. Then, inside the resulting animate
+// callback, requet another frame using setNeedsAnimate. End the test when
+// animate gets called yet-again, indicating that the proxy is correctly
+// handling the case where setNeedsAnimate() is called inside the begin frame
+// flow.
+class CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback()
+ : m_numAnimates(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsAnimateToMainThread();
+ }
+
+ virtual void animate(double) OVERRIDE
+ {
+ if (!m_numAnimates) {
+ m_layerTreeHost->setNeedsAnimate();
+ m_numAnimates++;
+ return;
+ }
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numAnimates;
+};
+
+TEST_F(CCLayerTreeHostTestSetNeedsAnimateInsideAnimationCallback, runMultiThread)
+{
+ runTest(true);
+}
+
+// Add a layer animation and confirm that CCLayerTreeHostImpl::animateLayers does get
+// called and continues to get called.
+class CCLayerTreeHostTestAddAnimation : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAddAnimation()
+ : m_numAnimates(0)
+ , m_receivedAnimationStartedNotification(false)
+ , m_startTime(0)
+ , m_firstMonotonicTime(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddInstantAnimationToMainThread();
+ }
+
+ virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) OVERRIDE
+ {
+ if (!m_numAnimates) {
+ // The animation had zero duration so layerTreeHostImpl should no
+ // longer need to animate its layers.
+ EXPECT_FALSE(layerTreeHostImpl->needsAnimateLayers());
+ m_numAnimates++;
+ m_firstMonotonicTime = monotonicTime;
+ return;
+ }
+ EXPECT_LT(0, m_startTime);
+ EXPECT_LT(0, m_firstMonotonicTime);
+ EXPECT_NE(m_startTime, m_firstMonotonicTime);
+ EXPECT_TRUE(m_receivedAnimationStartedNotification);
+ endTest();
+ }
+
+ virtual void notifyAnimationStarted(double wallClockTime) OVERRIDE
+ {
+ m_receivedAnimationStartedNotification = true;
+ m_startTime = wallClockTime;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numAnimates;
+ bool m_receivedAnimationStartedNotification;
+ double m_startTime;
+ double m_firstMonotonicTime;
+};
+
+TEST_F(CCLayerTreeHostTestAddAnimation, runMultiThread)
+{
+ runTest(true);
+}
+
+// Add a layer animation to a layer, but continually fail to draw. Confirm that after
+// a while, we do eventually force a draw.
+class CCLayerTreeHostTestCheckerboardDoesNotStarveDraws : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestCheckerboardDoesNotStarveDraws()
+ : m_startedAnimating(false)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddAnimationToMainThread();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+ virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) OVERRIDE
+ {
+ m_startedAnimating = true;
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ if (m_startedAnimating)
+ endTest();
+ }
+
+ virtual bool prepareToDrawOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ return false;
+ }
+
+private:
+ bool m_startedAnimating;
+};
+
+// Starvation can only be an issue with the MT compositor.
+TEST_F(CCLayerTreeHostTestCheckerboardDoesNotStarveDraws, runMultiThread)
+{
+ runTest(true);
+}
+
+// Ensures that animations continue to be ticked when we are backgrounded.
+class CCLayerTreeHostTestTickAnimationWhileBackgrounded : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestTickAnimationWhileBackgrounded()
+ : m_numAnimates(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddAnimationToMainThread();
+ }
+
+ // Use willAnimateLayers to set visible false before the animation runs and
+ // causes a commit, so we block the second visible animate in single-thread
+ // mode.
+ virtual void willAnimateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) OVERRIDE
+ {
+ if (m_numAnimates < 2) {
+ if (!m_numAnimates) {
+ // We have a long animation running. It should continue to tick even if we are not visible.
+ postSetVisibleToMainThread(false);
+ }
+ m_numAnimates++;
+ return;
+ }
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ int m_numAnimates;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestTickAnimationWhileBackgrounded)
+
+// Ensures that animations continue to be ticked when we are backgrounded.
+class CCLayerTreeHostTestAddAnimationWithTimingFunction : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAddAnimationWithTimingFunction()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddAnimationToMainThread();
+ }
+
+ virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) OVERRIDE
+ {
+ const CCActiveAnimation* animation = m_layerTreeHost->rootLayer()->layerAnimationController()->getActiveAnimation(0, CCActiveAnimation::Opacity);
+ if (!animation)
+ return;
+ const CCFloatAnimationCurve* curve = animation->curve()->toFloatAnimationCurve();
+ float startOpacity = curve->getValue(0);
+ float endOpacity = curve->getValue(curve->duration());
+ float linearlyInterpolatedOpacity = 0.25 * endOpacity + 0.75 * startOpacity;
+ double time = curve->duration() * 0.25;
+ // If the linear timing function associated with this animation was not picked up,
+ // then the linearly interpolated opacity would be different because of the
+ // default ease timing function.
+ EXPECT_FLOAT_EQ(linearlyInterpolatedOpacity, curve->getValue(time));
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestAddAnimationWithTimingFunction)
+
+// Ensures that when opacity is being animated, this value does not cause the subtree to be skipped.
+class CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->rootLayer()->setDrawOpacity(1);
+ m_layerTreeHost->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+ m_layerTreeHost->rootLayer()->setOpacity(0);
+ postAddAnimationToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ // If the subtree was skipped when preparing to draw, the layer's draw opacity
+ // will not have been updated. It should be set to 0 due to the animation.
+ // Without the animation, the layer will be skipped since it has zero opacity.
+ EXPECT_EQ(0, m_layerTreeHost->rootLayer()->drawOpacity());
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+#if OS(WINDOWS)
+// http://webkit.org/b/74623
+TEST_F(CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity, FLAKY_runMultiThread)
+#else
+TEST_F(CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity, runMultiThread)
+#endif
+{
+ runTest(true);
+}
+
+// Ensures that main thread animations have their start times synchronized with impl thread animations.
+class CCLayerTreeHostTestSynchronizeAnimationStartTimes : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSynchronizeAnimationStartTimes()
+ : m_layerTreeHostImpl(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddAnimationToMainThread();
+ }
+
+ // This is guaranteed to be called before CCLayerTreeHostImpl::animateLayers.
+ virtual void willAnimateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) OVERRIDE
+ {
+ m_layerTreeHostImpl = layerTreeHostImpl;
+ }
+
+ virtual void notifyAnimationStarted(double time) OVERRIDE
+ {
+ EXPECT_TRUE(m_layerTreeHostImpl);
+
+ CCLayerAnimationController* controllerImpl = m_layerTreeHostImpl->rootLayer()->layerAnimationController();
+ CCLayerAnimationController* controller = m_layerTreeHost->rootLayer()->layerAnimationController();
+ CCActiveAnimation* animationImpl = controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity);
+ CCActiveAnimation* animation = controller->getActiveAnimation(0, CCActiveAnimation::Opacity);
+
+ EXPECT_EQ(animationImpl->startTime(), animation->startTime());
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ CCLayerTreeHostImpl* m_layerTreeHostImpl;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestSynchronizeAnimationStartTimes)
+
+// Ensures that main thread animations have their start times synchronized with impl thread animations.
+class CCLayerTreeHostTestAnimationFinishedEvents : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAnimationFinishedEvents()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postAddInstantAnimationToMainThread();
+ }
+
+ virtual void notifyAnimationFinished(double time) OVERRIDE
+ {
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestAnimationFinishedEvents)
+
+class CCLayerTreeHostTestScrollSimple : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestScrollSimple()
+ : m_initialScroll(IntPoint(10, 20))
+ , m_secondScroll(IntPoint(40, 5))
+ , m_scrollAmount(2, -1)
+ , m_scrolls(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->rootLayer()->setScrollable(true);
+ m_layerTreeHost->rootLayer()->setScrollPosition(m_initialScroll);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ LayerChromium* root = m_layerTreeHost->rootLayer();
+ if (!m_layerTreeHost->commitNumber())
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll);
+ else {
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount);
+
+ // Pretend like Javascript updated the scroll position itself.
+ root->setScrollPosition(m_secondScroll);
+ }
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CCLayerImpl* root = impl->rootLayer();
+ EXPECT_EQ(root->scrollDelta(), IntSize());
+
+ root->setScrollable(true);
+ root->setMaxScrollPosition(IntSize(100, 100));
+ root->scrollBy(m_scrollAmount);
+
+ if (!impl->sourceFrameNumber()) {
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll);
+ EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
+ postSetNeedsCommitToMainThread();
+ } else if (impl->sourceFrameNumber() == 1) {
+ EXPECT_EQ(root->scrollPosition(), m_secondScroll);
+ EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
+ endTest();
+ }
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE
+ {
+ IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
+ m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
+ m_scrolls++;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(1, m_scrolls);
+ }
+private:
+ IntPoint m_initialScroll;
+ IntPoint m_secondScroll;
+ IntSize m_scrollAmount;
+ int m_scrolls;
+};
+
+TEST_F(CCLayerTreeHostTestScrollSimple, DISABLED_runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestScrollMultipleRedraw : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestScrollMultipleRedraw()
+ : m_initialScroll(IntPoint(40, 10))
+ , m_scrollAmount(-3, 17)
+ , m_scrolls(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->rootLayer()->setScrollable(true);
+ m_layerTreeHost->rootLayer()->setScrollPosition(m_initialScroll);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void beginCommitOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ LayerChromium* root = m_layerTreeHost->rootLayer();
+ if (!impl->sourceFrameNumber())
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll);
+ else if (impl->sourceFrameNumber() == 1)
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
+ else if (impl->sourceFrameNumber() == 2)
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CCLayerImpl* root = impl->rootLayer();
+ root->setScrollable(true);
+ root->setMaxScrollPosition(IntSize(100, 100));
+
+ if (!impl->sourceFrameNumber() && impl->sourceAnimationFrameNumber() == 1) {
+ // First draw after first commit.
+ EXPECT_EQ(root->scrollDelta(), IntSize());
+ root->scrollBy(m_scrollAmount);
+ EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
+
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll);
+ postSetNeedsRedrawToMainThread();
+ } else if (!impl->sourceFrameNumber() && impl->sourceAnimationFrameNumber() == 2) {
+ // Second draw after first commit.
+ EXPECT_EQ(root->scrollDelta(), m_scrollAmount);
+ root->scrollBy(m_scrollAmount);
+ EXPECT_EQ(root->scrollDelta(), m_scrollAmount + m_scrollAmount);
+
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll);
+ postSetNeedsCommitToMainThread();
+ } else if (impl->sourceFrameNumber() == 1) {
+ // Third or later draw after second commit.
+ EXPECT_GE(impl->sourceAnimationFrameNumber(), 3);
+ EXPECT_EQ(root->scrollDelta(), IntSize());
+ EXPECT_EQ(root->scrollPosition(), m_initialScroll + m_scrollAmount + m_scrollAmount);
+ endTest();
+ }
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE
+ {
+ IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
+ m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
+ m_scrolls++;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(1, m_scrolls);
+ }
+private:
+ IntPoint m_initialScroll;
+ IntSize m_scrollAmount;
+ int m_scrolls;
+};
+
+TEST_F(CCLayerTreeHostTestScrollMultipleRedraw, DISABLED_runMultiThread)
+{
+ runTest(true);
+}
+
+// This test verifies that properties on the layer tree host are commited to the impl side.
+class CCLayerTreeHostTestCommit : public CCLayerTreeHostTest {
+public:
+
+ CCLayerTreeHostTestCommit() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setViewportSize(IntSize(20, 20), IntSize(20, 20));
+ m_layerTreeHost->setBackgroundColor(SK_ColorGRAY);
+ m_layerTreeHost->setPageScaleFactorAndLimits(5, 5, 5);
+
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ EXPECT_EQ(IntSize(20, 20), impl->layoutViewportSize());
+ EXPECT_EQ(SK_ColorGRAY, impl->backgroundColor());
+ EXPECT_EQ(5, impl->pageScale());
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE { }
+};
+
+TEST_F(CCLayerTreeHostTestCommit, runTest)
+{
+ runTest(true);
+}
+
+// Verifies that startPageScaleAnimation events propagate correctly from CCLayerTreeHost to
+// CCLayerTreeHostImpl in the MT compositor.
+class CCLayerTreeHostTestStartPageScaleAnimation : public CCLayerTreeHostTest {
+public:
+
+ CCLayerTreeHostTestStartPageScaleAnimation()
+ : m_animationRequested(false)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->rootLayer()->setScrollable(true);
+ m_layerTreeHost->rootLayer()->setScrollPosition(IntPoint());
+ postSetNeedsRedrawToMainThread();
+ }
+
+ static void requestStartPageScaleAnimation(void* self)
+ {
+ CCLayerTreeHostTestStartPageScaleAnimation* test = static_cast<CCLayerTreeHostTestStartPageScaleAnimation*>(self);
+ if (test->layerTreeHost())
+ test->layerTreeHost()->startPageScaleAnimation(IntSize(), false, 1.25, 0);
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ impl->rootLayer()->setScrollable(true);
+ impl->rootLayer()->setScrollPosition(IntPoint());
+ impl->setPageScaleFactorAndLimits(impl->pageScale(), 0.5, 2);
+
+ // We request animation only once.
+ if (!m_animationRequested) {
+ callOnMainThread(CCLayerTreeHostTestStartPageScaleAnimation::requestStartPageScaleAnimation, this);
+ m_animationRequested = true;
+ }
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE
+ {
+ IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
+ m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
+ m_layerTreeHost->setPageScaleFactorAndLimits(scale, 0.5, 2);
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ impl->processScrollDeltas();
+ // We get one commit before the first draw, and the animation doesn't happen until the second draw.
+ if (impl->sourceFrameNumber() == 1) {
+ EXPECT_EQ(1.25, impl->pageScale());
+ endTest();
+ } else
+ postSetNeedsRedrawToMainThread();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ bool m_animationRequested;
+};
+
+TEST_F(CCLayerTreeHostTestStartPageScaleAnimation, runTest)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestSetVisible : public CCLayerTreeHostTest {
+public:
+
+ CCLayerTreeHostTestSetVisible()
+ : m_numDraws(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetVisibleToMainThread(false);
+ postSetNeedsRedrawToMainThread(); // This is suppressed while we're invisible.
+ postSetVisibleToMainThread(true); // Triggers the redraw.
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ EXPECT_TRUE(impl->visible());
+ ++m_numDraws;
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(1, m_numDraws);
+ }
+
+private:
+ int m_numDraws;
+};
+
+TEST_F(CCLayerTreeHostTestSetVisible, runMultiThread)
+{
+ runTest(true);
+}
+
+class TestOpacityChangeLayerDelegate : public ContentLayerDelegate {
+public:
+ TestOpacityChangeLayerDelegate(CCLayerTreeHostTest* test)
+ : m_test(test)
+ {
+ }
+
+ virtual void paintContents(SkCanvas*, const IntRect&, FloatRect&) OVERRIDE
+ {
+ // Set layer opacity to 0.
+ m_test->layerTreeHost()->rootLayer()->setOpacity(0);
+ }
+
+private:
+ CCLayerTreeHostTest* m_test;
+};
+
+class ContentLayerChromiumWithUpdateTracking : public ContentLayerChromium {
+public:
+ static PassRefPtr<ContentLayerChromiumWithUpdateTracking> create(ContentLayerDelegate *delegate) { return adoptRef(new ContentLayerChromiumWithUpdateTracking(delegate)); }
+
+ int paintContentsCount() { return m_paintContentsCount; }
+ void resetPaintContentsCount() { m_paintContentsCount = 0; }
+
+ virtual void update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats) OVERRIDE
+ {
+ ContentLayerChromium::update(queue, occlusion, stats);
+ m_paintContentsCount++;
+ }
+
+private:
+ explicit ContentLayerChromiumWithUpdateTracking(ContentLayerDelegate* delegate)
+ : ContentLayerChromium(delegate)
+ , m_paintContentsCount(0)
+ {
+ setAnchorPoint(FloatPoint(0, 0));
+ setBounds(IntSize(10, 10));
+ setIsDrawable(true);
+ }
+
+ int m_paintContentsCount;
+};
+
+// Layer opacity change during paint should not prevent compositor resources from being updated during commit.
+class CCLayerTreeHostTestOpacityChange : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestOpacityChange()
+ : m_testOpacityChangeDelegate(this)
+ , m_updateCheckLayer(ContentLayerChromiumWithUpdateTracking::create(&m_testOpacityChangeDelegate))
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setRootLayer(m_updateCheckLayer);
+ m_layerTreeHost->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) OVERRIDE
+ {
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ // update() should have been called once.
+ EXPECT_EQ(1, m_updateCheckLayer->paintContentsCount());
+
+ // clear m_updateCheckLayer so CCLayerTreeHost dies.
+ m_updateCheckLayer.clear();
+ }
+
+private:
+ TestOpacityChangeLayerDelegate m_testOpacityChangeDelegate;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_updateCheckLayer;
+};
+
+TEST_F(CCLayerTreeHostTestOpacityChange, runMultiThread)
+{
+ runTest(true);
+}
+
+class MockContentLayerDelegate : public ContentLayerDelegate {
+public:
+ bool drawsContent() const { return true; }
+ MOCK_CONST_METHOD0(preserves3D, bool());
+ void paintContents(SkCanvas*, const IntRect&, FloatRect&) OVERRIDE { }
+ void notifySyncRequired() { }
+};
+
+class CCLayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers : public CCLayerTreeHostTest {
+public:
+
+ CCLayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers()
+ : m_rootLayer(ContentLayerChromium::create(&m_delegate))
+ , m_childLayer(ContentLayerChromium::create(&m_delegate))
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setViewportSize(IntSize(40, 40), IntSize(60, 60));
+ m_layerTreeHost->setDeviceScaleFactor(1.5);
+ EXPECT_EQ(IntSize(40, 40), m_layerTreeHost->layoutViewportSize());
+ EXPECT_EQ(IntSize(60, 60), m_layerTreeHost->deviceViewportSize());
+
+ m_rootLayer->addChild(m_childLayer);
+
+ m_rootLayer->setIsDrawable(true);
+ m_rootLayer->setBounds(IntSize(30, 30));
+ m_rootLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ m_childLayer->setIsDrawable(true);
+ m_childLayer->setPosition(IntPoint(2, 2));
+ m_childLayer->setBounds(IntSize(10, 10));
+ m_childLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ m_layerTreeHost->setRootLayer(m_rootLayer);
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ // Get access to protected methods.
+ MockLayerTreeHostImpl* mockImpl = static_cast<MockLayerTreeHostImpl*>(impl);
+
+ // Should only do one commit.
+ EXPECT_EQ(0, impl->sourceFrameNumber());
+ // Device scale factor should come over to impl.
+ EXPECT_NEAR(impl->deviceScaleFactor(), 1.5, 0.00001);
+
+ // Both layers are on impl.
+ ASSERT_EQ(1u, impl->rootLayer()->children().size());
+
+ // Device viewport is scaled.
+ EXPECT_EQ(IntSize(40, 40), impl->layoutViewportSize());
+ EXPECT_EQ(IntSize(60, 60), impl->deviceViewportSize());
+
+ CCLayerImpl* root = impl->rootLayer();
+ CCLayerImpl* child = impl->rootLayer()->children()[0].get();
+
+ // Positions remain in layout pixels.
+ EXPECT_EQ(IntPoint(0, 0), root->position());
+ EXPECT_EQ(IntPoint(2, 2), child->position());
+
+ // Compute all the layer transforms for the frame.
+ MockLayerTreeHostImpl::CCLayerList renderSurfaceLayerList;
+ mockImpl->calculateRenderSurfaceLayerList(renderSurfaceLayerList);
+
+ // Both layers should be drawing into the root render surface.
+ ASSERT_EQ(1u, renderSurfaceLayerList.size());
+ ASSERT_EQ(root->renderSurface(), renderSurfaceLayerList[0]->renderSurface());
+ ASSERT_EQ(2u, root->renderSurface()->layerList().size());
+
+ // The root render surface is the size of the viewport.
+ EXPECT_EQ_RECT(IntRect(0, 0, 60, 60), root->renderSurface()->contentRect());
+
+ WebTransformationMatrix scaleTransform;
+ scaleTransform.scale(impl->deviceScaleFactor());
+
+ // The root layer is scaled by 2x.
+ WebTransformationMatrix rootScreenSpaceTransform = scaleTransform;
+ WebTransformationMatrix rootDrawTransform = scaleTransform;
+
+ EXPECT_EQ(rootDrawTransform, root->drawTransform());
+ EXPECT_EQ(rootScreenSpaceTransform, root->screenSpaceTransform());
+
+ // The child is at position 2,2, so translate by 2,2 before applying the scale by 2x.
+ WebTransformationMatrix childScreenSpaceTransform = scaleTransform;
+ childScreenSpaceTransform.translate(2, 2);
+ WebTransformationMatrix childDrawTransform = scaleTransform;
+ childDrawTransform.translate(2, 2);
+
+ EXPECT_EQ(childDrawTransform, child->drawTransform());
+ EXPECT_EQ(childScreenSpaceTransform, child->screenSpaceTransform());
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ m_rootLayer.clear();
+ m_childLayer.clear();
+ }
+
+private:
+ MockContentLayerDelegate m_delegate;
+ RefPtr<ContentLayerChromium> m_rootLayer;
+ RefPtr<ContentLayerChromium> m_childLayer;
+};
+
+TEST_F(CCLayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers, runMultiThread)
+{
+ runTest(true);
+}
+
+// Verify atomicity of commits and reuse of textures.
+class CCLayerTreeHostTestAtomicCommit : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAtomicCommit()
+ : m_layer(ContentLayerChromiumWithUpdateTracking::create(&m_delegate))
+ {
+ // Make sure partial texture updates are turned off.
+ m_settings.maxPartialTextureUpdates = 0;
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setRootLayer(m_layer);
+ m_layerTreeHost->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+
+ postSetNeedsCommitToMainThread();
+ postSetNeedsRedrawToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(impl->context()->context3D());
+
+ switch (impl->sourceFrameNumber()) {
+ case 0:
+ // Number of textures should be one.
+ ASSERT_EQ(1, context->numTextures());
+ // Number of textures used for commit should be one.
+ EXPECT_EQ(1, context->numUsedTextures());
+ // Verify that used texture is correct.
+ EXPECT_TRUE(context->usedTexture(context->texture(0)));
+
+ context->resetUsedTextures();
+ break;
+ case 1:
+ // Number of textures should be two as the first texture
+ // is used by impl thread and cannot by used for update.
+ ASSERT_EQ(2, context->numTextures());
+ // Number of textures used for commit should still be one.
+ EXPECT_EQ(1, context->numUsedTextures());
+ // First texture should not have been used.
+ EXPECT_FALSE(context->usedTexture(context->texture(0)));
+ // New texture should have been used.
+ EXPECT_TRUE(context->usedTexture(context->texture(1)));
+
+ context->resetUsedTextures();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(impl->context()->context3D());
+
+ // Number of textures used for draw should always be one.
+ EXPECT_EQ(1, context->numUsedTextures());
+
+ if (impl->sourceFrameNumber() < 1) {
+ context->resetUsedTextures();
+ postSetNeedsAnimateAndCommitToMainThread();
+ postSetNeedsRedrawToMainThread();
+ } else
+ endTest();
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ m_layer->setNeedsDisplay();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ MockContentLayerDelegate m_delegate;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_layer;
+};
+
+TEST_F(CCLayerTreeHostTestAtomicCommit, runMultiThread)
+{
+ runTest(true);
+}
+
+static void setLayerPropertiesForTesting(LayerChromium* layer, LayerChromium* parent, const WebTransformationMatrix& transform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque)
+{
+ layer->removeAllChildren();
+ if (parent)
+ parent->addChild(layer);
+ layer->setTransform(transform);
+ layer->setAnchorPoint(anchor);
+ layer->setPosition(position);
+ layer->setBounds(bounds);
+ layer->setOpaque(opaque);
+}
+
+class CCLayerTreeHostTestAtomicCommitWithPartialUpdate : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestAtomicCommitWithPartialUpdate()
+ : m_parent(ContentLayerChromiumWithUpdateTracking::create(&m_delegate))
+ , m_child(ContentLayerChromiumWithUpdateTracking::create(&m_delegate))
+ , m_numCommits(0)
+ {
+ // Allow one partial texture update.
+ m_settings.maxPartialTextureUpdates = 1;
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setRootLayer(m_parent);
+ m_layerTreeHost->setViewportSize(IntSize(10, 20), IntSize(10, 20));
+
+ WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(m_parent.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 20), true);
+ setLayerPropertiesForTesting(m_child.get(), m_parent.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(0, 10), IntSize(10, 10), false);
+
+ postSetNeedsCommitToMainThread();
+ postSetNeedsRedrawToMainThread();
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(impl->context()->context3D());
+
+ switch (impl->sourceFrameNumber()) {
+ case 0:
+ // Number of textures should be two.
+ ASSERT_EQ(2, context->numTextures());
+ // Number of textures used for commit should be two.
+ EXPECT_EQ(2, context->numUsedTextures());
+ // Verify that used textures are correct.
+ EXPECT_TRUE(context->usedTexture(context->texture(0)));
+ EXPECT_TRUE(context->usedTexture(context->texture(1)));
+
+ context->resetUsedTextures();
+ break;
+ case 1:
+ // Number of textures used for commit should still be two.
+ EXPECT_EQ(2, context->numUsedTextures());
+ // First two textures should not have been used.
+ EXPECT_FALSE(context->usedTexture(context->texture(0)));
+ EXPECT_FALSE(context->usedTexture(context->texture(1)));
+ // New textures should have been used.
+ EXPECT_TRUE(context->usedTexture(context->texture(2)));
+ EXPECT_TRUE(context->usedTexture(context->texture(3)));
+
+ context->resetUsedTextures();
+ break;
+ case 2:
+ // Number of textures used for commit should still be two.
+ EXPECT_EQ(2, context->numUsedTextures());
+
+ context->resetUsedTextures();
+ break;
+ case 3:
+ // No textures should be used for commit.
+ EXPECT_EQ(0, context->numUsedTextures());
+
+ context->resetUsedTextures();
+ break;
+ case 4:
+ // Number of textures used for commit should be one.
+ EXPECT_EQ(1, context->numUsedTextures());
+
+ context->resetUsedTextures();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(impl->context()->context3D());
+
+ // Number of textures used for drawing should two except for frame 4
+ // where the viewport only contains one layer.
+ if (impl->sourceFrameNumber() == 3)
+ EXPECT_EQ(1, context->numUsedTextures());
+ else
+ EXPECT_EQ(2, context->numUsedTextures());
+
+ if (impl->sourceFrameNumber() < 4) {
+ context->resetUsedTextures();
+ postSetNeedsAnimateAndCommitToMainThread();
+ postSetNeedsRedrawToMainThread();
+ } else
+ endTest();
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ switch (m_numCommits++) {
+ case 0:
+ case 1:
+ m_parent->setNeedsDisplay();
+ m_child->setNeedsDisplay();
+ break;
+ case 2:
+ // Damage part of layers.
+ m_parent->setNeedsDisplayRect(FloatRect(0, 0, 5, 5));
+ m_child->setNeedsDisplayRect(FloatRect(0, 0, 5, 5));
+ break;
+ case 3:
+ m_child->setNeedsDisplay();
+ m_layerTreeHost->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+ break;
+ case 4:
+ m_layerTreeHost->setViewportSize(IntSize(10, 20), IntSize(10, 20));
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ MockContentLayerDelegate m_delegate;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_parent;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_child;
+ int m_numCommits;
+};
+
+TEST_F(CCLayerTreeHostTestAtomicCommitWithPartialUpdate, runMultiThread)
+{
+ runTest(true);
+}
+
+class TestLayerChromium : public LayerChromium {
+public:
+ static PassRefPtr<TestLayerChromium> create() { return adoptRef(new TestLayerChromium()); }
+
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker* occlusion, CCRenderingStats&) OVERRIDE
+ {
+ // Gain access to internals of the CCOcclusionTracker.
+ const TestCCOcclusionTracker* testOcclusion = static_cast<const TestCCOcclusionTracker*>(occlusion);
+ m_occludedScreenSpace = testOcclusion ? testOcclusion->occlusionInScreenSpace() : Region();
+ }
+
+ virtual bool drawsContent() const OVERRIDE { return true; }
+
+ const Region& occludedScreenSpace() const { return m_occludedScreenSpace; }
+ void clearOccludedScreenSpace() { m_occludedScreenSpace = Region(); }
+
+private:
+ TestLayerChromium() : LayerChromium() { }
+
+ Region m_occludedScreenSpace;
+};
+
+static void setTestLayerPropertiesForTesting(TestLayerChromium* layer, LayerChromium* parent, const WebTransformationMatrix& transform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque)
+{
+ setLayerPropertiesForTesting(layer, parent, transform, anchor, position, bounds, opaque);
+ layer->clearOccludedScreenSpace();
+}
+
+class CCLayerTreeHostTestLayerOcclusion : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestLayerOcclusion() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> child = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> child2 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> grandChild = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> mask = TestLayerChromium::create();
+
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ child->setMasksToBounds(true);
+
+ // See CCLayerTreeHostCommonTest.layerAddsSelfToOccludedRegionWithRotatedSurface for a nice visual of these layers and how they end up
+ // positioned on the screen.
+
+ // The child layer is rotated and the grandChild is opaque, but clipped to the child and rootLayer
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ ASSERT_TRUE(m_layerTreeHost->initializeRendererIfNeeded());
+ CCTextureUpdateQueue queue;
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer is opaque, then it adds to the occlusion seen by the rootLayer.
+ setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // Add a second child to the root layer and the regions should merge
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(70, 20), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 20, 170, 180), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(2u, rootLayer->occludedScreenSpace().rects().size());
+
+ // Move the second child to be sure.
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 30, 190, 170), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(2u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer has a mask on it, then it shouldn't contribute to occlusion on stuff below it
+ setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+ setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ child->setMaskLayer(mask.get());
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer with a mask is below child2, then child2 should contribute to occlusion on everything, and child shouldn't contribute to the rootLayer
+ setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+ setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+
+ child->setMaskLayer(mask.get());
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(2u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer has a non-opaque drawOpacity, then it shouldn't contribute to occlusion on stuff below it
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+
+ child->setMaskLayer(0);
+ child->setOpacity(0.5);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer with non-opaque drawOpacity is below child2, then child2 should contribute to occlusion on everything, and child shouldn't contribute to the rootLayer
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+
+ child->setMaskLayer(0);
+ child->setOpacity(0.5);
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(2u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLayerOcclusion)
+
+class CCLayerTreeHostTestLayerOcclusionWithFilters : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestLayerOcclusionWithFilters() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> child = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> child2 = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> grandChild = TestLayerChromium::create();
+ RefPtr<TestLayerChromium> mask = TestLayerChromium::create();
+
+ WebTransformationMatrix identityMatrix;
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ child->setMasksToBounds(true);
+
+ // If the child layer has a filter that changes alpha values, and is below child2, then child2 should contribute to occlusion on everything,
+ // and child shouldn't contribute to the rootLayer
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+
+ {
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createOpacityFilter(0.5));
+ child->setFilters(filters);
+ }
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ ASSERT_TRUE(m_layerTreeHost->initializeRendererIfNeeded());
+ CCTextureUpdateQueue queue;
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(2u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // If the child layer has a filter that moves pixels/changes alpha, and is below child2, then child should not inherit occlusion from outside its subtree,
+ // and should not contribute to the rootLayer
+ setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true);
+ setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true);
+
+ {
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(10));
+ child->setFilters(filters);
+ }
+
+ m_layerTreeHost->setRootLayer(rootLayer);
+ m_layerTreeHost->setViewportSize(rootLayer->bounds(), rootLayer->bounds());
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds());
+ EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, child->occludedScreenSpace().rects().size());
+ EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size());
+
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ CCLayerTreeHost::setNeedsFilterContext(false);
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLayerOcclusionWithFilters)
+
+class CCLayerTreeHostTestManySurfaces : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestManySurfaces() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ // We create enough RenderSurfaces that it will trigger Vector reallocation while computing occlusion.
+ Region occluded;
+ const WebTransformationMatrix identityMatrix;
+ Vector<RefPtr<TestLayerChromium> > layers;
+ Vector<RefPtr<TestLayerChromium> > children;
+ int numSurfaces = 20;
+ RefPtr<TestLayerChromium> replica = TestLayerChromium::create();
+
+ for (int i = 0; i < numSurfaces; ++i) {
+ layers.append(TestLayerChromium::create());
+ if (!i) {
+ setTestLayerPropertiesForTesting(layers.last().get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true);
+ layers.last()->createRenderSurface();
+ } else {
+ setTestLayerPropertiesForTesting(layers.last().get(), layers[layers.size()-2].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(1, 1), IntSize(200-i, 200-i), true);
+ layers.last()->setMasksToBounds(true);
+ layers.last()->setReplicaLayer(replica.get()); // Make it have a RenderSurface
+ }
+ }
+
+ for (int i = 1; i < numSurfaces; ++i) {
+ children.append(TestLayerChromium::create());
+ setTestLayerPropertiesForTesting(children.last().get(), layers[i].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ }
+
+ m_layerTreeHost->setRootLayer(layers[0].get());
+ m_layerTreeHost->setViewportSize(layers[0]->bounds(), layers[0]->bounds());
+ ASSERT_TRUE(m_layerTreeHost->initializeRendererIfNeeded());
+ CCTextureUpdateQueue queue;
+ m_layerTreeHost->updateLayers(queue, std::numeric_limits<size_t>::max());
+ m_layerTreeHost->commitComplete();
+
+ for (int i = 0; i < numSurfaces-1; ++i) {
+ IntRect expectedOcclusion(i+1, i+1, 200-i-1, 200-i-1);
+
+ EXPECT_EQ_RECT(expectedOcclusion, layers[i]->occludedScreenSpace().bounds());
+ EXPECT_EQ(1u, layers[i]->occludedScreenSpace().rects().size());
+ }
+
+ // Kill the layerTreeHost immediately.
+ m_layerTreeHost->setRootLayer(0);
+ m_layerTreeHost.clear();
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestManySurfaces)
+
+// A loseContext(1) should lead to a didRecreateOutputSurface(true)
+class CCLayerTreeHostTestSetSingleLostContext : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetSingleLostContext()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ m_layerTreeHost->loseContext(1);
+ }
+
+ virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE
+ {
+ EXPECT_TRUE(succeeded);
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+TEST_F(CCLayerTreeHostTestSetSingleLostContext, runMultiThread)
+{
+ runTest(true);
+}
+
+// A loseContext(10) should lead to a didRecreateOutputSurface(false), and
+// a finishAllRendering() should not hang.
+class CCLayerTreeHostTestSetRepeatedLostContext : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSetRepeatedLostContext()
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ m_layerTreeHost->loseContext(10);
+ }
+
+ virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE
+ {
+ EXPECT_FALSE(succeeded);
+ m_layerTreeHost->finishAllRendering();
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+TEST_F(CCLayerTreeHostTestSetRepeatedLostContext, runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestFractionalScroll : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestFractionalScroll()
+ : m_scrollAmount(1.75, 0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->rootLayer()->setScrollable(true);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ CCLayerImpl* root = impl->rootLayer();
+ root->setMaxScrollPosition(IntSize(100, 100));
+
+ // Check that a fractional scroll delta is correctly accumulated over multiple commits.
+ if (!impl->sourceFrameNumber()) {
+ EXPECT_EQ(root->scrollPosition(), IntPoint(0, 0));
+ EXPECT_EQ(root->scrollDelta(), FloatSize(0, 0));
+ postSetNeedsCommitToMainThread();
+ } else if (impl->sourceFrameNumber() == 1) {
+ EXPECT_EQ(root->scrollPosition(), flooredIntPoint(m_scrollAmount));
+ EXPECT_EQ(root->scrollDelta(), FloatSize(fmod(m_scrollAmount.width(), 1), 0));
+ postSetNeedsCommitToMainThread();
+ } else if (impl->sourceFrameNumber() == 2) {
+ EXPECT_EQ(root->scrollPosition(), flooredIntPoint(m_scrollAmount + m_scrollAmount));
+ EXPECT_EQ(root->scrollDelta(), FloatSize(fmod(2 * m_scrollAmount.width(), 1), 0));
+ endTest();
+ }
+ root->scrollBy(m_scrollAmount);
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE
+ {
+ IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition();
+ m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta);
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+private:
+ FloatSize m_scrollAmount;
+};
+
+TEST_F(CCLayerTreeHostTestFractionalScroll, runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestFinishAllRendering : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestFinishAllRendering()
+ : m_once(false)
+ , m_mutex()
+ , m_drawCount(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setNeedsRedraw();
+ }
+
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ if (m_once)
+ return;
+ m_once = true;
+ m_layerTreeHost->setNeedsRedraw();
+ m_layerTreeHost->acquireLayerTextures();
+ {
+ Locker<Mutex> lock(m_mutex);
+ m_drawCount = 0;
+ }
+ m_layerTreeHost->finishAllRendering();
+ {
+ Locker<Mutex> lock(m_mutex);
+ EXPECT_EQ(0, m_drawCount);
+ }
+ endTest();
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ Locker<Mutex> lock(m_mutex);
+ ++m_drawCount;
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+private:
+
+ bool m_once;
+ Mutex m_mutex;
+ int m_drawCount;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestFinishAllRendering)
+
+// Layers added to tree with existing active animations should have the animation
+// correctly recognized.
+class CCLayerTreeHostTestLayerAddedWithAnimation : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestLayerAddedWithAnimation()
+ : m_addedAnimation(false)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ EXPECT_FALSE(m_addedAnimation);
+
+ RefPtr<LayerChromium> layer = LayerChromium::create();
+ layer->setLayerAnimationDelegate(this);
+
+ // Any valid CCAnimationCurve will do here.
+ OwnPtr<CCAnimationCurve> curve(CCEaseTimingFunction::create());
+ OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), 1, 1, CCActiveAnimation::Opacity));
+ layer->layerAnimationController()->addAnimation(animation.release());
+
+ // We add the animation *before* attaching the layer to the tree.
+ m_layerTreeHost->rootLayer()->addChild(layer);
+ EXPECT_TRUE(m_addedAnimation);
+
+ endTest();
+ }
+
+ virtual void didAddAnimation() OVERRIDE
+ {
+ m_addedAnimation = true;
+ }
+
+ virtual void afterTest() OVERRIDE { }
+
+private:
+ bool m_addedAnimation;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLayerAddedWithAnimation)
+
+class CCLayerTreeHostTestScrollChildLayer : public CCLayerTreeHostTest, public LayerChromiumScrollDelegate {
+public:
+ CCLayerTreeHostTestScrollChildLayer()
+ : m_scrollAmount(2, 1)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setViewportSize(IntSize(10, 10), IntSize(10, 10));
+ m_layerTreeHost->rootLayer()->setBounds(IntSize(10, 10));
+
+ m_rootScrollLayer = ContentLayerChromium::create(&m_mockDelegate);
+ m_rootScrollLayer->setBounds(IntSize(10, 10));
+
+ m_rootScrollLayer->setPosition(FloatPoint(0, 0));
+ m_rootScrollLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ m_rootScrollLayer->setIsDrawable(true);
+ m_rootScrollLayer->setScrollable(true);
+ m_rootScrollLayer->setMaxScrollPosition(IntSize(100, 100));
+ m_layerTreeHost->rootLayer()->addChild(m_rootScrollLayer);
+ m_childLayer = ContentLayerChromium::create(&m_mockDelegate);
+ m_childLayer->setLayerScrollDelegate(this);
+ m_childLayer->setBounds(IntSize(50, 50));
+ m_childLayer->setIsDrawable(true);
+ m_childLayer->setScrollable(true);
+ m_childLayer->setMaxScrollPosition(IntSize(100, 100));
+
+ m_childLayer->setPosition(FloatPoint(0, 0));
+ m_childLayer->setAnchorPoint(FloatPoint(0, 0));
+
+ m_rootScrollLayer->addChild(m_childLayer);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void didScroll(const IntSize& scrollDelta) OVERRIDE
+ {
+ m_reportedScrollAmount = scrollDelta;
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float) OVERRIDE
+ {
+ IntPoint position = m_rootScrollLayer->scrollPosition();
+ m_rootScrollLayer->setScrollPosition(position + scrollDelta);
+ }
+
+ virtual void beginCommitOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ EXPECT_EQ(m_rootScrollLayer->scrollPosition(), IntPoint());
+ if (!m_layerTreeHost->commitNumber())
+ EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint());
+ else
+ EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint() + m_scrollAmount);
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ if (impl->sourceAnimationFrameNumber() == 1) {
+ EXPECT_EQ(impl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted);
+ impl->scrollBy(IntPoint(), m_scrollAmount);
+ impl->scrollEnd();
+ } else if (impl->sourceAnimationFrameNumber() == 2)
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(m_scrollAmount, m_reportedScrollAmount);
+ }
+
+private:
+ const IntSize m_scrollAmount;
+ IntSize m_reportedScrollAmount;
+ MockContentLayerDelegate m_mockDelegate;
+ RefPtr<LayerChromium> m_childLayer;
+ RefPtr<LayerChromium> m_rootScrollLayer;
+};
+
+TEST_F(CCLayerTreeHostTestScrollChildLayer, runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestCompositeAndReadbackCleanup : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestCompositeAndReadbackCleanup() { }
+
+ virtual void beginTest() OVERRIDE
+ {
+ LayerChromium* rootLayer = m_layerTreeHost->rootLayer();
+
+ OwnArrayPtr<char> pixels(adoptArrayPtr(new char[4]));
+ m_layerTreeHost->compositeAndReadback(static_cast<void*>(pixels.get()), IntRect(0, 0, 1, 1));
+ EXPECT_FALSE(rootLayer->renderSurface());
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestCompositeAndReadbackCleanup)
+
+class CCLayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit()
+ : m_rootLayer(ContentLayerChromiumWithUpdateTracking::create(&m_mockDelegate))
+ , m_surfaceLayer1(ContentLayerChromiumWithUpdateTracking::create(&m_mockDelegate))
+ , m_replicaLayer1(ContentLayerChromiumWithUpdateTracking::create(&m_mockDelegate))
+ , m_surfaceLayer2(ContentLayerChromiumWithUpdateTracking::create(&m_mockDelegate))
+ , m_replicaLayer2(ContentLayerChromiumWithUpdateTracking::create(&m_mockDelegate))
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setViewportSize(IntSize(100, 100), IntSize(100, 100));
+
+ m_rootLayer->setBounds(IntSize(100, 100));
+ m_surfaceLayer1->setBounds(IntSize(100, 100));
+ m_surfaceLayer1->setForceRenderSurface(true);
+ m_surfaceLayer1->setOpacity(0.5);
+ m_surfaceLayer2->setBounds(IntSize(100, 100));
+ m_surfaceLayer2->setForceRenderSurface(true);
+ m_surfaceLayer2->setOpacity(0.5);
+
+ m_surfaceLayer1->setReplicaLayer(m_replicaLayer1.get());
+ m_surfaceLayer2->setReplicaLayer(m_replicaLayer2.get());
+
+ m_rootLayer->addChild(m_surfaceLayer1);
+ m_surfaceLayer1->addChild(m_surfaceLayer2);
+ m_layerTreeHost->setRootLayer(m_rootLayer);
+ }
+
+ virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* hostImpl) OVERRIDE
+ {
+ CCRenderer* renderer = hostImpl->renderer();
+ unsigned surface1RenderPassId = hostImpl->rootLayer()->children()[0]->id();
+ unsigned surface2RenderPassId = hostImpl->rootLayer()->children()[0]->children()[0]->id();
+
+ switch (hostImpl->sourceFrameNumber()) {
+ case 0:
+ EXPECT_TRUE(renderer->haveCachedResourcesForRenderPassId(surface1RenderPassId));
+ EXPECT_TRUE(renderer->haveCachedResourcesForRenderPassId(surface2RenderPassId));
+
+ // Reduce the memory limit to only fit the root layer and one render surface. This
+ // prevents any contents drawing into surfaces from being allocated.
+ hostImpl->setMemoryAllocationLimitBytes(100 * 100 * 4 * 2);
+ break;
+ case 1:
+ EXPECT_FALSE(renderer->haveCachedResourcesForRenderPassId(surface1RenderPassId));
+ EXPECT_FALSE(renderer->haveCachedResourcesForRenderPassId(surface2RenderPassId));
+
+ endTest();
+ break;
+ }
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ EXPECT_EQ(2, m_rootLayer->paintContentsCount());
+ EXPECT_EQ(2, m_surfaceLayer1->paintContentsCount());
+ EXPECT_EQ(2, m_surfaceLayer2->paintContentsCount());
+
+ // Clear layer references so CCLayerTreeHost dies.
+ m_rootLayer.clear();
+ m_surfaceLayer1.clear();
+ m_replicaLayer1.clear();
+ m_surfaceLayer2.clear();
+ m_replicaLayer2.clear();
+ }
+
+private:
+ MockContentLayerDelegate m_mockDelegate;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_rootLayer;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_surfaceLayer1;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_replicaLayer1;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_surfaceLayer2;
+ RefPtr<ContentLayerChromiumWithUpdateTracking> m_replicaLayer2;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestSurfaceNotAllocatedForLayersOutsideMemoryLimit)
+
+
+class EvictionTrackingTexture : public LayerTextureUpdater::Texture {
+public:
+ static PassOwnPtr<EvictionTrackingTexture> create(PassOwnPtr<CCPrioritizedTexture> texture) { return adoptPtr(new EvictionTrackingTexture(texture)); }
+ virtual ~EvictionTrackingTexture() { }
+
+ virtual void updateRect(CCResourceProvider* resourceProvider, const IntRect&, const IntSize&) OVERRIDE
+ {
+ ASSERT_TRUE(!texture()->haveBackingTexture() || resourceProvider->numResources() > 0);
+ texture()->acquireBackingTexture(resourceProvider);
+ m_updated = true;
+ }
+ void resetUpdated() { m_updated = false; }
+ bool updated() const { return m_updated; }
+
+private:
+ explicit EvictionTrackingTexture(PassOwnPtr<CCPrioritizedTexture> texture)
+ : LayerTextureUpdater::Texture(texture)
+ , m_updated(false)
+ { }
+ bool m_updated;
+};
+
+class EvictionTestLayer : public LayerChromium {
+public:
+ static PassRefPtr<EvictionTestLayer> create() { return adoptRef(new EvictionTestLayer()); }
+
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual bool drawsContent() const OVERRIDE { return true; }
+
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+ virtual void setTexturePriorities(const CCPriorityCalculator&) OVERRIDE;
+
+ void resetUpdated()
+ {
+ if (m_texture.get())
+ m_texture->resetUpdated();
+ }
+ bool updated() const { return m_texture.get() ? m_texture->updated() : false; }
+
+private:
+ EvictionTestLayer() : LayerChromium() { }
+
+ void createTextureIfNeeded()
+ {
+ if (m_texture.get())
+ return;
+ m_texture = EvictionTrackingTexture::create(CCPrioritizedTexture::create(layerTreeHost()->contentsTextureManager()));
+ m_texture->texture()->setDimensions(WebCore::IntSize(10, 10), WebCore::GraphicsContext3D::RGBA);
+ }
+
+ OwnPtr<EvictionTrackingTexture> m_texture;
+};
+
+class EvictionTestLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<EvictionTestLayerImpl> create(int id)
+ {
+ return adoptPtr(new EvictionTestLayerImpl(id));
+ }
+ virtual ~EvictionTestLayerImpl() { }
+
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE
+ {
+ ASSERT_TRUE(m_hasTexture);
+ ASSERT_NE(0u, layerTreeHostImpl()->resourceProvider()->numResources());
+ }
+
+ void setHasTexture(bool hasTexture) { m_hasTexture = hasTexture; }
+
+private:
+ explicit EvictionTestLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_hasTexture(false) { }
+
+ bool m_hasTexture;
+};
+
+void EvictionTestLayer::setTexturePriorities(const CCPriorityCalculator&)
+{
+ createTextureIfNeeded();
+ if (!m_texture.get())
+ return;
+ m_texture->texture()->setRequestPriority(CCPriorityCalculator::uiPriority(true));
+}
+
+void EvictionTestLayer::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker*, CCRenderingStats&)
+{
+ createTextureIfNeeded();
+ if (!m_texture.get())
+ return;
+ IntRect fullRect(0, 0, 10, 10);
+ TextureUploader::Parameters parameters = { m_texture.get(), fullRect, IntSize() };
+ queue.appendFullUpload(parameters);
+}
+
+PassOwnPtr<CCLayerImpl> EvictionTestLayer::createCCLayerImpl()
+{
+ return EvictionTestLayerImpl::create(m_layerId);
+}
+
+void EvictionTestLayer::pushPropertiesTo(CCLayerImpl* layerImpl)
+{
+ LayerChromium::pushPropertiesTo(layerImpl);
+
+ EvictionTestLayerImpl* testLayerImpl = static_cast<EvictionTestLayerImpl*>(layerImpl);
+ testLayerImpl->setHasTexture(m_texture->texture()->haveBackingTexture());
+}
+
+class CCLayerTreeHostTestEvictTextures : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestEvictTextures()
+ : m_layer(EvictionTestLayer::create())
+ , m_implForEvictTextures(0)
+ , m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setRootLayer(m_layer);
+ m_layerTreeHost->setViewportSize(IntSize(10, 20), IntSize(10, 20));
+
+ WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(m_layer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 20), true);
+ }
+
+ class EvictTexturesTask : public WebKit::WebThread::Task {
+ public:
+ EvictTexturesTask(CCLayerTreeHostTestEvictTextures* test) : m_test(test) { }
+ virtual ~EvictTexturesTask() { }
+ virtual void run() OVERRIDE
+ {
+ ASSERT(m_test->m_implForEvictTextures);
+ m_test->m_implForEvictTextures->releaseContentsTextures();
+ }
+
+ private:
+ CCLayerTreeHostTestEvictTextures* m_test;
+ };
+
+ void postEvictTextures()
+ {
+ ASSERT(webThread());
+ webThread()->postTask(new EvictTexturesTask(this));
+ }
+
+ // Commit 1: Just commit and draw normally, then post an eviction at the end
+ // that will trigger a commit.
+ // Commit 2: Triggered by the eviction, let it go through and then set
+ // needsCommit.
+ // Commit 3: Triggered by the setNeedsCommit. In layout(), post an eviction
+ // task, which will be handled before the commit. Don't set needsCommit, it
+ // should have been posted. A frame should not be drawn (note,
+ // didCommitAndDrawFrame may be called anyway).
+ // Commit 4: Triggered by the eviction, let it go through and then set
+ // needsCommit.
+ // Commit 5: Triggered by the setNeedsCommit, post an eviction task in
+ // layout(), a frame should not be drawn but a commit will be posted.
+ // Commit 6: Triggered by the eviction, post an eviction task in
+ // layout(), which will be a noop, letting the commit (which recreates the
+ // textures) go through and draw a frame, then end the test.
+ //
+ // Commits 1+2 test the eviction recovery path where eviction happens outside
+ // of the beginFrame/commit pair.
+ // Commits 3+4 test the eviction recovery path where eviction happens inside
+ // the beginFrame/commit pair.
+ // Commits 5+6 test the path where an eviction happens during the eviction
+ // recovery path.
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ switch (m_numCommits) {
+ case 1:
+ EXPECT_TRUE(m_layer->updated());
+ postEvictTextures();
+ break;
+ case 2:
+ EXPECT_TRUE(m_layer->updated());
+ m_layerTreeHost->setNeedsCommit();
+ break;
+ case 3:
+ break;
+ case 4:
+ EXPECT_TRUE(m_layer->updated());
+ m_layerTreeHost->setNeedsCommit();
+ break;
+ case 5:
+ break;
+ case 6:
+ EXPECT_TRUE(m_layer->updated());
+ endTest();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ m_implForEvictTextures = impl;
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ ++m_numCommits;
+ switch (m_numCommits) {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ postEvictTextures();
+ break;
+ case 4:
+ // We couldn't check in didCommitAndDrawFrame on commit 3, so check here.
+ EXPECT_FALSE(m_layer->updated());
+ break;
+ case 5:
+ postEvictTextures();
+ break;
+ case 6:
+ // We couldn't check in didCommitAndDrawFrame on commit 5, so check here.
+ EXPECT_FALSE(m_layer->updated());
+ postEvictTextures();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ m_layer->resetUpdated();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ MockContentLayerDelegate m_delegate;
+ RefPtr<EvictionTestLayer> m_layer;
+ CCLayerTreeHostImpl* m_implForEvictTextures;
+ int m_numCommits;
+};
+
+TEST_F(CCLayerTreeHostTestEvictTextures, runMultiThread)
+{
+ runTest(true);
+}
+
+class CCLayerTreeHostTestLostContextAfterEvictTextures : public CCLayerTreeHostTest {
+public:
+ CCLayerTreeHostTestLostContextAfterEvictTextures()
+ : m_layer(EvictionTestLayer::create())
+ , m_implForEvictTextures(0)
+ , m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->setRootLayer(m_layer);
+ m_layerTreeHost->setViewportSize(IntSize(10, 20), IntSize(10, 20));
+
+ WebTransformationMatrix identityMatrix;
+ setLayerPropertiesForTesting(m_layer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 20), true);
+ }
+
+ class EvictTexturesTask : public WebKit::WebThread::Task {
+ public:
+ EvictTexturesTask(CCLayerTreeHostTestLostContextAfterEvictTextures* test) : m_test(test) { }
+ virtual ~EvictTexturesTask() { }
+ virtual void run() OVERRIDE
+ {
+ m_test->evictTexturesOnImplThread();
+ }
+
+ private:
+ CCLayerTreeHostTestLostContextAfterEvictTextures* m_test;
+ };
+
+ void postEvictTextures()
+ {
+ if (webThread())
+ webThread()->postTask(new EvictTexturesTask(this));
+ else {
+ DebugScopedSetImplThread impl;
+ evictTexturesOnImplThread();
+ }
+ }
+
+ void evictTexturesOnImplThread()
+ {
+ ASSERT(m_implForEvictTextures);
+ m_implForEvictTextures->releaseContentsTextures();
+ }
+
+ // Commit 1: Just commit and draw normally, then at the end, set ourselves
+ // invisible (to prevent a commit that would recreate textures after
+ // eviction, before the context recovery), and post a task that will evict
+ // textures, then cause the context to be lost, and then set ourselves
+ // visible again (to allow commits, since that's what causes context
+ // recovery in single thread).
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ ++m_numCommits;
+ switch (m_numCommits) {
+ case 1:
+ EXPECT_TRUE(m_layer->updated());
+ m_layerTreeHost->setVisible(false);
+ postEvictTextures();
+ m_layerTreeHost->loseContext(1);
+ m_layerTreeHost->setVisible(true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE
+ {
+ m_implForEvictTextures = impl;
+ }
+
+ virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE
+ {
+ EXPECT_TRUE(succeeded);
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ MockContentLayerDelegate m_delegate;
+ RefPtr<EvictionTestLayer> m_layer;
+ CCLayerTreeHostImpl* m_implForEvictTextures;
+ int m_numCommits;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLostContextAfterEvictTextures)
+
+} // namespace
diff --git a/cc/CCMathUtil.cpp b/cc/CCMathUtil.cpp
new file mode 100644
index 0000000..c8969a0
--- /dev/null
+++ b/cc/CCMathUtil.cpp
@@ -0,0 +1,379 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCMathUtil.h"
+
+#include "FloatPoint.h"
+#include "FloatQuad.h"
+#include "IntRect.h"
+#include <public/WebTransformationMatrix.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+static HomogeneousCoordinate projectHomogeneousPoint(const WebTransformationMatrix& transform, const FloatPoint& p)
+{
+ // In this case, the layer we are trying to project onto is perpendicular to ray
+ // (point p and z-axis direction) that we are trying to project. This happens when the
+ // layer is rotated so that it is infinitesimally thin, or when it is co-planar with
+ // the camera origin -- i.e. when the layer is invisible anyway.
+ if (!transform.m33())
+ return HomogeneousCoordinate(0, 0, 0, 1);
+
+ double x = p.x();
+ double y = p.y();
+ double z = -(transform.m13() * x + transform.m23() * y + transform.m43()) / transform.m33();
+ // implicit definition of w = 1;
+
+ double outX = x * transform.m11() + y * transform.m21() + z * transform.m31() + transform.m41();
+ double outY = x * transform.m12() + y * transform.m22() + z * transform.m32() + transform.m42();
+ double outZ = x * transform.m13() + y * transform.m23() + z * transform.m33() + transform.m43();
+ double outW = x * transform.m14() + y * transform.m24() + z * transform.m34() + transform.m44();
+
+ return HomogeneousCoordinate(outX, outY, outZ, outW);
+}
+
+static HomogeneousCoordinate mapHomogeneousPoint(const WebTransformationMatrix& transform, const FloatPoint3D& p)
+{
+ double x = p.x();
+ double y = p.y();
+ double z = p.z();
+ // implicit definition of w = 1;
+
+ double outX = x * transform.m11() + y * transform.m21() + z * transform.m31() + transform.m41();
+ double outY = x * transform.m12() + y * transform.m22() + z * transform.m32() + transform.m42();
+ double outZ = x * transform.m13() + y * transform.m23() + z * transform.m33() + transform.m43();
+ double outW = x * transform.m14() + y * transform.m24() + z * transform.m34() + transform.m44();
+
+ return HomogeneousCoordinate(outX, outY, outZ, outW);
+}
+
+static HomogeneousCoordinate computeClippedPointForEdge(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2)
+{
+ // Points h1 and h2 form a line in 4d, and any point on that line can be represented
+ // as an interpolation between h1 and h2:
+ // p = (1-t) h1 + (t) h2
+ //
+ // We want to compute point p such that p.w == epsilon, where epsilon is a small
+ // non-zero number. (but the smaller the number is, the higher the risk of overflow)
+ // To do this, we solve for t in the following equation:
+ // p.w = epsilon = (1-t) * h1.w + (t) * h2.w
+ //
+ // Once paramter t is known, the rest of p can be computed via p = (1-t) h1 + (t) h2.
+
+ // Technically this is a special case of the following assertion, but its a good idea to keep it an explicit sanity check here.
+ ASSERT(h2.w != h1.w);
+ // Exactly one of h1 or h2 (but not both) must be on the negative side of the w plane when this is called.
+ ASSERT(h1.shouldBeClipped() ^ h2.shouldBeClipped());
+
+ double w = 0.00001; // or any positive non-zero small epsilon
+
+ double t = (w - h1.w) / (h2.w - h1.w);
+
+ double x = (1-t) * h1.x + t * h2.x;
+ double y = (1-t) * h1.y + t * h2.y;
+ double z = (1-t) * h1.z + t * h2.z;
+
+ return HomogeneousCoordinate(x, y, z, w);
+}
+
+static inline void expandBoundsToIncludePoint(float& xmin, float& xmax, float& ymin, float& ymax, const FloatPoint& p)
+{
+ xmin = std::min(p.x(), xmin);
+ xmax = std::max(p.x(), xmax);
+ ymin = std::min(p.y(), ymin);
+ ymax = std::max(p.y(), ymax);
+}
+
+static inline void addVertexToClippedQuad(const FloatPoint& newVertex, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad)
+{
+ clippedQuad[numVerticesInClippedQuad] = newVertex;
+ numVerticesInClippedQuad++;
+}
+
+IntRect CCMathUtil::mapClippedRect(const WebTransformationMatrix& transform, const IntRect& srcRect)
+{
+ return enclosingIntRect(mapClippedRect(transform, FloatRect(srcRect)));
+}
+
+FloatRect CCMathUtil::mapClippedRect(const WebTransformationMatrix& transform, const FloatRect& srcRect)
+{
+ if (transform.isIdentityOrTranslation()) {
+ FloatRect mappedRect(srcRect);
+ mappedRect.move(static_cast<float>(transform.m41()), static_cast<float>(transform.m42()));
+ return mappedRect;
+ }
+
+ // Apply the transform, but retain the result in homogeneous coordinates.
+ FloatQuad q = FloatQuad(FloatRect(srcRect));
+ HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, q.p1());
+ HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, q.p2());
+ HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, q.p3());
+ HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, q.p4());
+
+ return computeEnclosingClippedRect(h1, h2, h3, h4);
+}
+
+FloatRect CCMathUtil::projectClippedRect(const WebTransformationMatrix& transform, const FloatRect& srcRect)
+{
+ // Perform the projection, but retain the result in homogeneous coordinates.
+ FloatQuad q = FloatQuad(FloatRect(srcRect));
+ HomogeneousCoordinate h1 = projectHomogeneousPoint(transform, q.p1());
+ HomogeneousCoordinate h2 = projectHomogeneousPoint(transform, q.p2());
+ HomogeneousCoordinate h3 = projectHomogeneousPoint(transform, q.p3());
+ HomogeneousCoordinate h4 = projectHomogeneousPoint(transform, q.p4());
+
+ return computeEnclosingClippedRect(h1, h2, h3, h4);
+}
+
+void CCMathUtil::mapClippedQuad(const WebTransformationMatrix& transform, const FloatQuad& srcQuad, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad)
+{
+ HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, srcQuad.p1());
+ HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, srcQuad.p2());
+ HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, srcQuad.p3());
+ HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, srcQuad.p4());
+
+ // The order of adding the vertices to the array is chosen so that clockwise / counter-clockwise orientation is retained.
+
+ numVerticesInClippedQuad = 0;
+
+ if (!h1.shouldBeClipped())
+ addVertexToClippedQuad(h1.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (h1.shouldBeClipped() ^ h2.shouldBeClipped())
+ addVertexToClippedQuad(computeClippedPointForEdge(h1, h2).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (!h2.shouldBeClipped())
+ addVertexToClippedQuad(h2.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (h2.shouldBeClipped() ^ h3.shouldBeClipped())
+ addVertexToClippedQuad(computeClippedPointForEdge(h2, h3).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (!h3.shouldBeClipped())
+ addVertexToClippedQuad(h3.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (h3.shouldBeClipped() ^ h4.shouldBeClipped())
+ addVertexToClippedQuad(computeClippedPointForEdge(h3, h4).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (!h4.shouldBeClipped())
+ addVertexToClippedQuad(h4.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ if (h4.shouldBeClipped() ^ h1.shouldBeClipped())
+ addVertexToClippedQuad(computeClippedPointForEdge(h4, h1).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+ ASSERT(numVerticesInClippedQuad <= 8);
+}
+
+FloatRect CCMathUtil::computeEnclosingRectOfVertices(FloatPoint vertices[], int numVertices)
+{
+ if (numVertices < 2)
+ return FloatRect();
+
+ float xmin = std::numeric_limits<float>::max();
+ float xmax = -std::numeric_limits<float>::max();
+ float ymin = std::numeric_limits<float>::max();
+ float ymax = -std::numeric_limits<float>::max();
+
+ for (int i = 0; i < numVertices; ++i)
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, vertices[i]);
+
+ return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin));
+}
+
+FloatRect CCMathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4)
+{
+ // This function performs clipping as necessary and computes the enclosing 2d
+ // FloatRect of the vertices. Doing these two steps simultaneously allows us to avoid
+ // the overhead of storing an unknown number of clipped vertices.
+
+ // If no vertices on the quad are clipped, then we can simply return the enclosing rect directly.
+ bool somethingClipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped();
+ if (!somethingClipped) {
+ FloatQuad mappedQuad = FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
+ return mappedQuad.boundingBox();
+ }
+
+ bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3.shouldBeClipped() && h4.shouldBeClipped();
+ if (everythingClipped)
+ return FloatRect();
+
+
+ float xmin = std::numeric_limits<float>::max();
+ float xmax = -std::numeric_limits<float>::max();
+ float ymin = std::numeric_limits<float>::max();
+ float ymax = -std::numeric_limits<float>::max();
+
+ if (!h1.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h1.cartesianPoint2d());
+
+ if (h1.shouldBeClipped() ^ h2.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h1, h2).cartesianPoint2d());
+
+ if (!h2.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h2.cartesianPoint2d());
+
+ if (h2.shouldBeClipped() ^ h3.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h2, h3).cartesianPoint2d());
+
+ if (!h3.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h3.cartesianPoint2d());
+
+ if (h3.shouldBeClipped() ^ h4.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h3, h4).cartesianPoint2d());
+
+ if (!h4.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, h4.cartesianPoint2d());
+
+ if (h4.shouldBeClipped() ^ h1.shouldBeClipped())
+ expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, computeClippedPointForEdge(h4, h1).cartesianPoint2d());
+
+ return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin));
+}
+
+FloatQuad CCMathUtil::mapQuad(const WebTransformationMatrix& transform, const FloatQuad& q, bool& clipped)
+{
+ if (transform.isIdentityOrTranslation()) {
+ FloatQuad mappedQuad(q);
+ mappedQuad.move(static_cast<float>(transform.m41()), static_cast<float>(transform.m42()));
+ clipped = false;
+ return mappedQuad;
+ }
+
+ HomogeneousCoordinate h1 = mapHomogeneousPoint(transform, q.p1());
+ HomogeneousCoordinate h2 = mapHomogeneousPoint(transform, q.p2());
+ HomogeneousCoordinate h3 = mapHomogeneousPoint(transform, q.p3());
+ HomogeneousCoordinate h4 = mapHomogeneousPoint(transform, q.p4());
+
+ clipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped();
+
+ // Result will be invalid if clipped == true. But, compute it anyway just in case, to emulate existing behavior.
+ return FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
+}
+
+FloatPoint CCMathUtil::mapPoint(const WebTransformationMatrix& transform, const FloatPoint& p, bool& clipped)
+{
+ HomogeneousCoordinate h = mapHomogeneousPoint(transform, p);
+
+ if (h.w > 0) {
+ clipped = false;
+ return h.cartesianPoint2d();
+ }
+
+ // The cartesian coordinates will be invalid after dividing by w.
+ clipped = true;
+
+ // Avoid dividing by w if w == 0.
+ if (!h.w)
+ return FloatPoint();
+
+ // This return value will be invalid because clipped == true, but (1) users of this
+ // code should be ignoring the return value when clipped == true anyway, and (2) this
+ // behavior is more consistent with existing behavior of WebKit transforms if the user
+ // really does not ignore the return value.
+ return h.cartesianPoint2d();
+}
+
+FloatPoint3D CCMathUtil::mapPoint(const WebTransformationMatrix& transform, const FloatPoint3D& p, bool& clipped)
+{
+ HomogeneousCoordinate h = mapHomogeneousPoint(transform, p);
+
+ if (h.w > 0) {
+ clipped = false;
+ return h.cartesianPoint3d();
+ }
+
+ // The cartesian coordinates will be invalid after dividing by w.
+ clipped = true;
+
+ // Avoid dividing by w if w == 0.
+ if (!h.w)
+ return FloatPoint3D();
+
+ // This return value will be invalid because clipped == true, but (1) users of this
+ // code should be ignoring the return value when clipped == true anyway, and (2) this
+ // behavior is more consistent with existing behavior of WebKit transforms if the user
+ // really does not ignore the return value.
+ return h.cartesianPoint3d();
+}
+
+FloatQuad CCMathUtil::projectQuad(const WebTransformationMatrix& transform, const FloatQuad& q, bool& clipped)
+{
+ FloatQuad projectedQuad;
+ bool clippedPoint;
+ projectedQuad.setP1(projectPoint(transform, q.p1(), clippedPoint));
+ clipped = clippedPoint;
+ projectedQuad.setP2(projectPoint(transform, q.p2(), clippedPoint));
+ clipped |= clippedPoint;
+ projectedQuad.setP3(projectPoint(transform, q.p3(), clippedPoint));
+ clipped |= clippedPoint;
+ projectedQuad.setP4(projectPoint(transform, q.p4(), clippedPoint));
+ clipped |= clippedPoint;
+
+ return projectedQuad;
+}
+
+FloatPoint CCMathUtil::projectPoint(const WebTransformationMatrix& transform, const FloatPoint& p, bool& clipped)
+{
+ HomogeneousCoordinate h = projectHomogeneousPoint(transform, p);
+
+ if (h.w > 0) {
+ // The cartesian coordinates will be valid in this case.
+ clipped = false;
+ return h.cartesianPoint2d();
+ }
+
+ // The cartesian coordinates will be invalid after dividing by w.
+ clipped = true;
+
+ // Avoid dividing by w if w == 0.
+ if (!h.w)
+ return FloatPoint();
+
+ // This return value will be invalid because clipped == true, but (1) users of this
+ // code should be ignoring the return value when clipped == true anyway, and (2) this
+ // behavior is more consistent with existing behavior of WebKit transforms if the user
+ // really does not ignore the return value.
+ return h.cartesianPoint2d();
+}
+
+void CCMathUtil::flattenTransformTo2d(WebTransformationMatrix& transform)
+{
+ // Set both the 3rd row and 3rd column to (0, 0, 1, 0).
+ //
+ // One useful interpretation of doing this operation:
+ // - For x and y values, the new transform behaves effectively like an orthographic
+ // projection was added to the matrix sequence.
+ // - For z values, the new transform overrides any effect that the transform had on
+ // z, and instead it preserves the z value for any points that are transformed.
+ // - Because of linearity of transforms, this flattened transform also preserves the
+ // effect that any subsequent (post-multiplied) transforms would have on z values.
+ //
+ transform.setM13(0);
+ transform.setM23(0);
+ transform.setM31(0);
+ transform.setM32(0);
+ transform.setM33(1);
+ transform.setM34(0);
+ transform.setM43(0);
+}
+
+float CCMathUtil::smallestAngleBetweenVectors(const FloatSize& v1, const FloatSize& v2)
+{
+ float dotProduct = (v1.width() * v2.width() + v1.height() * v2.height()) / (v1.diagonalLength() * v2.diagonalLength());
+ // Clamp to compensate for rounding errors.
+ dotProduct = std::max(-1.f, std::min(1.f, dotProduct));
+ return rad2deg(acosf(dotProduct));
+}
+
+FloatSize CCMathUtil::projectVector(const FloatSize& source, const FloatSize& destination)
+{
+ float sourceDotDestination = source.width() * destination.width() + source.height() * destination.height();
+ float projectedLength = sourceDotDestination / destination.diagonalLengthSquared();
+ return FloatSize(projectedLength * destination.width(), projectedLength * destination.height());
+}
+
+} // namespace WebCore
diff --git a/cc/CCMathUtil.h b/cc/CCMathUtil.h
new file mode 100644
index 0000000..834acf5
--- /dev/null
+++ b/cc/CCMathUtil.h
@@ -0,0 +1,109 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCMathUtil_h
+#define CCMathUtil_h
+
+#include "FloatPoint.h"
+#include "FloatPoint3D.h"
+
+namespace WebKit {
+class WebTransformationMatrix;
+}
+
+namespace WebCore {
+
+class IntRect;
+class FloatRect;
+class FloatQuad;
+
+struct HomogeneousCoordinate {
+ HomogeneousCoordinate(double newX, double newY, double newZ, double newW)
+ : x(newX)
+ , y(newY)
+ , z(newZ)
+ , w(newW)
+ {
+ }
+
+ bool shouldBeClipped() const
+ {
+ return w <= 0;
+ }
+
+ FloatPoint cartesianPoint2d() const
+ {
+ if (w == 1)
+ return FloatPoint(x, y);
+
+ // For now, because this code is used privately only by CCMathUtil, it should never be called when w == 0, and we do not yet need to handle that case.
+ ASSERT(w);
+ double invW = 1.0 / w;
+ return FloatPoint(x * invW, y * invW);
+ }
+
+ FloatPoint3D cartesianPoint3d() const
+ {
+ if (w == 1)
+ return FloatPoint3D(x, y, z);
+
+ // For now, because this code is used privately only by CCMathUtil, it should never be called when w == 0, and we do not yet need to handle that case.
+ ASSERT(w);
+ double invW = 1.0 / w;
+ return FloatPoint3D(x * invW, y * invW, z * invW);
+ }
+
+ double x;
+ double y;
+ double z;
+ double w;
+};
+
+// This class contains math helper functionality that does not belong in WebCore.
+// It is possible that this functionality should be migrated to WebCore eventually.
+class CCMathUtil {
+public:
+
+ // Background: WebTransformationMatrix code in WebCore does not do the right thing in
+ // mapRect / mapQuad / projectQuad when there is a perspective projection that causes
+ // one of the transformed vertices to go to w < 0. In those cases, it is necessary to
+ // perform clipping in homogeneous coordinates, after applying the transform, before
+ // dividing-by-w to convert to cartesian coordinates.
+ //
+ // These functions return the axis-aligned rect that encloses the correctly clipped,
+ // transformed polygon.
+ static IntRect mapClippedRect(const WebKit::WebTransformationMatrix&, const IntRect&);
+ static FloatRect mapClippedRect(const WebKit::WebTransformationMatrix&, const FloatRect&);
+ static FloatRect projectClippedRect(const WebKit::WebTransformationMatrix&, const FloatRect&);
+
+ // Returns an array of vertices that represent the clipped polygon. After returning, indexes from
+ // 0 to numVerticesInClippedQuad are valid in the clippedQuad array. Note that
+ // numVerticesInClippedQuad may be zero, which means the entire quad was clipped, and
+ // none of the vertices in the array are valid.
+ static void mapClippedQuad(const WebKit::WebTransformationMatrix&, const FloatQuad& srcQuad, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad);
+
+ static FloatRect computeEnclosingRectOfVertices(FloatPoint vertices[], int numVertices);
+ static FloatRect computeEnclosingClippedRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4);
+
+ // NOTE: These functions do not do correct clipping against w = 0 plane, but they
+ // correctly detect the clipped condition via the boolean clipped.
+ static FloatQuad mapQuad(const WebKit::WebTransformationMatrix&, const FloatQuad&, bool& clipped);
+ static FloatPoint mapPoint(const WebKit::WebTransformationMatrix&, const FloatPoint&, bool& clipped);
+ static FloatPoint3D mapPoint(const WebKit::WebTransformationMatrix&, const FloatPoint3D&, bool& clipped);
+ static FloatQuad projectQuad(const WebKit::WebTransformationMatrix&, const FloatQuad&, bool& clipped);
+ static FloatPoint projectPoint(const WebKit::WebTransformationMatrix&, const FloatPoint&, bool& clipped);
+
+ static void flattenTransformTo2d(WebKit::WebTransformationMatrix&);
+
+ // Returns the smallest angle between the given two vectors in degrees. Neither vector is
+ // assumed to be normalized.
+ static float smallestAngleBetweenVectors(const FloatSize&, const FloatSize&);
+
+ // Projects the |source| vector onto |destination|. Neither vector is assumed to be normalized.
+ static FloatSize projectVector(const FloatSize& source, const FloatSize& destination);
+};
+
+} // namespace WebCore
+
+#endif // #define CCMathUtil_h
diff --git a/cc/CCMathUtilTest.cpp b/cc/CCMathUtilTest.cpp
new file mode 100644
index 0000000..e56fa3c
--- /dev/null
+++ b/cc/CCMathUtilTest.cpp
@@ -0,0 +1,182 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCMathUtil.h"
+
+#include "CCLayerTreeTestCommon.h"
+#include "FloatRect.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+TEST(CCMathUtilTest, verifyBackfaceVisibilityBasicCases)
+{
+ WebTransformationMatrix transform;
+
+ transform.makeIdentity();
+ EXPECT_FALSE(transform.isBackFaceVisible());
+
+ transform.makeIdentity();
+ transform.rotate3d(0, 80, 0);
+ EXPECT_FALSE(transform.isBackFaceVisible());
+
+ transform.makeIdentity();
+ transform.rotate3d(0, 100, 0);
+ EXPECT_TRUE(transform.isBackFaceVisible());
+
+ // Edge case, 90 degree rotation should return false.
+ transform.makeIdentity();
+ transform.rotate3d(0, 90, 0);
+ EXPECT_FALSE(transform.isBackFaceVisible());
+}
+
+TEST(CCMathUtilTest, verifyBackfaceVisibilityForPerspective)
+{
+ WebTransformationMatrix layerSpaceToProjectionPlane;
+
+ // This tests if isBackFaceVisible works properly under perspective transforms.
+ // Specifically, layers that may have their back face visible in orthographic
+ // projection, may not actually have back face visible under perspective projection.
+
+ // Case 1: Layer is rotated by slightly more than 90 degrees, at the center of the
+ // prespective projection. In this case, the layer's back-side is visible to
+ // the camera.
+ layerSpaceToProjectionPlane.makeIdentity();
+ layerSpaceToProjectionPlane.applyPerspective(1);
+ layerSpaceToProjectionPlane.translate3d(0, 0, 0);
+ layerSpaceToProjectionPlane.rotate3d(0, 100, 0);
+ EXPECT_TRUE(layerSpaceToProjectionPlane.isBackFaceVisible());
+
+ // Case 2: Layer is rotated by slightly more than 90 degrees, but shifted off to the
+ // side of the camera. Because of the wide field-of-view, the layer's front
+ // side is still visible.
+ //
+ // |<-- front side of layer is visible to perspective camera
+ // \ | /
+ // \ | /
+ // \| /
+ // | /
+ // |\ /<-- camera field of view
+ // | \ /
+ // back side of layer -->| \ /
+ // \./ <-- camera origin
+ //
+ layerSpaceToProjectionPlane.makeIdentity();
+ layerSpaceToProjectionPlane.applyPerspective(1);
+ layerSpaceToProjectionPlane.translate3d(-10, 0, 0);
+ layerSpaceToProjectionPlane.rotate3d(0, 100, 0);
+ EXPECT_FALSE(layerSpaceToProjectionPlane.isBackFaceVisible());
+
+ // Case 3: Additionally rotating the layer by 180 degrees should of course show the
+ // opposite result of case 2.
+ layerSpaceToProjectionPlane.rotate3d(0, 180, 0);
+ EXPECT_TRUE(layerSpaceToProjectionPlane.isBackFaceVisible());
+}
+
+TEST(CCMathUtilTest, verifyProjectionOfPerpendicularPlane)
+{
+ // In this case, the m33() element of the transform becomes zero, which could cause a
+ // divide-by-zero when projecting points/quads.
+
+ WebTransformationMatrix transform;
+ transform.makeIdentity();
+ transform.setM33(0);
+
+ FloatRect rect = FloatRect(0, 0, 1, 1);
+ FloatRect projectedRect = CCMathUtil::projectClippedRect(transform, rect);
+
+ EXPECT_EQ(0, projectedRect.x());
+ EXPECT_EQ(0, projectedRect.y());
+ EXPECT_TRUE(projectedRect.isEmpty());
+}
+
+TEST(CCMathUtilTest, verifyEnclosingClippedRectUsesCorrectInitialBounds)
+{
+ HomogeneousCoordinate h1(-100, -100, 0, 1);
+ HomogeneousCoordinate h2(-10, -10, 0, 1);
+ HomogeneousCoordinate h3(10, 10, 0, -1);
+ HomogeneousCoordinate h4(100, 100, 0, -1);
+
+ // The bounds of the enclosing clipped rect should be -100 to -10 for both x and y.
+ // However, if there is a bug where the initial xmin/xmax/ymin/ymax are initialized to
+ // numeric_limits<float>::min() (which is zero, not -flt_max) then the enclosing
+ // clipped rect will be computed incorrectly.
+ FloatRect result = CCMathUtil::computeEnclosingClippedRect(h1, h2, h3, h4);
+
+ EXPECT_FLOAT_RECT_EQ(FloatRect(FloatPoint(-100, -100), FloatSize(90, 90)), result);
+}
+
+TEST(CCMathUtilTest, verifyEnclosingRectOfVerticesUsesCorrectInitialBounds)
+{
+ FloatPoint vertices[3];
+ int numVertices = 3;
+
+ vertices[0] = FloatPoint(-10, -100);
+ vertices[1] = FloatPoint(-100, -10);
+ vertices[2] = FloatPoint(-30, -30);
+
+ // The bounds of the enclosing rect should be -100 to -10 for both x and y. However,
+ // if there is a bug where the initial xmin/xmax/ymin/ymax are initialized to
+ // numeric_limits<float>::min() (which is zero, not -flt_max) then the enclosing
+ // clipped rect will be computed incorrectly.
+ FloatRect result = CCMathUtil::computeEnclosingRectOfVertices(vertices, numVertices);
+
+ EXPECT_FLOAT_RECT_EQ(FloatRect(FloatPoint(-100, -100), FloatSize(90, 90)), result);
+}
+
+TEST(CCMathUtilTest, smallestAngleBetweenVectors)
+{
+ FloatSize x(1, 0);
+ FloatSize y(0, 1);
+ FloatSize testVector(0.5, 0.5);
+
+ // Orthogonal vectors are at an angle of 90 degress.
+ EXPECT_EQ(90, CCMathUtil::smallestAngleBetweenVectors(x, y));
+
+ // A vector makes a zero angle with itself.
+ EXPECT_EQ(0, CCMathUtil::smallestAngleBetweenVectors(x, x));
+ EXPECT_EQ(0, CCMathUtil::smallestAngleBetweenVectors(y, y));
+ EXPECT_EQ(0, CCMathUtil::smallestAngleBetweenVectors(testVector, testVector));
+
+ // Parallel but reversed vectors are at 180 degrees.
+ EXPECT_FLOAT_EQ(180, CCMathUtil::smallestAngleBetweenVectors(x, -x));
+ EXPECT_FLOAT_EQ(180, CCMathUtil::smallestAngleBetweenVectors(y, -y));
+ EXPECT_FLOAT_EQ(180, CCMathUtil::smallestAngleBetweenVectors(testVector, -testVector));
+
+ // The test vector is at a known angle.
+ EXPECT_FLOAT_EQ(45, floor(CCMathUtil::smallestAngleBetweenVectors(testVector, x)));
+ EXPECT_FLOAT_EQ(45, floor(CCMathUtil::smallestAngleBetweenVectors(testVector, y)));
+}
+
+TEST(CCMathUtilTest, vectorProjection)
+{
+ FloatSize x(1, 0);
+ FloatSize y(0, 1);
+ FloatSize testVector(0.3f, 0.7f);
+
+ // Orthogonal vectors project to a zero vector.
+ EXPECT_EQ(FloatSize(0, 0), CCMathUtil::projectVector(x, y));
+ EXPECT_EQ(FloatSize(0, 0), CCMathUtil::projectVector(y, x));
+
+ // Projecting a vector onto the orthonormal basis gives the corresponding component of the
+ // vector.
+ EXPECT_EQ(FloatSize(testVector.width(), 0), CCMathUtil::projectVector(testVector, x));
+ EXPECT_EQ(FloatSize(0, testVector.height()), CCMathUtil::projectVector(testVector, y));
+
+ // Finally check than an arbitrary vector projected to another one gives a vector parallel to
+ // the second vector.
+ FloatSize targetVector(0.5, 0.2f);
+ FloatSize projectedVector = CCMathUtil::projectVector(testVector, targetVector);
+ EXPECT_EQ(projectedVector.width() / targetVector.width(),
+ projectedVector.height() / targetVector.height());
+}
+
+} // namespace
diff --git a/cc/CCOcclusionTracker.cpp b/cc/CCOcclusionTracker.cpp
new file mode 100644
index 0000000..2bd1181
--- /dev/null
+++ b/cc/CCOcclusionTracker.cpp
@@ -0,0 +1,482 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCOcclusionTracker.h"
+
+#include "CCLayerImpl.h"
+#include "CCMathUtil.h"
+#include "CCOverdrawMetrics.h"
+#include "LayerChromium.h"
+#include <algorithm>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+template<typename LayerType, typename RenderSurfaceType>
+CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::CCOcclusionTrackerBase(IntRect rootTargetRect, bool recordMetricsForFrame)
+ : m_rootTargetRect(rootTargetRect)
+ , m_overdrawMetrics(CCOverdrawMetrics::create(recordMetricsForFrame))
+ , m_occludingScreenSpaceRects(0)
+{
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterLayer(const CCLayerIteratorPosition<LayerType>& layerIterator)
+{
+ LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer;
+
+ if (layerIterator.representsItself)
+ enterRenderTarget(renderTarget);
+ else if (layerIterator.representsTargetRenderSurface)
+ finishedRenderTarget(renderTarget);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveLayer(const CCLayerIteratorPosition<LayerType>& layerIterator)
+{
+ LayerType* renderTarget = layerIterator.targetRenderSurfaceLayer;
+
+ if (layerIterator.representsItself)
+ markOccludedBehindLayer(layerIterator.currentLayer);
+ else if (layerIterator.representsContributingRenderSurface)
+ leaveToRenderTarget(renderTarget);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterRenderTarget(const LayerType* newTarget)
+{
+ if (!m_stack.isEmpty() && m_stack.last().target == newTarget)
+ return;
+
+ const LayerType* oldTarget = m_stack.isEmpty() ? 0 : m_stack.last().target;
+ const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->renderSurface()->nearestAncestorThatMovesPixels();
+ const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->renderSurface()->nearestAncestorThatMovesPixels();
+
+ m_stack.append(StackObject(newTarget));
+
+ // We copy the screen occlusion into the new RenderSurface subtree, but we never copy in the
+ // target occlusion, since we are looking at a new RenderSurface target.
+
+ // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed
+ // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus
+ // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible.
+ bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels;
+
+ bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels;
+ if (copyScreenOcclusionForward) {
+ int lastIndex = m_stack.size() - 1;
+ m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen;
+ }
+}
+
+static inline bool layerOpacityKnown(const LayerChromium* layer) { return !layer->drawOpacityIsAnimating(); }
+static inline bool layerOpacityKnown(const CCLayerImpl*) { return true; }
+static inline bool layerTransformsToTargetKnown(const LayerChromium* layer) { return !layer->drawTransformIsAnimating(); }
+static inline bool layerTransformsToTargetKnown(const CCLayerImpl*) { return true; }
+static inline bool layerTransformsToScreenKnown(const LayerChromium* layer) { return !layer->screenSpaceTransformIsAnimating(); }
+static inline bool layerTransformsToScreenKnown(const CCLayerImpl*) { return true; }
+
+static inline bool surfaceOpacityKnown(const RenderSurfaceChromium* surface) { return !surface->drawOpacityIsAnimating(); }
+static inline bool surfaceOpacityKnown(const CCRenderSurface*) { return true; }
+static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceChromium* surface) { return !surface->targetSurfaceTransformsAreAnimating(); }
+static inline bool surfaceTransformsToTargetKnown(const CCRenderSurface*) { return true; }
+static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceChromium* surface) { return !surface->screenSpaceTransformsAreAnimating(); }
+static inline bool surfaceTransformsToScreenKnown(const CCRenderSurface*) { return true; }
+
+static inline bool layerIsInUnsorted3dRenderingContext(const LayerChromium* layer) { return layer->parent() && layer->parent()->preserves3D(); }
+static inline bool layerIsInUnsorted3dRenderingContext(const CCLayerImpl*) { return false; }
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedRenderTarget(const LayerType* finishedTarget)
+{
+ // Make sure we know about the target surface.
+ enterRenderTarget(finishedTarget);
+
+ RenderSurfaceType* surface = finishedTarget->renderSurface();
+
+ // If the occlusion within the surface can not be applied to things outside of the surface's subtree, then clear the occlusion here so it won't be used.
+ if (finishedTarget->maskLayer() || !surfaceOpacityKnown(surface) || surface->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
+ m_stack.last().occlusionInScreen = Region();
+ m_stack.last().occlusionInTarget = Region();
+ } else {
+ if (!surfaceTransformsToTargetKnown(surface))
+ m_stack.last().occlusionInTarget = Region();
+ if (!surfaceTransformsToScreenKnown(surface))
+ m_stack.last().occlusionInScreen = Region();
+ }
+}
+
+template<typename RenderSurfaceType>
+static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const WebTransformationMatrix& transform)
+{
+ // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then
+ // apply |transform| to each rect within |region| in order to transform the entire Region.
+
+ bool clipped;
+ FloatQuad transformedBoundsQuad = CCMathUtil::mapQuad(transform, FloatQuad(region.bounds()), clipped);
+ // FIXME: Find a rect interior to each transformed quad.
+ if (clipped || !transformedBoundsQuad.isRectilinear())
+ return Region();
+
+ Region transformedRegion;
+
+ Vector<IntRect> rects = region.rects();
+ for (size_t i = 0; i < rects.size(); ++i) {
+ // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further.
+ IntRect transformedRect = enclosedIntRect(CCMathUtil::mapClippedRect(transform, FloatRect(rects[i])));
+ if (!surface->clipRect().isEmpty())
+ transformedRect.intersect(surface->clipRect());
+ transformedRegion.unite(transformedRect);
+ }
+ return transformedRegion;
+}
+
+static inline void reduceOcclusion(const IntRect& affectedArea, const IntRect& expandedPixel, Region& occlusion)
+{
+ if (affectedArea.isEmpty())
+ return;
+
+ Region affectedOcclusion = intersect(occlusion, affectedArea);
+ Vector<IntRect> affectedOcclusionRects = affectedOcclusion.rects();
+
+ occlusion.subtract(affectedArea);
+ for (size_t j = 0; j < affectedOcclusionRects.size(); ++j) {
+ IntRect& occlusionRect = affectedOcclusionRects[j];
+
+ // Shrink the rect by expanding the non-opaque pixels outside the rect.
+
+ // The expandedPixel is the IntRect for a single pixel after being
+ // expanded by filters on the layer. The original pixel would be
+ // IntRect(0, 0, 1, 1), and the expanded pixel is the rect, relative
+ // to this original rect, that the original pixel can influence after
+ // being filtered.
+ // To convert the expandedPixel IntRect back to filter outsets:
+ // x = -leftOutset
+ // width = leftOutset + rightOutset
+ // maxX = x + width = -leftOutset + leftOutset + rightOutset = rightOutset
+
+ // The leftOutset of the filters moves pixels on the right side of
+ // the occlusionRect into it, shrinking its right edge.
+ int shrinkLeft = occlusionRect.x() == affectedArea.x() ? 0 : expandedPixel.maxX();
+ int shrinkTop = occlusionRect.y() == affectedArea.y() ? 0 : expandedPixel.maxY();
+ int shrinkRight = occlusionRect.maxX() == affectedArea.maxX() ? 0 : -expandedPixel.x();
+ int shrinkBottom = occlusionRect.maxY() == affectedArea.maxY() ? 0 : -expandedPixel.y();
+
+ occlusionRect.move(shrinkLeft, shrinkTop);
+ occlusionRect.contract(shrinkLeft + shrinkRight, shrinkTop + shrinkBottom);
+
+ occlusion.unite(occlusionRect);
+ }
+}
+
+template<typename LayerType>
+static void reduceOcclusionBelowSurface(LayerType* contributingLayer, const IntRect& surfaceRect, const WebTransformationMatrix& surfaceTransform, LayerType* renderTarget, Region& occlusionInTarget, Region& occlusionInScreen)
+{
+ if (surfaceRect.isEmpty())
+ return;
+
+ IntRect boundsInTarget = enclosingIntRect(CCMathUtil::mapClippedRect(surfaceTransform, FloatRect(surfaceRect)));
+ if (!contributingLayer->renderSurface()->clipRect().isEmpty())
+ boundsInTarget.intersect(contributingLayer->renderSurface()->clipRect());
+
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ contributingLayer->backgroundFilters().getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ // The filter can move pixels from outside of the clip, so allow affectedArea to expand outside the clip.
+ boundsInTarget.move(-outsetLeft, -outsetTop);
+ boundsInTarget.expand(outsetLeft + outsetRight, outsetTop + outsetBottom);
+
+ IntRect boundsInScreen = enclosingIntRect(CCMathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), FloatRect(boundsInTarget)));
+
+ IntRect filterOutsetsInTarget(-outsetLeft, -outsetTop, outsetLeft + outsetRight, outsetTop + outsetBottom);
+ IntRect filterOutsetsInScreen = enclosingIntRect(CCMathUtil::mapClippedRect(renderTarget->renderSurface()->screenSpaceTransform(), FloatRect(filterOutsetsInTarget)));
+
+ reduceOcclusion(boundsInTarget, filterOutsetsInTarget, occlusionInTarget);
+ reduceOcclusion(boundsInScreen, filterOutsetsInScreen, occlusionInScreen);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToRenderTarget(const LayerType* newTarget)
+{
+ int lastIndex = m_stack.size() - 1;
+ bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].target == newTarget;
+
+ // We merge the screen occlusion from the current RenderSurface subtree out to its parent target RenderSurface.
+ // The target occlusion can be merged out as well but needs to be transformed to the new target.
+
+ const LayerType* oldTarget = m_stack[lastIndex].target;
+ const RenderSurfaceType* oldSurface = oldTarget->renderSurface();
+ Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->drawTransform());
+ if (oldTarget->hasReplica() && !oldTarget->replicaHasMask())
+ oldTargetOcclusionInNewTarget.unite(transformSurfaceOpaqueRegion<RenderSurfaceType>(oldSurface, m_stack[lastIndex].occlusionInTarget, oldSurface->replicaDrawTransform()));
+
+ IntRect unoccludedSurfaceRect;
+ IntRect unoccludedReplicaRect;
+ if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
+ unoccludedSurfaceRect = unoccludedContributingSurfaceContentRect(oldTarget, false, oldSurface->contentRect());
+ if (oldTarget->hasReplica())
+ unoccludedReplicaRect = unoccludedContributingSurfaceContentRect(oldTarget, true, oldSurface->contentRect());
+ }
+
+ if (surfaceWillBeAtTopAfterPop) {
+ // Merge the top of the stack down.
+ m_stack[lastIndex - 1].occlusionInScreen.unite(m_stack[lastIndex].occlusionInScreen);
+ m_stack[lastIndex - 1].occlusionInTarget.unite(oldTargetOcclusionInNewTarget);
+ m_stack.removeLast();
+ } else {
+ // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top.
+ m_stack.last().target = newTarget;
+ m_stack.last().occlusionInTarget = oldTargetOcclusionInNewTarget;
+ }
+
+ if (oldTarget->backgroundFilters().hasFilterThatMovesPixels()) {
+ reduceOcclusionBelowSurface(oldTarget, unoccludedSurfaceRect, oldSurface->drawTransform(), newTarget, m_stack.last().occlusionInTarget, m_stack.last().occlusionInScreen);
+ if (oldTarget->hasReplica())
+ reduceOcclusionBelowSurface(oldTarget, unoccludedReplicaRect, oldSurface->replicaDrawTransform(), newTarget, m_stack.last().occlusionInTarget, m_stack.last().occlusionInScreen);
+ }
+}
+
+// FIXME: Remove usePaintTracking when paint tracking is on for paint culling.
+template<typename LayerType>
+static inline void addOcclusionBehindLayer(Region& region, const LayerType* layer, const WebTransformationMatrix& transform, const Region& opaqueContents, const IntRect& clipRectInTarget, const IntSize& minimumTrackingSize, Vector<IntRect>* occludingScreenSpaceRects)
+{
+ ASSERT(layer->visibleContentRect().contains(opaqueContents.bounds()));
+
+ bool clipped;
+ FloatQuad visibleTransformedQuad = CCMathUtil::mapQuad(transform, FloatQuad(layer->visibleContentRect()), clipped);
+ // FIXME: Find a rect interior to each transformed quad.
+ if (clipped || !visibleTransformedQuad.isRectilinear())
+ return;
+
+ Vector<IntRect> contentRects = opaqueContents.rects();
+ for (size_t i = 0; i < contentRects.size(); ++i) {
+ // We've already checked for clipping in the mapQuad call above, these calls should not clip anything further.
+ IntRect transformedRect = enclosedIntRect(CCMathUtil::mapClippedRect(transform, FloatRect(contentRects[i])));
+ transformedRect.intersect(clipRectInTarget);
+ if (transformedRect.width() >= minimumTrackingSize.width() || transformedRect.height() >= minimumTrackingSize.height()) {
+ if (occludingScreenSpaceRects)
+ occludingScreenSpaceRects->append(transformedRect);
+ region.unite(transformedRect);
+ }
+ }
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLayer(const LayerType* layer)
+{
+ ASSERT(!m_stack.isEmpty());
+ ASSERT(layer->renderTarget() == m_stack.last().target);
+ if (m_stack.isEmpty())
+ return;
+
+ if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1)
+ return;
+
+ if (layerIsInUnsorted3dRenderingContext(layer))
+ return;
+
+ Region opaqueContents = layer->visibleContentOpaqueRegion();
+ if (opaqueContents.isEmpty())
+ return;
+
+ IntRect clipRectInTarget = layerClipRectInTarget(layer);
+ if (layerTransformsToTargetKnown(layer))
+ addOcclusionBehindLayer<LayerType>(m_stack.last().occlusionInTarget, layer, layer->drawTransform(), opaqueContents, clipRectInTarget, m_minimumTrackingSize, 0);
+
+ // We must clip the occlusion within the layer's clipRectInTarget within screen space as well. If the clip rect can't be moved to screen space and
+ // remain rectilinear, then we don't add any occlusion in screen space.
+
+ if (layerTransformsToScreenKnown(layer)) {
+ WebTransformationMatrix targetToScreenTransform = m_stack.last().target->renderSurface()->screenSpaceTransform();
+ bool clipped;
+ FloatQuad clipQuadInScreen = CCMathUtil::mapQuad(targetToScreenTransform, FloatQuad(FloatRect(clipRectInTarget)), clipped);
+ // FIXME: Find a rect interior to the transformed clip quad.
+ if (clipped || !clipQuadInScreen.isRectilinear())
+ return;
+ IntRect clipRectInScreen = intersection(m_rootTargetRect, enclosedIntRect(clipQuadInScreen.boundingBox()));
+ addOcclusionBehindLayer<LayerType>(m_stack.last().occlusionInScreen, layer, layer->screenSpaceTransform(), opaqueContents, clipRectInScreen, m_minimumTrackingSize, m_occludingScreenSpaceRects);
+ }
+}
+
+static inline bool testContentRectOccluded(const IntRect& contentRect, const WebTransformationMatrix& contentSpaceTransform, const IntRect& clipRectInTarget, const Region& occlusion)
+{
+ FloatRect transformedRect = CCMathUtil::mapClippedRect(contentSpaceTransform, FloatRect(contentRect));
+ // Take the enclosingIntRect, as we want to include partial pixels in the test.
+ IntRect targetRect = intersection(enclosingIntRect(transformedRect), clipRectInTarget);
+ return targetRect.isEmpty() || occlusion.contains(targetRect);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+bool CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerType* layer, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const
+{
+ if (hasOcclusionFromOutsideTargetSurface)
+ *hasOcclusionFromOutsideTargetSurface = false;
+
+ ASSERT(!m_stack.isEmpty());
+ if (m_stack.isEmpty())
+ return false;
+ if (contentRect.isEmpty())
+ return true;
+
+ ASSERT(layer->renderTarget() == m_stack.last().target);
+
+ if (layerTransformsToTargetKnown(layer) && testContentRectOccluded(contentRect, layer->drawTransform(), layerClipRectInTarget(layer), m_stack.last().occlusionInTarget))
+ return true;
+
+ if (layerTransformsToScreenKnown(layer) && testContentRectOccluded(contentRect, layer->screenSpaceTransform(), m_rootTargetRect, m_stack.last().occlusionInScreen)) {
+ if (hasOcclusionFromOutsideTargetSurface)
+ *hasOcclusionFromOutsideTargetSurface = true;
+ return true;
+ }
+
+ return false;
+}
+
+// Determines what portion of rect, if any, is unoccluded (not occluded by region). If
+// the resulting unoccluded region is not rectangular, we return a rect containing it.
+static inline IntRect rectSubtractRegion(const IntRect& rect, const Region& region)
+{
+ Region rectRegion(rect);
+ rectRegion.subtract(region);
+ return rectRegion.bounds();
+}
+
+static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const WebTransformationMatrix& contentSpaceTransform, const IntRect& clipRectInTarget, const Region& occlusion)
+{
+ if (!contentSpaceTransform.isInvertible())
+ return contentRect;
+
+ // Take the enclosingIntRect at each step, as we want to contain any unoccluded partial pixels in the resulting IntRect.
+ FloatRect transformedRect = CCMathUtil::mapClippedRect(contentSpaceTransform, FloatRect(contentRect));
+ IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), clipRectInTarget), occlusion);
+ IntRect unoccludedRect = enclosingIntRect(CCMathUtil::projectClippedRect(contentSpaceTransform.inverse(), FloatRect(shrunkRect)));
+ // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds.
+ return intersection(unoccludedRect, contentRect);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentRect(const LayerType* layer, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const
+{
+ ASSERT(!m_stack.isEmpty());
+ if (m_stack.isEmpty())
+ return contentRect;
+ if (contentRect.isEmpty())
+ return contentRect;
+
+ ASSERT(layer->renderTarget() == m_stack.last().target);
+
+ // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface.
+ // So we find the visible parts of |contentRect| in each space, and take the intersection.
+
+ IntRect unoccludedInScreen = contentRect;
+ if (layerTransformsToScreenKnown(layer))
+ unoccludedInScreen = computeUnoccludedContentRect(contentRect, layer->screenSpaceTransform(), m_rootTargetRect, m_stack.last().occlusionInScreen);
+
+ IntRect unoccludedInTarget = contentRect;
+ if (layerTransformsToTargetKnown(layer))
+ unoccludedInTarget = computeUnoccludedContentRect(contentRect, layer->drawTransform(), layerClipRectInTarget(layer), m_stack.last().occlusionInTarget);
+
+ if (hasOcclusionFromOutsideTargetSurface)
+ *hasOcclusionFromOutsideTargetSurface = (intersection(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget);
+
+ return intersection(unoccludedInScreen, unoccludedInTarget);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContributingSurfaceContentRect(const LayerType* layer, bool forReplica, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const
+{
+ ASSERT(!m_stack.isEmpty());
+ // The layer is a contributing renderTarget so it should have a surface.
+ ASSERT(layer->renderSurface());
+ // The layer is a contributing renderTarget so its target should be itself.
+ ASSERT(layer->renderTarget() == layer);
+ // The layer should not be the root, else what is is contributing to?
+ ASSERT(layer->parent());
+ // This should be called while the layer is still considered the current target in the occlusion tracker.
+ ASSERT(layer == m_stack.last().target);
+
+ if (contentRect.isEmpty())
+ return contentRect;
+
+ RenderSurfaceType* surface = layer->renderSurface();
+
+ IntRect surfaceClipRect = surface->clipRect();
+ if (surfaceClipRect.isEmpty()) {
+ LayerType* contributingSurfaceRenderTarget = layer->parent()->renderTarget();
+ surfaceClipRect = intersection(contributingSurfaceRenderTarget->renderSurface()->contentRect(), enclosingIntRect(surface->drawableContentRect()));
+ }
+
+ // A contributing surface doesn't get occluded by things inside its own surface, so only things outside the surface can occlude it. That occlusion is
+ // found just below the top of the stack (if it exists).
+ bool hasOcclusion = m_stack.size() > 1;
+
+ const WebTransformationMatrix& transformToScreen = forReplica ? surface->replicaScreenSpaceTransform() : surface->screenSpaceTransform();
+ const WebTransformationMatrix& transformToTarget = forReplica ? surface->replicaDrawTransform() : surface->drawTransform();
+
+ IntRect unoccludedInScreen = contentRect;
+ if (surfaceTransformsToScreenKnown(surface)) {
+ if (hasOcclusion) {
+ const StackObject& secondLast = m_stack[m_stack.size() - 2];
+ unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, secondLast.occlusionInScreen);
+ } else
+ unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_rootTargetRect, Region());
+ }
+
+ IntRect unoccludedInTarget = contentRect;
+ if (surfaceTransformsToTargetKnown(surface)) {
+ if (hasOcclusion) {
+ const StackObject& secondLast = m_stack[m_stack.size() - 2];
+ unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, secondLast.occlusionInTarget);
+ } else
+ unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, Region());
+ }
+
+ if (hasOcclusionFromOutsideTargetSurface)
+ *hasOcclusionFromOutsideTargetSurface = (intersection(unoccludedInScreen, unoccludedInTarget) != unoccludedInTarget);
+
+ return intersection(unoccludedInScreen, unoccludedInTarget);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
+IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerClipRectInTarget(const LayerType* layer) const
+{
+ // FIXME: we could remove this helper function, but unit tests currently override this
+ // function, and they need to be verified/adjusted before this can be removed.
+ return layer->drawableContentRect();
+}
+
+// Declare the possible functions here for the linker.
+template CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::CCOcclusionTrackerBase(IntRect rootTargetRect, bool recordMetricsForFrame);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterLayer(const CCLayerIteratorPosition<LayerChromium>&);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveLayer(const CCLayerIteratorPosition<LayerChromium>&);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::enterRenderTarget(const LayerChromium* newTarget);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::finishedRenderTarget(const LayerChromium* finishedTarget);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leaveToRenderTarget(const LayerChromium* newTarget);
+template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::markOccludedBehindLayer(const LayerChromium*);
+template bool CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::occluded(const LayerChromium*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContributingSurfaceContentRect(const LayerChromium*, bool forReplica, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::layerClipRectInTarget(const LayerChromium*) const;
+
+template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect rootTargetRect, bool recordMetricsForFrame);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterLayer(const CCLayerIteratorPosition<CCLayerImpl>&);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveLayer(const CCLayerIteratorPosition<CCLayerImpl>&);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::enterRenderTarget(const CCLayerImpl* newTarget);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::finishedRenderTarget(const CCLayerImpl* finishedTarget);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveToRenderTarget(const CCLayerImpl* newTarget);
+template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::markOccludedBehindLayer(const CCLayerImpl*);
+template bool CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::occluded(const CCLayerImpl*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContributingSurfaceContentRect(const CCLayerImpl*, bool forReplica, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface) const;
+template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::layerClipRectInTarget(const CCLayerImpl*) const;
+
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCOcclusionTracker.h b/cc/CCOcclusionTracker.h
new file mode 100644
index 0000000..c923056
--- /dev/null
+++ b/cc/CCOcclusionTracker.h
@@ -0,0 +1,103 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCOcclusionTracker_h
+#define CCOcclusionTracker_h
+
+#include "CCLayerIterator.h"
+#include "FloatQuad.h"
+#include "Region.h"
+
+namespace WebCore {
+class CCOverdrawMetrics;
+class CCLayerImpl;
+class CCRenderSurface;
+class LayerChromium;
+class RenderSurfaceChromium;
+
+// This class is used to track occlusion of layers while traversing them in a front-to-back order. As each layer is visited, one of the
+// methods in this class is called to notify it about the current target surface.
+// Then, occlusion in the content space of the current layer may be queried, via methods such as occluded() and unoccludedContentRect().
+// If the current layer owns a RenderSurface, then occlusion on that RenderSurface may also be queried via surfaceOccluded() and surfaceUnoccludedContentRect().
+// Finally, once finished with the layer, occlusion behind the layer should be marked by calling markOccludedBehindLayer().
+template<typename LayerType, typename RenderSurfaceType>
+class CCOcclusionTrackerBase {
+ WTF_MAKE_NONCOPYABLE(CCOcclusionTrackerBase);
+public:
+ CCOcclusionTrackerBase(IntRect rootTargetRect, bool recordMetricsForFrame);
+
+ // Called at the beginning of each step in the CCLayerIterator's front-to-back traversal.
+ void enterLayer(const CCLayerIteratorPosition<LayerType>&);
+ // Called at the end of each step in the CCLayerIterator's front-to-back traversal.
+ void leaveLayer(const CCLayerIteratorPosition<LayerType>&);
+
+ // Returns true if the given rect in content space for the layer is fully occluded in either screen space or the layer's target surface.
+ bool occluded(const LayerType*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface = 0) const;
+ // Gives an unoccluded sub-rect of |contentRect| in the content space of the layer. Used when considering occlusion for a layer that paints/draws something.
+ IntRect unoccludedContentRect(const LayerType*, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface = 0) const;
+
+ // Gives an unoccluded sub-rect of |contentRect| in the content space of the renderTarget owned by the layer.
+ // Used when considering occlusion for a contributing surface that is rendering into another target.
+ IntRect unoccludedContributingSurfaceContentRect(const LayerType*, bool forReplica, const IntRect& contentRect, bool* hasOcclusionFromOutsideTargetSurface = 0) const;
+
+ // Report operations for recording overdraw metrics.
+ CCOverdrawMetrics& overdrawMetrics() const { return *m_overdrawMetrics.get(); }
+
+ // Gives the region of the screen that is not occluded by something opaque.
+ Region computeVisibleRegionInScreen() const { return subtract(Region(m_rootTargetRect), m_stack.last().occlusionInScreen); }
+
+ void setMinimumTrackingSize(const IntSize& size) { m_minimumTrackingSize = size; }
+
+ // The following is used for visualization purposes.
+ void setOccludingScreenSpaceRectsContainer(Vector<IntRect>* rects) { m_occludingScreenSpaceRects = rects; }
+
+protected:
+ struct StackObject {
+ StackObject() : target(0) { }
+ StackObject(const LayerType* target) : target(target) { }
+ const LayerType* target;
+ Region occlusionInScreen;
+ Region occlusionInTarget;
+ };
+
+ // The stack holds occluded regions for subtrees in the RenderSurface-Layer tree, so that when we leave a subtree we may
+ // apply a mask to it, but not to the parts outside the subtree.
+ // - The first time we see a new subtree under a target, we add that target to the top of the stack. This can happen as a layer representing itself, or as a target surface.
+ // - When we visit a target surface, we apply its mask to its subtree, which is at the top of the stack.
+ // - When we visit a layer representing itself, we add its occlusion to the current subtree, which is at the top of the stack.
+ // - When we visit a layer representing a contributing surface, the current target will never be the top of the stack since we just came from the contributing surface.
+ // We merge the occlusion at the top of the stack with the new current subtree. This new target is pushed onto the stack if not already there.
+ Vector<StackObject, 1> m_stack;
+
+ // Allow tests to override this.
+ virtual IntRect layerClipRectInTarget(const LayerType*) const;
+
+private:
+ // Called when visiting a layer representing itself. If the target was not already current, then this indicates we have entered a new surface subtree.
+ void enterRenderTarget(const LayerType* newTarget);
+
+ // Called when visiting a layer representing a target surface. This indicates we have visited all the layers within the surface, and we may
+ // perform any surface-wide operations.
+ void finishedRenderTarget(const LayerType* finishedTarget);
+
+ // Called when visiting a layer representing a contributing surface. This indicates that we are leaving our current surface, and
+ // entering the new one. We then perform any operations required for merging results from the child subtree into its parent.
+ void leaveToRenderTarget(const LayerType* newTarget);
+
+ // Add the layer's occlusion to the tracked state.
+ void markOccludedBehindLayer(const LayerType*);
+
+ IntRect m_rootTargetRect;
+ OwnPtr<CCOverdrawMetrics> m_overdrawMetrics;
+ IntSize m_minimumTrackingSize;
+
+ // This is used for visualizing the occlusion tracking process.
+ Vector<IntRect>* m_occludingScreenSpaceRects;
+};
+
+typedef CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium> CCOcclusionTracker;
+typedef CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface> CCOcclusionTrackerImpl;
+
+}
+#endif // CCOcclusionTracker_h
diff --git a/cc/CCOcclusionTrackerTest.cpp b/cc/CCOcclusionTrackerTest.cpp
new file mode 100644
index 0000000..cb0eefe6
--- /dev/null
+++ b/cc/CCOcclusionTrackerTest.cpp
@@ -0,0 +1,3005 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCOcclusionTracker.h"
+
+#include "CCAnimationTestCommon.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHostCommon.h"
+#include "CCLayerTreeTestCommon.h"
+#include "CCMathUtil.h"
+#include "CCOcclusionTrackerTestCommon.h"
+#include "CCOverdrawMetrics.h"
+#include "CCSingleThreadProxy.h"
+#include "LayerChromium.h"
+#include "Region.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WebKitTests;
+
+namespace {
+
+class TestContentLayerChromium : public LayerChromium {
+public:
+ TestContentLayerChromium()
+ : LayerChromium()
+ , m_overrideOpaqueContentsRect(false)
+ {
+ }
+
+ virtual bool drawsContent() const OVERRIDE { return true; }
+ virtual Region visibleContentOpaqueRegion() const OVERRIDE
+ {
+ if (m_overrideOpaqueContentsRect)
+ return intersection(m_opaqueContentsRect, visibleContentRect());
+ return LayerChromium::visibleContentOpaqueRegion();
+ }
+ void setOpaqueContentsRect(const IntRect& opaqueContentsRect)
+ {
+ m_overrideOpaqueContentsRect = true;
+ m_opaqueContentsRect = opaqueContentsRect;
+ }
+
+private:
+ bool m_overrideOpaqueContentsRect;
+ IntRect m_opaqueContentsRect;
+};
+
+class TestContentLayerImpl : public CCLayerImpl {
+public:
+ TestContentLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_overrideOpaqueContentsRect(false)
+ {
+ setDrawsContent(true);
+ }
+
+ virtual Region visibleContentOpaqueRegion() const OVERRIDE
+ {
+ if (m_overrideOpaqueContentsRect)
+ return intersection(m_opaqueContentsRect, visibleContentRect());
+ return CCLayerImpl::visibleContentOpaqueRegion();
+ }
+ void setOpaqueContentsRect(const IntRect& opaqueContentsRect)
+ {
+ m_overrideOpaqueContentsRect = true;
+ m_opaqueContentsRect = opaqueContentsRect;
+ }
+
+private:
+ bool m_overrideOpaqueContentsRect;
+ IntRect m_opaqueContentsRect;
+};
+
+template<typename LayerType, typename RenderSurfaceType>
+class TestCCOcclusionTrackerWithClip : public TestCCOcclusionTrackerBase<LayerType, RenderSurfaceType> {
+public:
+ TestCCOcclusionTrackerWithClip(IntRect viewportRect, bool recordMetricsForFrame = false)
+ : TestCCOcclusionTrackerBase<LayerType, RenderSurfaceType>(viewportRect, recordMetricsForFrame)
+ , m_overrideLayerClipRect(false)
+ {
+ }
+
+ void setLayerClipRect(const IntRect& rect) { m_overrideLayerClipRect = true; m_layerClipRect = rect;}
+ void useDefaultLayerClipRect() { m_overrideLayerClipRect = false; }
+
+protected:
+ virtual IntRect layerClipRectInTarget(const LayerType* layer) const { return m_overrideLayerClipRect ? m_layerClipRect : CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerClipRectInTarget(layer); }
+
+private:
+ bool m_overrideLayerClipRect;
+ IntRect m_layerClipRect;
+};
+
+struct CCOcclusionTrackerTestMainThreadTypes {
+ typedef LayerChromium LayerType;
+ typedef RenderSurfaceChromium RenderSurfaceType;
+ typedef TestContentLayerChromium ContentLayerType;
+ typedef RefPtr<LayerChromium> LayerPtrType;
+ typedef PassRefPtr<LayerChromium> PassLayerPtrType;
+ typedef RefPtr<ContentLayerType> ContentLayerPtrType;
+ typedef PassRefPtr<ContentLayerType> PassContentLayerPtrType;
+ typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> LayerIterator;
+ typedef CCOcclusionTracker OcclusionTrackerType;
+
+ static PassLayerPtrType createLayer()
+ {
+ return LayerChromium::create();
+ }
+ static PassContentLayerPtrType createContentLayer() { return adoptRef(new ContentLayerType()); }
+};
+
+struct CCOcclusionTrackerTestImplThreadTypes {
+ typedef CCLayerImpl LayerType;
+ typedef CCRenderSurface RenderSurfaceType;
+ typedef TestContentLayerImpl ContentLayerType;
+ typedef OwnPtr<CCLayerImpl> LayerPtrType;
+ typedef PassOwnPtr<CCLayerImpl> PassLayerPtrType;
+ typedef OwnPtr<ContentLayerType> ContentLayerPtrType;
+ typedef PassOwnPtr<ContentLayerType> PassContentLayerPtrType;
+ typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> LayerIterator;
+ typedef CCOcclusionTrackerImpl OcclusionTrackerType;
+
+ static PassLayerPtrType createLayer() { return CCLayerImpl::create(nextCCLayerImplId++); }
+ static PassContentLayerPtrType createContentLayer() { return adoptPtr(new ContentLayerType(nextCCLayerImplId++)); }
+ static int nextCCLayerImplId;
+};
+
+int CCOcclusionTrackerTestImplThreadTypes::nextCCLayerImplId = 1;
+
+template<typename Types, bool opaqueLayers>
+class CCOcclusionTrackerTest : public testing::Test {
+protected:
+ CCOcclusionTrackerTest()
+ : testing::Test()
+ { }
+
+ virtual void runMyTest() = 0;
+
+ virtual void TearDown()
+ {
+ m_root.clear();
+ m_renderSurfaceLayerListChromium.clear();
+ m_renderSurfaceLayerListImpl.clear();
+ m_replicaLayers.clear();
+ m_maskLayers.clear();
+ CCLayerTreeHost::setNeedsFilterContext(false);
+ }
+
+ typename Types::ContentLayerType* createRoot(const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ typename Types::ContentLayerPtrType layer(Types::createContentLayer());
+ typename Types::ContentLayerType* layerPtr = layer.get();
+ setProperties(layerPtr, transform, position, bounds);
+
+ ASSERT(!m_root);
+ m_root = layer.release();
+ return layerPtr;
+ }
+
+ typename Types::LayerType* createLayer(typename Types::LayerType* parent, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ typename Types::LayerPtrType layer(Types::createLayer());
+ typename Types::LayerType* layerPtr = layer.get();
+ setProperties(layerPtr, transform, position, bounds);
+ parent->addChild(layer.release());
+ return layerPtr;
+ }
+
+ typename Types::LayerType* createSurface(typename Types::LayerType* parent, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ typename Types::LayerType* layer = createLayer(parent, transform, position, bounds);
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createGrayscaleFilter(0.5));
+ layer->setFilters(filters);
+ return layer;
+ }
+
+ typename Types::ContentLayerType* createDrawingLayer(typename Types::LayerType* parent, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds, bool opaque)
+ {
+ typename Types::ContentLayerPtrType layer(Types::createContentLayer());
+ typename Types::ContentLayerType* layerPtr = layer.get();
+ setProperties(layerPtr, transform, position, bounds);
+
+ if (opaqueLayers)
+ layerPtr->setOpaque(opaque);
+ else {
+ layerPtr->setOpaque(false);
+ if (opaque)
+ layerPtr->setOpaqueContentsRect(IntRect(IntPoint(), bounds));
+ else
+ layerPtr->setOpaqueContentsRect(IntRect());
+ }
+
+ parent->addChild(layer.release());
+ return layerPtr;
+ }
+
+ typename Types::LayerType* createReplicaLayer(typename Types::LayerType* owningLayer, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ typename Types::ContentLayerPtrType layer(Types::createContentLayer());
+ typename Types::ContentLayerType* layerPtr = layer.get();
+ setProperties(layerPtr, transform, position, bounds);
+ setReplica(owningLayer, layer.release());
+ return layerPtr;
+ }
+
+ typename Types::LayerType* createMaskLayer(typename Types::LayerType* owningLayer, const IntSize& bounds)
+ {
+ typename Types::ContentLayerPtrType layer(Types::createContentLayer());
+ typename Types::ContentLayerType* layerPtr = layer.get();
+ setProperties(layerPtr, identityMatrix, FloatPoint(), bounds);
+ setMask(owningLayer, layer.release());
+ return layerPtr;
+ }
+
+ typename Types::ContentLayerType* createDrawingSurface(typename Types::LayerType* parent, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds, bool opaque)
+ {
+ typename Types::ContentLayerType* layer = createDrawingLayer(parent, transform, position, bounds, opaque);
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createGrayscaleFilter(0.5));
+ layer->setFilters(filters);
+ return layer;
+ }
+
+ void calcDrawEtc(TestContentLayerImpl* root)
+ {
+ ASSERT(root == m_root.get());
+ int dummyMaxTextureSize = 512;
+ CCLayerSorter layerSorter;
+
+ ASSERT(!root->renderSurface());
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(root, root->bounds(), 1, &layerSorter, dummyMaxTextureSize, m_renderSurfaceLayerListImpl);
+ CCLayerTreeHostCommon::calculateVisibleRects(m_renderSurfaceLayerListImpl);
+
+ m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListImpl);
+ }
+
+ void calcDrawEtc(TestContentLayerChromium* root)
+ {
+ ASSERT(root == m_root.get());
+ int dummyMaxTextureSize = 512;
+
+ ASSERT(!root->renderSurface());
+
+ CCLayerTreeHostCommon::calculateDrawTransforms(root, root->bounds(), 1, dummyMaxTextureSize, m_renderSurfaceLayerListChromium);
+ CCLayerTreeHostCommon::calculateVisibleRects(m_renderSurfaceLayerListChromium);
+
+ m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListChromium);
+ }
+
+ void enterLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ ASSERT_EQ(layer, *m_layerIterator);
+ ASSERT_TRUE(m_layerIterator.representsItself());
+ occlusion.enterLayer(m_layerIterator);
+ }
+
+ void leaveLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ ASSERT_EQ(layer, *m_layerIterator);
+ ASSERT_TRUE(m_layerIterator.representsItself());
+ occlusion.leaveLayer(m_layerIterator);
+ ++m_layerIterator;
+ }
+
+ void visitLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ enterLayer(layer, occlusion);
+ leaveLayer(layer, occlusion);
+ }
+
+ void enterContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ ASSERT_EQ(layer, *m_layerIterator);
+ ASSERT_TRUE(m_layerIterator.representsTargetRenderSurface());
+ occlusion.enterLayer(m_layerIterator);
+ occlusion.leaveLayer(m_layerIterator);
+ ++m_layerIterator;
+ ASSERT_TRUE(m_layerIterator.representsContributingRenderSurface());
+ occlusion.enterLayer(m_layerIterator);
+ }
+
+ void leaveContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ ASSERT_EQ(layer, *m_layerIterator);
+ ASSERT_TRUE(m_layerIterator.representsContributingRenderSurface());
+ occlusion.leaveLayer(m_layerIterator);
+ ++m_layerIterator;
+ }
+
+ void visitContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion)
+ {
+ enterContributingSurface(layer, occlusion);
+ leaveContributingSurface(layer, occlusion);
+ }
+
+ void resetLayerIterator()
+ {
+ m_layerIterator = m_layerIteratorBegin;
+ }
+
+ const WebTransformationMatrix identityMatrix;
+
+private:
+ void setBaseProperties(typename Types::LayerType* layer, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ layer->setTransform(transform);
+ layer->setSublayerTransform(WebTransformationMatrix());
+ layer->setAnchorPoint(FloatPoint(0, 0));
+ layer->setPosition(position);
+ layer->setBounds(bounds);
+ }
+
+ void setProperties(LayerChromium* layer, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ setBaseProperties(layer, transform, position, bounds);
+ }
+
+ void setProperties(CCLayerImpl* layer, const WebTransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+ {
+ setBaseProperties(layer, transform, position, bounds);
+
+ layer->setContentBounds(layer->bounds());
+ }
+
+ void setReplica(LayerChromium* owningLayer, PassRefPtr<LayerChromium> layer)
+ {
+ owningLayer->setReplicaLayer(layer.get());
+ m_replicaLayers.append(layer);
+ }
+
+ void setReplica(CCLayerImpl* owningLayer, PassOwnPtr<CCLayerImpl> layer)
+ {
+ owningLayer->setReplicaLayer(layer);
+ }
+
+ void setMask(LayerChromium* owningLayer, PassRefPtr<LayerChromium> layer)
+ {
+ owningLayer->setMaskLayer(layer.get());
+ m_maskLayers.append(layer);
+ }
+
+ void setMask(CCLayerImpl* owningLayer, PassOwnPtr<CCLayerImpl> layer)
+ {
+ owningLayer->setMaskLayer(layer);
+ }
+
+ // These hold ownership of the layers for the duration of the test.
+ typename Types::LayerPtrType m_root;
+ Vector<RefPtr<LayerChromium> > m_renderSurfaceLayerListChromium;
+ Vector<CCLayerImpl*> m_renderSurfaceLayerListImpl;
+ typename Types::LayerIterator m_layerIteratorBegin;
+ typename Types::LayerIterator m_layerIterator;
+ typename Types::LayerType* m_lastLayerVisited;
+ Vector<RefPtr<LayerChromium> > m_replicaLayers;
+ Vector<RefPtr<LayerChromium> > m_maskLayers;
+};
+
+#define RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \
+ class ClassName##MainThreadOpaqueLayers : public ClassName<CCOcclusionTrackerTestMainThreadTypes, true> { \
+ public: \
+ ClassName##MainThreadOpaqueLayers() : ClassName<CCOcclusionTrackerTestMainThreadTypes, true>() { } \
+ }; \
+ TEST_F(ClassName##MainThreadOpaqueLayers, runTest) { runMyTest(); }
+#define RUN_TEST_MAIN_THREAD_OPAQUE_PAINTS(ClassName) \
+ class ClassName##MainThreadOpaquePaints : public ClassName<CCOcclusionTrackerTestMainThreadTypes, false> { \
+ public: \
+ ClassName##MainThreadOpaquePaints() : ClassName<CCOcclusionTrackerTestMainThreadTypes, false>() { } \
+ }; \
+ TEST_F(ClassName##MainThreadOpaquePaints, runTest) { runMyTest(); }
+
+#define RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName) \
+ class ClassName##ImplThreadOpaqueLayers : public ClassName<CCOcclusionTrackerTestImplThreadTypes, true> { \
+ DebugScopedSetImplThread impl; \
+ public: \
+ ClassName##ImplThreadOpaqueLayers() : ClassName<CCOcclusionTrackerTestImplThreadTypes, true>() { } \
+ }; \
+ TEST_F(ClassName##ImplThreadOpaqueLayers, runTest) { runMyTest(); }
+#define RUN_TEST_IMPL_THREAD_OPAQUE_PAINTS(ClassName) \
+ class ClassName##ImplThreadOpaquePaints : public ClassName<CCOcclusionTrackerTestImplThreadTypes, false> { \
+ DebugScopedSetImplThread impl; \
+ public: \
+ ClassName##ImplThreadOpaquePaints() : ClassName<CCOcclusionTrackerTestImplThreadTypes, false>() { } \
+ }; \
+ TEST_F(ClassName##ImplThreadOpaquePaints, runTest) { runMyTest(); }
+
+#define ALL_CCOCCLUSIONTRACKER_TEST(ClassName) \
+ RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \
+ RUN_TEST_MAIN_THREAD_OPAQUE_PAINTS(ClassName) \
+ RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName) \
+ RUN_TEST_IMPL_THREAD_OPAQUE_PAINTS(ClassName)
+
+#define MAIN_THREAD_TEST(ClassName) \
+ RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName)
+
+#define IMPL_THREAD_TEST(ClassName) \
+ RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName)
+
+#define MAIN_AND_IMPL_THREAD_TEST(ClassName) \
+ RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \
+ RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName)
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestIdentityTransforms : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(30, 30), IntSize(500, 500), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 31, 70, 70)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 70, 70)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 31, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 31, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 31, 70, 70)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestIdentityTransforms);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestRotatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(250, 250);
+ layerTransform.rotate(90);
+ layerTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 31, 70, 70)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 70, 70)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 29, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 31, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 31, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 31, 70, 70)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestRotatedChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestTranslatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(20, 20);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 50, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(50, 50, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(49, 50, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 49, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(51, 50, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 51, 50, 50)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(50, 50, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(49, 50, 50, 50)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 49, 50, 50)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(51, 50, 50, 50)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(50, 51, 50, 50)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 50, 50, 50)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(49, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 50, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(49, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 49, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(50, 49, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 49, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(51, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 49, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 50, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(51, 51, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 51, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(50, 100, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 51, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(49, 51, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 51, 50, 50)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 50, 50, 50)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(49, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 50, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(49, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 49, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(50, 49, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 49, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(51, 49, 49, 1), occlusion.unoccludedContentRect(parent, IntRect(51, 49, 50, 50)));
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(51, 50, 50, 50)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(51, 51, 50, 50)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 51, 50, 50)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(49, 51, 1, 49), occlusion.unoccludedContentRect(parent, IntRect(49, 51, 50, 50)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestTranslatedChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestChildInRotatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500));
+ child->setMasksToBounds(true);
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(layer, occlusion);
+ this->enterContributingSurface(child, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->leaveContributingSurface(child, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 41, 70, 60)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 40, 70, 60)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 41, 70, 60)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+
+ /* Justification for the above occlusion from |layer|:
+ 100
+ +---------------------+ +---------------------+
+ | | | |30 Visible region of |layer|: /////
+ | 30 | rotate(90) | |
+ | 30 + ---------------------------------+ | +---------------------------------+
+ 100 | | 10 | | ==> | | |10 |
+ | |10+---------------------------------+ | +---------------------------------+ |
+ | | | | | | | | |///////////////| 420 | |
+ | | | | | | | | |///////////////|60 | |
+ | | | | | | | | |///////////////| | |
+ +----|--|-------------+ | | +--|--|---------------+ | |
+ | | | | 20|10| 70 | |
+ | | | | | | | |
+ | | | |500 | | | |
+ | | | | | | | |
+ | | | | | | | |
+ | | | | | | | |
+ | | | | | | |10|
+ +--|-------------------------------+ | | +------------------------------|--+
+ | | | 490 |
+ +---------------------------------+ +---------------------------------+
+ 500 500
+ */
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestChildInRotatedChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestVisitTargetTwoTimes : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500));
+ child->setMasksToBounds(true);
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true);
+ // |child2| makes |parent|'s surface get considered by CCOcclusionTracker first, instead of |child|'s. This exercises different code in
+ // leaveToTargetRenderSurface, as the target surface has already been seen.
+ typename Types::ContentLayerType* child2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(30, 30), IntSize(60, 20), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(-10, -10, 1000, 1000));
+
+ this->visitLayer(child2, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 60, 20), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 60, 20), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(layer, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->enterContributingSurface(child, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // Occlusion in |child2| should get merged with the |child| surface we are leaving now.
+ this->leaveContributingSurface(child, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 30, 70, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(90, 30, 10, 10), occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70)));
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 60, 10)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 60, 10)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 60, 10)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 60, 10)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 60, 10)));
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 60, 10)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(29, 30, 1, 10), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 60, 10)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 29, 60, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 60, 10)));
+ EXPECT_INT_RECT_EQ(IntRect(90, 30, 1, 10), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 60, 10)));
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 31, 60, 10)).isEmpty());
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 40, 70, 60)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(29, 40, 70, 60)));
+ // This rect is mostly occluded by |child2|.
+ EXPECT_INT_RECT_EQ(IntRect(90, 39, 10, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 39, 70, 60)));
+ // This rect extends past top/right ends of |child2|.
+ EXPECT_INT_RECT_EQ(IntRect(30, 29, 70, 11), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 70, 70)));
+ // This rect extends past left/right ends of |child2|.
+ EXPECT_INT_RECT_EQ(IntRect(20, 39, 80, 60), occlusion.unoccludedContentRect(parent, IntRect(20, 39, 80, 60)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(31, 40, 70, 60)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 41, 70, 60)));
+
+ /* Justification for the above occlusion from |layer|:
+ 100
+ +---------------------+ +---------------------+
+ | | | |30 Visible region of |layer|: /////
+ | 30 | rotate(90) | 30 60 | |child2|: \\\\\
+ | 30 + ------------+--------------------+ | 30 +------------+--------------------+
+ 100 | | 10 | | | ==> | |\\\\\\\\\\\\| |10 |
+ | |10+----------|----------------------+ | +--|\\\\\\\\\\\\|-----------------+ |
+ | + ------------+ | | | | | +------------+//| 420 | |
+ | | | | | | | | |///////////////|60 | |
+ | | | | | | | | |///////////////| | |
+ +----|--|-------------+ | | +--|--|---------------+ | |
+ | | | | 20|10| 70 | |
+ | | | | | | | |
+ | | | |500 | | | |
+ | | | | | | | |
+ | | | | | | | |
+ | | | | | | | |
+ | | | | | | |10|
+ +--|-------------------------------+ | | +------------------------------|--+
+ | | | 490 |
+ +---------------------------------+ +---------------------------------+
+ 500 500
+ */
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestVisitTargetTwoTimes);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceRotatedOffAxis : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(95);
+ childTransform.translate(-250, -250);
+
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(10, 10);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500));
+ child->setMasksToBounds(true);
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(child, layerTransform, FloatPoint(0, 0), IntSize(500, 500), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ IntRect clippedLayerInChild = CCMathUtil::mapClippedRect(layerTransform, layer->visibleContentRect());
+
+ this->visitLayer(layer, occlusion);
+ this->enterContributingSurface(child, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(clippedLayerInChild, occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child, clippedLayerInChild));
+ EXPECT_TRUE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty());
+ clippedLayerInChild.move(-1, 0);
+ EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild));
+ EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty());
+ clippedLayerInChild.move(1, 0);
+ clippedLayerInChild.move(1, 0);
+ EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild));
+ EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty());
+ clippedLayerInChild.move(-1, 0);
+ clippedLayerInChild.move(0, -1);
+ EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild));
+ EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty());
+ clippedLayerInChild.move(0, 1);
+ clippedLayerInChild.move(0, 1);
+ EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild));
+ EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty());
+ clippedLayerInChild.move(0, -1);
+
+ this->leaveContributingSurface(child, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(75, 55, 1, 1)));
+ EXPECT_INT_RECT_EQ(IntRect(75, 55, 1, 1), occlusion.unoccludedContentRect(parent, IntRect(75, 55, 1, 1)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceRotatedOffAxis);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceWithTwoOpaqueChildren : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500));
+ child->setMasksToBounds(true);
+ typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true);
+ typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 450), IntSize(500, 60), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(layer2, occlusion);
+ this->visitLayer(layer1, occlusion);
+ this->enterContributingSurface(child, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70)));
+ EXPECT_FALSE(occlusion.occluded(child, IntRect(9, 430, 60, 70)));
+ EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 429, 60, 70)));
+ EXPECT_FALSE(occlusion.occluded(child, IntRect(11, 430, 60, 70)));
+ EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 431, 60, 70)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(10, 430, 60, 70)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(9, 430, 1, 70), occlusion.unoccludedContentRect(child, IntRect(9, 430, 60, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(10, 429, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 429, 60, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(70, 430, 1, 70), occlusion.unoccludedContentRect(child, IntRect(11, 430, 60, 70)));
+ EXPECT_INT_RECT_EQ(IntRect(10, 500, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 431, 60, 70)));
+
+ this->leaveContributingSurface(child, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 40, 70, 60), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 40, 70, 60)).isEmpty());
+ EXPECT_INT_RECT_EQ(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(29, 40, 70, 60)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 39, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 39, 70, 60)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(31, 40, 70, 60)));
+ EXPECT_INT_RECT_EQ(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 41, 70, 60)));
+
+ /* Justification for the above occlusion from |layer1| and |layer2|:
+
+ +---------------------+
+ | |30 Visible region of |layer1|: /////
+ | | Visible region of |layer2|: \\\\\
+ | +---------------------------------+
+ | | |10 |
+ | +---------------+-----------------+ |
+ | | |\\\\\\\\\\\\|//| 420 | |
+ | | |\\\\\\\\\\\\|//|60 | |
+ | | |\\\\\\\\\\\\|//| | |
+ +--|--|------------|--+ | |
+ 20|10| 70 | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | | |
+ | | | |10|
+ | +------------|-----------------|--+
+ | | 490 |
+ +---------------+-----------------+
+ 60 440
+ */
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceWithTwoOpaqueChildren);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestOverlappingSurfaceSiblings : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix childTransform;
+ childTransform.translate(250, 250);
+ childTransform.rotate(90);
+ childTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child1 = this->createSurface(parent, childTransform, FloatPoint(30, 30), IntSize(10, 10));
+ typename Types::LayerType* child2 = this->createSurface(parent, childTransform, FloatPoint(20, 40), IntSize(10, 10));
+ typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child1, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true);
+ typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child2, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(-20, -20, 1000, 1000));
+
+ this->visitLayer(layer2, occlusion);
+ this->enterContributingSurface(child2, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(20, 30, 80, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(-10, 420, 70, 80), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80)));
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80)));
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80)));
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80)));
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81)));
+ occlusion.setLayerClipRect(IntRect(-20, -20, 1000, 1000));
+
+ // There is nothing above child2's surface in the z-order.
+ EXPECT_INT_RECT_EQ(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2, false, IntRect(-10, 420, 70, 80)));
+
+ this->leaveContributingSurface(child2, occlusion);
+ this->visitLayer(layer1, occlusion);
+ this->enterContributingSurface(child1, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(20, 20, 80, 80), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(-10, 430, 80, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child1, IntRect(-10, 430, 80, 70)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(-11, 430, 80, 70)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 429, 80, 70)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 430, 81, 70)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 430, 80, 71)));
+
+ // child2's contents will occlude child1 below it.
+ EXPECT_INT_RECT_EQ(IntRect(-10, 430, 10, 70), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(-10, 430, 80, 70)));
+
+ this->leaveContributingSurface(child1, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(20, 20, 80, 80), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(20, 20, 80, 80), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(20, 20, 80, 80)));
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 20, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 20, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 19, 70, 80)));
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(20, 30, 80, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(19, 30, 80, 70)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(20, 29, 80, 70)));
+
+ /* Justification for the above occlusion:
+ 100
+ +---------------------+
+ | 20 | layer1
+ | 30+ ---------------------------------+
+ 100 | 30| | layer2 |
+ |20+----------------------------------+ |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ +--|-|----------------+ | |
+ | | | | 510
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | +--------------------------------|-+
+ | |
+ +----------------------------------+
+ 510
+ */
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestOverlappingSurfaceSiblings);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix child1Transform;
+ child1Transform.translate(250, 250);
+ child1Transform.rotate(-90);
+ child1Transform.translate(-250, -250);
+
+ WebTransformationMatrix child2Transform;
+ child2Transform.translate(250, 250);
+ child2Transform.rotate(90);
+ child2Transform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::LayerType* child1 = this->createSurface(parent, child1Transform, FloatPoint(30, 20), IntSize(10, 10));
+ typename Types::LayerType* child2 = this->createDrawingSurface(parent, child2Transform, FloatPoint(20, 40), IntSize(10, 10), false);
+ typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child1, this->identityMatrix, FloatPoint(-10, -20), IntSize(510, 510), true);
+ typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child2, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(-30, -30, 1000, 1000));
+
+ this->visitLayer(layer2, occlusion);
+ this->enterLayer(child2, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(20, 30, 80, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(-10, 420, 70, 80), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80)));
+ EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81)));
+
+ this->leaveLayer(child2, occlusion);
+ this->enterContributingSurface(child2, occlusion);
+
+ // There is nothing above child2's surface in the z-order.
+ EXPECT_INT_RECT_EQ(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2, false, IntRect(-10, 420, 70, 80)));
+
+ this->leaveContributingSurface(child2, occlusion);
+ this->visitLayer(layer1, occlusion);
+ this->enterContributingSurface(child1, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(420, -20, 80, 90), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(child1, IntRect(420, -20, 80, 90)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(419, -20, 80, 90)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(420, -21, 80, 90)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(420, -19, 80, 90)));
+ EXPECT_FALSE(occlusion.occluded(child1, IntRect(421, -20, 80, 90)));
+
+ // child2's contents will occlude child1 below it.
+ EXPECT_INT_RECT_EQ(IntRect(420, -20, 80, 90), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -20, 80, 90)));
+ EXPECT_INT_RECT_EQ(IntRect(490, -10, 10, 80), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -10, 80, 90)));
+ EXPECT_INT_RECT_EQ(IntRect(420, -20, 70, 10), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -20, 70, 90)));
+
+ this->leaveContributingSurface(child1, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(10, 20, 90, 80), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(10, 20, 90, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(9, 20, 90, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(10, 19, 90, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(11, 20, 90, 80)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(10, 21, 90, 80)));
+
+ /* Justification for the above occlusion:
+ 100
+ +---------------------+
+ |20 | layer1
+ 10+----------------------------------+
+ 100 || 30 | layer2 |
+ |20+----------------------------------+
+ || | | | |
+ || | | | |
+ || | | | |
+ +|-|------------------+ | |
+ | | | | 510
+ | | 510 | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | 520 | |
+ +----------------------------------+ |
+ | |
+ +----------------------------------+
+ 510
+ */
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestFilters : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix layerTransform;
+ layerTransform.translate(250, 250);
+ layerTransform.rotate(90);
+ layerTransform.translate(-250, -250);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* blurLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true);
+ typename Types::ContentLayerType* opaqueLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true);
+ typename Types::ContentLayerType* opacityLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true);
+
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(10));
+ blurLayer->setFilters(filters);
+
+ filters.clear();
+ filters.append(WebFilterOperation::createGrayscaleFilter(0.5));
+ opaqueLayer->setFilters(filters);
+
+ filters.clear();
+ filters.append(WebFilterOperation::createOpacityFilter(0.5));
+ opacityLayer->setFilters(filters);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // Opacity layer won't contribute to occlusion.
+ this->visitLayer(opacityLayer, occlusion);
+ this->enterContributingSurface(opacityLayer, occlusion);
+
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+
+ // And has nothing to contribute to its parent surface.
+ this->leaveContributingSurface(opacityLayer, occlusion);
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+
+ // Opaque layer will contribute to occlusion.
+ this->visitLayer(opaqueLayer, occlusion);
+ this->enterContributingSurface(opaqueLayer, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 430, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // And it gets translated to the parent surface.
+ this->leaveContributingSurface(opaqueLayer, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The blur layer needs to throw away any occlusion from outside its subtree.
+ this->enterLayer(blurLayer, occlusion);
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+
+ // And it won't contribute to occlusion.
+ this->leaveLayer(blurLayer, occlusion);
+ this->enterContributingSurface(blurLayer, occlusion);
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+
+ // But the opaque layer's occlusion is preserved on the parent.
+ this->leaveContributingSurface(blurLayer, occlusion);
+ this->enterLayer(parent, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestFilters);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReplicaDoesOcclude : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 50), true);
+ this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(50, 50), IntSize());
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 50, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitContributingSurface(surface, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ // The surface and replica should both be occluding the parent.
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaDoesOcclude);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReplicaWithClipping : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 170));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 50), true);
+ this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(50, 50), IntSize());
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 50, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitContributingSurface(surface, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ // The surface and replica should both be occluding the parent.
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 100, 70), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaWithClipping);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReplicaWithMask : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 50), true);
+ typename Types::LayerType* replica = this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(50, 50), IntSize());
+ this->createMaskLayer(replica, IntSize(10, 10));
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 50, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitContributingSurface(surface, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ // The replica should not be occluding the parent, since it has a mask applied to it.
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaWithMask);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerClipRectOutsideChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(200, 100, 100, 100));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+ occlusion.setLayerClipRect(IntRect(200, 100, 100, 100));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerClipRectOutsideChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestViewportRectOutsideChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(200, 100, 100, 100));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+
+ occlusion.useDefaultLayerClipRect();
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestViewportRectOutsideChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerClipRectOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(100, 100, 100, 100));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerClipRectOverChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestViewportRectOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(100, 100, 100, 100));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestViewportRectOverChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerClipRectPartlyOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(50, 50, 200, 200));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerClipRectPartlyOverChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestViewportRectPartlyOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(50, 50, 200, 200));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestViewportRectPartlyOverChild);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerClipRectOverNothing : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(500, 500, 100, 100));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)).isEmpty());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerClipRectOverNothing);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestViewportRectOverNothing : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(500, 500, 100, 100));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)).isEmpty());
+ EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)).isEmpty());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestViewportRectOverNothing);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerClipRectForLayerOffOrigin : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->enterLayer(layer, occlusion);
+
+ // This layer is translated when drawn into its target. So if the clip rect given from the target surface
+ // is not in that target space, then after translating these query rects into the target, they will fall outside
+ // the clip and be considered occluded.
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerClipRectForLayerOffOrigin);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestOpaqueContentsRegionEmpty : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), false);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100)));
+
+ // Occluded since its outside the surface bounds.
+ EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+
+ // Test without any clip rect.
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+ EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100)));
+ occlusion.useDefaultLayerClipRect();
+
+ this->leaveLayer(layer, occlusion);
+ this->visitContributingSurface(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().bounds().isEmpty());
+ EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size());
+ }
+};
+
+MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestOpaqueContentsRegionEmpty);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestOpaqueContentsRegionNonEmpty : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 100), IntSize(200, 200), false);
+ this->calcDrawEtc(parent);
+
+ {
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ layer->setOpaqueContentsRect(IntRect(0, 0, 100, 100));
+
+ this->resetLayerIterator();
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(100, 100, 100, 100), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+ }
+
+ {
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ layer->setOpaqueContentsRect(IntRect(20, 20, 180, 180));
+
+ this->resetLayerIterator();
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(120, 120, 180, 180), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+ }
+
+ {
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ layer->setOpaqueContentsRect(IntRect(150, 150, 100, 100));
+
+ this->resetLayerIterator();
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(250, 250, 50, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 100, 100, 100)));
+ EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100)));
+ }
+ }
+};
+
+MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestOpaqueContentsRegionNonEmpty);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTest3dTransform : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix transform;
+ transform.rotate3d(0, 30, 0);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(100, 100), IntSize(200, 200), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->enterLayer(layer, occlusion);
+
+ // The layer is rotated in 3d but without preserving 3d, so it only gets resized.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 200, 200), occlusion.unoccludedContentRect(layer, IntRect(0, 0, 200, 200)));
+ }
+};
+
+MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTest3dTransform);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestUnsorted3dLayers : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // Currently, the main thread layer iterator does not iterate over 3d items in
+ // sorted order, because layer sorting is not performed on the main thread.
+ // Because of this, the occlusion tracker cannot assume that a 3d layer occludes
+ // other layers that have not yet been iterated over. For now, the expected
+ // behavior is that a 3d layer simply does not add any occlusion to the occlusion
+ // tracker.
+
+ WebTransformationMatrix translationToFront;
+ translationToFront.translate3d(0, 0, -10);
+ WebTransformationMatrix translationToBack;
+ translationToFront.translate3d(0, 0, -100);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* child1 = this->createDrawingLayer(parent, translationToBack, FloatPoint(0, 0), IntSize(100, 100), true);
+ typename Types::ContentLayerType* child2 = this->createDrawingLayer(parent, translationToFront, FloatPoint(50, 50), IntSize(100, 100), true);
+ parent->setPreserves3D(true);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->visitLayer(child2, occlusion);
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+
+ this->visitLayer(child1, occlusion);
+ EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty());
+ EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty());
+ }
+};
+
+// This test will have different layer ordering on the impl thread; the test will only work on the main thread.
+MAIN_THREAD_TEST(CCOcclusionTrackerTestUnsorted3dLayers);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestPerspectiveTransform : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix transform;
+ transform.translate(150, 150);
+ transform.applyPerspective(400);
+ transform.rotate3d(1, 0, 0, -30);
+ transform.translate(-150, -150);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(100, 100), IntSize(200, 200), true);
+ container->setPreserves3D(true);
+ layer->setPreserves3D(true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->enterLayer(layer, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 200, 200), occlusion.unoccludedContentRect(layer, IntRect(0, 0, 200, 200)));
+ }
+};
+
+// This test requires accumulating occlusion of 3d layers, which are skipped by the occlusion tracker on the main thread. So this test should run on the impl thread.
+IMPL_THREAD_TEST(CCOcclusionTrackerTestPerspectiveTransform);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestPerspectiveTransformBehindCamera : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // This test is based on the platform/chromium/compositing/3d-corners.html layout test.
+ WebTransformationMatrix transform;
+ transform.translate(250, 50);
+ transform.applyPerspective(10);
+ transform.translate(-250, -50);
+ transform.translate(250, 50);
+ transform.rotate3d(1, 0, 0, -167);
+ transform.translate(-250, -50);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(500, 100));
+ typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(500, 500));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(0, 0), IntSize(500, 500), true);
+ container->setPreserves3D(true);
+ layer->setPreserves3D(true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ this->enterLayer(layer, occlusion);
+
+ // The bottom 11 pixel rows of this layer remain visible inside the container, after translation to the target surface. When translated back,
+ // this will include many more pixels but must include at least the bottom 11 rows.
+ EXPECT_TRUE(occlusion.unoccludedContentRect(layer, IntRect(0, 0, 500, 500)).contains(IntRect(0, 489, 500, 11)));
+ }
+};
+
+// This test requires accumulating occlusion of 3d layers, which are skipped by the occlusion tracker on the main thread. So this test should run on the impl thread.
+IMPL_THREAD_TEST(CCOcclusionTrackerTestPerspectiveTransformBehindCamera);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLayerBehindCameraDoesNotOcclude : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix transform;
+ transform.translate(50, 50);
+ transform.applyPerspective(100);
+ transform.translate3d(0, 0, 110);
+ transform.translate(-50, -50);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, transform, FloatPoint(0, 0), IntSize(100, 100), true);
+ parent->setPreserves3D(true);
+ layer->setPreserves3D(true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ // The |layer| is entirely behind the camera and should not occlude.
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+ EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size());
+ }
+};
+
+// This test requires accumulating occlusion of 3d layers, which are skipped by the occlusion tracker on the main thread. So this test should run on the impl thread.
+IMPL_THREAD_TEST(CCOcclusionTrackerTestLayerBehindCameraDoesNotOcclude);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestLargePixelsOccludeInsideClipRect : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix transform;
+ transform.translate(50, 50);
+ transform.applyPerspective(100);
+ transform.translate3d(0, 0, 99);
+ transform.translate(-50, -50);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, transform, FloatPoint(0, 0), IntSize(100, 100), true);
+ parent->setPreserves3D(true);
+ layer->setPreserves3D(true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ // This is very close to the camera, so pixels in its visibleContentRect will actually go outside of the layer's clipRect.
+ // Ensure that those pixels don't occlude things outside the clipRect.
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ }
+};
+
+// This test requires accumulating occlusion of 3d layers, which are skipped by the occlusion tracker on the main thread. So this test should run on the impl thread.
+IMPL_THREAD_TEST(CCOcclusionTrackerTestLargePixelsOccludeInsideClipRect);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationOpacity1OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+ typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+ typename Types::ContentLayerType* parent2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false);
+ typename Types::ContentLayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 0), IntSize(50, 300), true);
+
+ addOpacityTransitionToController(*layer->layerAnimationController(), 10, 0, 1, false);
+ addOpacityTransitionToController(*surface->layerAnimationController(), 10, 0, 1, false);
+ this->calcDrawEtc(parent);
+
+ EXPECT_TRUE(layer->drawOpacityIsAnimating());
+ EXPECT_FALSE(surface->drawOpacityIsAnimating());
+ EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating());
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(topmost, occlusion);
+ this->enterLayer(parent2, occlusion);
+ // This occlusion will affect all surfaces.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent2, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(parent2, occlusion);
+
+ this->visitLayer(surfaceChild2, occlusion);
+ this->enterLayer(surfaceChild, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(100, 0, 100, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(surfaceChild, occlusion);
+ this->enterLayer(surface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(200, 0, 50, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(surface, occlusion);
+
+ this->enterContributingSurface(surface, occlusion);
+ // Occlusion within the surface is lost when leaving the animating surface.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+ this->leaveContributingSurface(surface, occlusion);
+
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ // Occlusion is not added for the animating |layer|.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity1OnMainThread);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationOpacity0OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+ typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+ typename Types::ContentLayerType* parent2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false);
+ typename Types::ContentLayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 0), IntSize(50, 300), true);
+
+ addOpacityTransitionToController(*layer->layerAnimationController(), 10, 1, 0, false);
+ addOpacityTransitionToController(*surface->layerAnimationController(), 10, 1, 0, false);
+ this->calcDrawEtc(parent);
+
+ EXPECT_TRUE(layer->drawOpacityIsAnimating());
+ EXPECT_FALSE(surface->drawOpacityIsAnimating());
+ EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating());
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(topmost, occlusion);
+ this->enterLayer(parent2, occlusion);
+ // This occlusion will affect all surfaces.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(parent2, occlusion);
+
+ this->visitLayer(surfaceChild2, occlusion);
+ this->enterLayer(surfaceChild, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(100, 0, 100, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(surfaceChild, occlusion);
+ this->enterLayer(surface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(200, 0, 50, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(surface, occlusion);
+
+ this->enterContributingSurface(surface, occlusion);
+ // Occlusion within the surface is lost when leaving the animating surface.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+ this->leaveContributingSurface(surface, occlusion);
+
+ this->visitLayer(layer, occlusion);
+ this->enterLayer(parent, occlusion);
+
+ // Occlusion is not added for the animating |layer|.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity0OnMainThread);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationTranslateOnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+ typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+ typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+ typename Types::ContentLayerType* surface2 = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(50, 300), true);
+
+ addAnimatedTransformToController(*layer->layerAnimationController(), 10, 30, 0);
+ addAnimatedTransformToController(*surface->layerAnimationController(), 10, 30, 0);
+ addAnimatedTransformToController(*surfaceChild->layerAnimationController(), 10, 30, 0);
+ this->calcDrawEtc(parent);
+
+ EXPECT_TRUE(layer->drawTransformIsAnimating());
+ EXPECT_TRUE(layer->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(surface->renderSurface()->targetSurfaceTransformsAreAnimating());
+ EXPECT_TRUE(surface->renderSurface()->screenSpaceTransformsAreAnimating());
+ // The surface owning layer doesn't animate against its own surface.
+ EXPECT_FALSE(surface->drawTransformIsAnimating());
+ EXPECT_TRUE(surface->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(surfaceChild->drawTransformIsAnimating());
+ EXPECT_TRUE(surfaceChild->screenSpaceTransformIsAnimating());
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface2, occlusion);
+ this->enterContributingSurface(surface2, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+
+ this->leaveContributingSurface(surface2, occlusion);
+ this->enterLayer(surfaceChild2, occlusion);
+
+ // surfaceChild2 is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+ // It also means that things occluding in screen space (e.g. surface2) cannot occlude this layer.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 300), occlusion.unoccludedContentRect(surfaceChild2, IntRect(0, 0, 100, 300)));
+ EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300)));
+
+ this->leaveLayer(surfaceChild2, occlusion);
+ this->enterLayer(surfaceChild, occlusion);
+ EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 100, 300)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+ // The surfaceChild is occluded by the surfaceChild2, but is moving relative its target and the screen, so it
+ // can't be occluded.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 200, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 200, 300)));
+ EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300)));
+
+ this->leaveLayer(surfaceChild, occlusion);
+ this->enterLayer(surface, occlusion);
+ // The surfaceChild is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+ this->leaveLayer(surface, occlusion);
+ // The surface's owning layer is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 300), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+ this->enterContributingSurface(surface, occlusion);
+ // The contributing |surface| is animating so it can't be occluded.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+ this->leaveContributingSurface(surface, occlusion);
+
+ this->enterLayer(layer, occlusion);
+ // The |surface| is moving in the screen and in its target, so all occlusion within the surface is lost when leaving it.
+ EXPECT_INT_RECT_EQ(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ this->leaveLayer(layer, occlusion);
+
+ this->enterLayer(parent, occlusion);
+ // The |layer| is animating in the screen and in its target, so no occlusion is added.
+ EXPECT_INT_RECT_EQ(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+ }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationTranslateOnMainThread);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceOcclusionTranslatesToParent : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix surfaceTransform;
+ surfaceTransform.translate(300, 300);
+ surfaceTransform.scale(2);
+ surfaceTransform.translate(-150, -150);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(500, 500));
+ typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, surfaceTransform, FloatPoint(0, 0), IntSize(300, 300), false);
+ typename Types::ContentLayerType* surface2 = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(50, 50), IntSize(300, 300), false);
+ surface->setOpaqueContentsRect(IntRect(0, 0, 200, 200));
+ surface2->setOpaqueContentsRect(IntRect(0, 0, 200, 200));
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface2, occlusion);
+ this->visitContributingSurface(surface2, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 200, 200), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(50, 50, 200, 200), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // Clear any stored occlusion.
+ occlusion.setOcclusionInScreenSpace(Region());
+ occlusion.setOcclusionInTargetSurface(Region());
+
+ this->visitLayer(surface, occlusion);
+ this->visitContributingSurface(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 400, 400), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 400, 400), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestSurfaceOcclusionTranslatesToParent);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+ typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(500, 300), false);
+ surface->setOpaqueContentsRect(IntRect(0, 0, 400, 200));
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface, occlusion);
+ this->visitContributingSurface(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 200), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 200), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReplicaOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+ this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+ typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // |topmost| occludes the replica, but not the surface itself.
+ this->visitLayer(topmost, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 100, 100), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 100, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 200), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->enterContributingSurface(surface, occlusion);
+
+ // Surface is not occluded so it shouldn't think it is.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaOccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+ this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+ typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 110), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // |topmost| occludes the surface, but not the entire surface's replica.
+ this->visitLayer(topmost, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 110), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 110), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 110), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->enterContributingSurface(surface, occlusion);
+
+ // Surface is occluded, but only the top 10px of the replica.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 0, 0), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 10, 100, 90), occlusion.unoccludedContributingSurfaceContentRect(surface, true, IntRect(0, 0, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+ this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+ typename Types::LayerType* overSurface = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(40, 100), true);
+ typename Types::LayerType* overReplica = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 100), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // These occlude the surface and replica differently, so we can test each one.
+ this->visitLayer(overReplica, occlusion);
+ this->visitLayer(overSurface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 200), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 200), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(surface, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 200), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->enterContributingSurface(surface, occlusion);
+
+ // Surface and replica are occluded different amounts.
+ EXPECT_INT_RECT_EQ(IntRect(40, 0, 60, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(50, 0, 50, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, true, IntRect(0, 0, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceChildOfSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // This test verifies that the surface cliprect does not end up empty and clip away the entire unoccluded rect.
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+ typename Types::LayerType* surfaceChild = this->createDrawingSurface(surface, this->identityMatrix, FloatPoint(0, 10), IntSize(100, 50), true);
+ typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 50), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(-100, -100, 1000, 1000));
+
+ // |topmost| occludes everything partially so we know occlusion is happening at all.
+ this->visitLayer(topmost, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(surfaceChild, occlusion);
+
+ // surfaceChild increases the occlusion in the screen by a narrow sliver.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ // In its own surface, surfaceChild is at 0,0 as is its occlusion.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The root layer always has a clipRect. So the parent of |surface| has a clipRect. However, the owning layer for |surface| does not
+ // mask to bounds, so it doesn't have a clipRect of its own. Thus the parent of |surfaceChild| exercises different code paths
+ // as its parent does not have a clipRect.
+
+ this->enterContributingSurface(surfaceChild, occlusion);
+ // The surfaceChild's parent does not have a clipRect as it owns a render surface. Make sure the unoccluded rect
+ // does not get clipped away inappropriately.
+ EXPECT_INT_RECT_EQ(IntRect(0, 40, 100, 10), occlusion.unoccludedContributingSurfaceContentRect(surfaceChild, false, IntRect(0, 0, 100, 50)));
+ this->leaveContributingSurface(surfaceChild, occlusion);
+
+ // When the surfaceChild's occlusion is transformed up to its parent, make sure it is not clipped away inappropriately also.
+ this->enterLayer(surface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 60), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 10, 100, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ this->leaveLayer(surface, occlusion);
+
+ this->enterContributingSurface(surface, occlusion);
+ // The surface's parent does have a clipRect as it is the root layer.
+ EXPECT_INT_RECT_EQ(IntRect(0, 50, 100, 50), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfSurface);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestTopmostSurfaceIsClippedToViewport : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // This test verifies that the top-most surface is considered occluded outside of its target's clipRect and outside the viewport rect.
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+ this->calcDrawEtc(parent);
+
+ {
+ // Make a viewport rect that is larger than the root layer.
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(surface, occlusion);
+
+ // The root layer always has a clipRect. So the parent of |surface| has a clipRect giving the surface itself a clipRect.
+ this->enterContributingSurface(surface, occlusion);
+ // Make sure the parent's clipRect clips the unoccluded region of the child surface.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 200), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 300)));
+ }
+ this->resetLayerIterator();
+ {
+ // Make a viewport rect that is smaller than the root layer.
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 100, 100));
+
+ this->visitLayer(surface, occlusion);
+
+ // The root layer always has a clipRect. So the parent of |surface| has a clipRect giving the surface itself a clipRect.
+ this->enterContributingSurface(surface, occlusion);
+ // Make sure the viewport rect clips the unoccluded region of the child surface.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 300)));
+ }
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestTopmostSurfaceIsClippedToViewport);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceChildOfClippingSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // This test verifies that the surface cliprect does not end up empty and clip away the entire unoccluded rect.
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(80, 200));
+ typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+ typename Types::LayerType* surfaceChild = this->createDrawingSurface(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), false);
+ typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 50), true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // |topmost| occludes everything partially so we know occlusion is happening at all.
+ this->visitLayer(topmost, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 80, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 80, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // surfaceChild is not opaque and does not occlude, so we have a non-empty unoccluded area on surface.
+ this->visitLayer(surfaceChild, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 80, 50), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The root layer always has a clipRect. So the parent of |surface| has a clipRect. However, the owning layer for |surface| does not
+ // mask to bounds, so it doesn't have a clipRect of its own. Thus the parent of |surfaceChild| exercises different code paths
+ // as its parent does not have a clipRect.
+
+ this->enterContributingSurface(surfaceChild, occlusion);
+ // The surfaceChild's parent does not have a clipRect as it owns a render surface.
+ EXPECT_INT_RECT_EQ(IntRect(0, 50, 80, 50), occlusion.unoccludedContributingSurfaceContentRect(surfaceChild, false, IntRect(0, 0, 100, 100)));
+ this->leaveContributingSurface(surfaceChild, occlusion);
+
+ this->visitLayer(surface, occlusion);
+ this->enterContributingSurface(surface, occlusion);
+ // The surface's parent does have a clipRect as it is the root layer.
+ EXPECT_INT_RECT_EQ(IntRect(0, 50, 80, 50), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfClippingSurface);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilter : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix scaleByHalf;
+ scaleByHalf.scale(0.5);
+
+ // Make a surface and its replica, each 50x50, that are completely surrounded by opaque layers which are above them in the z-order.
+ // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface
+ // appears at 50, 50 and the replica at 200, 50.
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150));
+ typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false);
+ this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize());
+ typename Types::LayerType* occludingLayer1 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 50), true);
+ typename Types::LayerType* occludingLayer2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(300, 50), true);
+ typename Types::LayerType* occludingLayer3 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 50), IntSize(50, 50), true);
+ typename Types::LayerType* occludingLayer4 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 50), IntSize(100, 50), true);
+ typename Types::LayerType* occludingLayer5 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 50), IntSize(50, 50), true);
+
+ // Filters make the layer own a surface.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(10));
+ filteredSurface->setBackgroundFilters(filters);
+
+ // Save the distance of influence for the blur effect.
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // These layers occlude pixels directly beside the filteredSurface. Because filtered surface blends pixels in a radius, it will
+ // need to see some of the pixels (up to radius far) underneath the occludingLayers.
+ this->visitLayer(occludingLayer5, occlusion);
+ this->visitLayer(occludingLayer4, occlusion);
+ this->visitLayer(occludingLayer3, occlusion);
+ this->visitLayer(occludingLayer2, occlusion);
+ this->visitLayer(occludingLayer1, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // Everything outside the surface/replica is occluded but the surface/replica itself is not.
+ this->enterLayer(filteredSurface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(1, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(1, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 1, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, 1, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(-1, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, -1, 100, 100)));
+
+ EXPECT_INT_RECT_EQ(IntRect(300 + 1, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 1, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(300 + 0, 1, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 0, 1, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(300 + 0, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 - 1, 0, 100, 100)));
+ EXPECT_INT_RECT_EQ(IntRect(300 + 0, 0, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 0, -1, 100, 100)));
+ this->leaveLayer(filteredSurface, occlusion);
+
+ // The filtered layer/replica does not occlude.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The surface has a background blur, so it needs pixels that are currently considered occluded in order to be drawn. So the pixels
+ // it needs should be removed some the occluded area so that when we get to the parent they are drawn.
+ this->visitContributingSurface(filteredSurface, occlusion);
+
+ this->enterLayer(parent, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size());
+
+ IntRect outsetRect;
+ IntRect testRect;
+
+ // Nothing in the blur outsets for the filteredSurface is occluded.
+ outsetRect = IntRect(50 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom);
+ testRect = outsetRect;
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+
+ // Stuff outside the blur outsets is still occluded though.
+ testRect = outsetRect;
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.move(-1, 0);
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.move(0, -1);
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+
+ // Nothing in the blur outsets for the filteredSurface's replica is occluded.
+ outsetRect = IntRect(200 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom);
+ testRect = outsetRect;
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+
+ // Stuff outside the blur outsets is still occluded though.
+ testRect = outsetRect;
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.move(-1, 0);
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ testRect = outsetRect;
+ testRect.move(0, -1);
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(outsetRect, occlusion.unoccludedContentRect(parent, testRect));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilter);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestTwoBackgroundFiltersReduceOcclusionTwice : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix scaleByHalf;
+ scaleByHalf.scale(0.5);
+
+ // Makes two surfaces that completely cover |parent|. The occlusion both above and below the filters will be reduced by each of them.
+ typename Types::ContentLayerType* root = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(75, 75));
+ typename Types::LayerType* parent = this->createSurface(root, scaleByHalf, FloatPoint(0, 0), IntSize(150, 150));
+ parent->setMasksToBounds(true);
+ typename Types::LayerType* filteredSurface1 = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(0, 0), IntSize(300, 300), false);
+ typename Types::LayerType* filteredSurface2 = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(0, 0), IntSize(300, 300), false);
+ typename Types::LayerType* occludingLayerAbove = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 100), IntSize(50, 50), true);
+
+ // Filters make the layers own surfaces.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(3));
+ filteredSurface1->setBackgroundFilters(filters);
+ filteredSurface2->setBackgroundFilters(filters);
+
+ // Save the distance of influence for the blur effect.
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ this->calcDrawEtc(root);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(occludingLayerAbove, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(100 / 2, 100 / 2, 50 / 2, 50 / 2), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(100, 100, 50, 50), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ this->visitLayer(filteredSurface2, occlusion);
+ this->visitContributingSurface(filteredSurface2, occlusion);
+ this->visitLayer(filteredSurface1, occlusion);
+ this->visitContributingSurface(filteredSurface1, occlusion);
+
+ ASSERT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ ASSERT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // Test expectations in the target.
+ IntRect expectedOcclusion = IntRect(100 + outsetRight * 2, 100 + outsetBottom * 2, 50 - (outsetLeft + outsetRight) * 2, 50 - (outsetTop + outsetBottom) * 2);
+ EXPECT_INT_RECT_EQ(expectedOcclusion, occlusion.occlusionInTargetSurface().rects()[0]);
+
+ // Test expectations in the screen. Take the ceiling of half of the outsets.
+ outsetTop = (outsetTop + 1) / 2;
+ outsetRight = (outsetRight + 1) / 2;
+ outsetBottom = (outsetBottom + 1) / 2;
+ outsetLeft = (outsetLeft + 1) / 2;
+ expectedOcclusion = IntRect(100 / 2 + outsetRight * 2, 100 / 2 + outsetBottom * 2, 50 / 2 - (outsetLeft + outsetRight) * 2, 50 /2 - (outsetTop + outsetBottom) * 2);
+
+ EXPECT_INT_RECT_EQ(expectedOcclusion, occlusion.occlusionInScreenSpace().rects()[0]);
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestTwoBackgroundFiltersReduceOcclusionTwice);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilterWithClip : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ // Make a surface and its replica, each 50x50, that are completely surrounded by opaque layers which are above them in the z-order.
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150));
+ // We stick the filtered surface inside a clipping surface so that we can make sure the clip is honored when exposing pixels for
+ // the background filter.
+ typename Types::LayerType* clippingSurface = this->createSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 70));
+ clippingSurface->setMasksToBounds(true);
+ typename Types::LayerType* filteredSurface = this->createDrawingLayer(clippingSurface, this->identityMatrix, FloatPoint(50, 50), IntSize(50, 50), false);
+ this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(150, 0), IntSize());
+ typename Types::LayerType* occludingLayer1 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 50), true);
+ typename Types::LayerType* occludingLayer2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(300, 50), true);
+ typename Types::LayerType* occludingLayer3 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 50), IntSize(50, 50), true);
+ typename Types::LayerType* occludingLayer4 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 50), IntSize(100, 50), true);
+ typename Types::LayerType* occludingLayer5 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 50), IntSize(50, 50), true);
+
+ // Filters make the layer own a surface. This filter is large enough that it goes outside the bottom of the clippingSurface.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(12));
+ filteredSurface->setBackgroundFilters(filters);
+
+ // Save the distance of influence for the blur effect.
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // These layers occlude pixels directly beside the filteredSurface. Because filtered surface blends pixels in a radius, it will
+ // need to see some of the pixels (up to radius far) underneath the occludingLayers.
+ this->visitLayer(occludingLayer5, occlusion);
+ this->visitLayer(occludingLayer4, occlusion);
+ this->visitLayer(occludingLayer3, occlusion);
+ this->visitLayer(occludingLayer2, occlusion);
+ this->visitLayer(occludingLayer1, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // Everything outside the surface/replica is occluded but the surface/replica itself is not.
+ this->enterLayer(filteredSurface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(1, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(1, 0, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 1, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, 1, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(-1, 0, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, -1, 50, 50)));
+
+ EXPECT_INT_RECT_EQ(IntRect(150 + 1, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 1, 0, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(150 + 0, 1, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 0, 1, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(150 + 0, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 - 1, 0, 50, 50)));
+ EXPECT_INT_RECT_EQ(IntRect(150 + 0, 0, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 0, -1, 50, 50)));
+ this->leaveLayer(filteredSurface, occlusion);
+
+ // The filtered layer/replica does not occlude.
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The surface has a background blur, so it needs pixels that are currently considered occluded in order to be drawn. So the pixels
+ // it needs should be removed some the occluded area so that when we get to the parent they are drawn.
+ this->visitContributingSurface(filteredSurface, occlusion);
+
+ this->enterContributingSurface(clippingSurface, occlusion);
+ EXPECT_INT_RECT_EQ(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size());
+
+ IntRect outsetRect;
+ IntRect clippedOutsetRect;
+ IntRect testRect;
+
+ // Nothing in the (clipped) blur outsets for the filteredSurface is occluded.
+ outsetRect = IntRect(50 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom);
+ clippedOutsetRect = intersection(outsetRect, IntRect(0 - outsetLeft, 0 - outsetTop, 300 + outsetLeft + outsetRight, 70 + outsetTop + outsetBottom));
+ testRect = outsetRect;
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+
+ // Stuff outside the (clipped) blur outsets is still occluded though.
+ testRect = outsetRect;
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.move(-1, 0);
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.move(0, -1);
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+
+ // Nothing in the (clipped) blur outsets for the filteredSurface's replica is occluded.
+ outsetRect = IntRect(200 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom);
+ clippedOutsetRect = intersection(outsetRect, IntRect(0 - outsetLeft, 0 - outsetTop, 300 + outsetLeft + outsetRight, 70 + outsetTop + outsetBottom));
+ testRect = outsetRect;
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+
+ // Stuff outside the (clipped) blur outsets is still occluded though.
+ testRect = outsetRect;
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.move(-1, 0);
+ testRect.expand(1, 0);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ testRect = outsetRect;
+ testRect.move(0, -1);
+ testRect.expand(0, 1);
+ EXPECT_INT_RECT_EQ(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect));
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilterWithClip);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestDontReduceOcclusionBelowBackgroundFilter : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix scaleByHalf;
+ scaleByHalf.scale(0.5);
+
+ // Make a surface and its replica, each 50x50, with a smaller 30x30 layer centered below each.
+ // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface
+ // appears at 50, 50 and the replica at 200, 50.
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150));
+ typename Types::LayerType* behindSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(60, 60), IntSize(30, 30), true);
+ typename Types::LayerType* behindReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(210, 60), IntSize(30, 30), true);
+ typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false);
+ this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize());
+
+ // Filters make the layer own a surface.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(3));
+ filteredSurface->setBackgroundFilters(filters);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ // The surface has a background blur, so it blurs non-opaque pixels below it.
+ this->visitLayer(filteredSurface, occlusion);
+ this->visitContributingSurface(filteredSurface, occlusion);
+
+ this->visitLayer(behindReplicaLayer, occlusion);
+ this->visitLayer(behindSurfaceLayer, occlusion);
+
+ // The layers behind the surface are not blurred, and their occlusion does not change, until we leave the surface.
+ // So it should not be modified by the filter here.
+ IntRect occlusionBehindSurface = IntRect(60, 60, 30, 30);
+ IntRect occlusionBehindReplica = IntRect(210, 60, 30, 30);
+
+ IntRect expectedOpaqueBounds = unionRect(occlusionBehindSurface, occlusionBehindReplica);
+ EXPECT_INT_RECT_EQ(expectedOpaqueBounds, occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(expectedOpaqueBounds, occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontReduceOcclusionBelowBackgroundFilter);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestDontReduceOcclusionIfBackgroundFilterIsOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix scaleByHalf;
+ scaleByHalf.scale(0.5);
+
+ // Make a surface and its replica, each 50x50, that are completely occluded by opaque layers which are above them in the z-order.
+ // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface
+ // appears at 50, 50 and the replica at 200, 50.
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150));
+ typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false);
+ this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize());
+ typename Types::LayerType* aboveSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(50, 50), IntSize(50, 50), true);
+ typename Types::LayerType* aboveReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 50), IntSize(50, 50), true);
+
+ // Filters make the layer own a surface.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(3));
+ filteredSurface->setBackgroundFilters(filters);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(aboveReplicaLayer, occlusion);
+ this->visitLayer(aboveSurfaceLayer, occlusion);
+
+ // The surface has a background blur, so it blurs non-opaque pixels below it.
+ this->visitLayer(filteredSurface, occlusion);
+ this->visitContributingSurface(filteredSurface, occlusion);
+
+ // The filter is completely occluded, so it should not blur anything and reduce any occlusion.
+ IntRect occlusionAboveSurface = IntRect(50, 50, 50, 50);
+ IntRect occlusionAboveReplica = IntRect(200, 50, 50, 50);
+
+ IntRect expectedOpaqueBounds = unionRect(occlusionAboveSurface, occlusionAboveReplica);
+ EXPECT_INT_RECT_EQ(expectedOpaqueBounds, occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(expectedOpaqueBounds, occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontReduceOcclusionIfBackgroundFilterIsOccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReduceOcclusionWhenBackgroundFilterIsPartiallyOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ WebTransformationMatrix scaleByHalf;
+ scaleByHalf.scale(0.5);
+
+ // Make a surface and its replica, each 50x50, that are partially occluded by opaque layers which are above them in the z-order.
+ // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface
+ // appears at 50, 50 and the replica at 200, 50.
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150));
+ typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false);
+ this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize());
+ typename Types::LayerType* aboveSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(70, 50), IntSize(30, 50), true);
+ typename Types::LayerType* aboveReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 50), IntSize(30, 50), true);
+ typename Types::LayerType* besideSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(90, 40), IntSize(10, 10), true);
+ typename Types::LayerType* besideReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 40), IntSize(10, 10), true);
+
+ // Filters make the layer own a surface.
+ WebFilterOperations filters;
+ filters.append(WebFilterOperation::createBlurFilter(3));
+ filteredSurface->setBackgroundFilters(filters);
+
+ // Save the distance of influence for the blur effect.
+ int outsetTop, outsetRight, outsetBottom, outsetLeft;
+ filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft);
+
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+
+ this->visitLayer(besideReplicaLayer, occlusion);
+ this->visitLayer(besideSurfaceLayer, occlusion);
+ this->visitLayer(aboveReplicaLayer, occlusion);
+ this->visitLayer(aboveSurfaceLayer, occlusion);
+
+ // The surface has a background blur, so it blurs non-opaque pixels below it.
+ this->visitLayer(filteredSurface, occlusion);
+ this->visitContributingSurface(filteredSurface, occlusion);
+
+ // The filter in the surface and replica are partially unoccluded. Only the unoccluded parts should reduce occlusion.
+ // This means it will push back the occlusion that touches the unoccluded part (occlusionAbove___), but it will not
+ // touch occlusionBeside____ since that is not beside the unoccluded part of the surface, even though it is beside
+ // the occluded part of the surface.
+ IntRect occlusionAboveSurface = IntRect(70 + outsetRight, 50, 30 - outsetRight, 50);
+ IntRect occlusionAboveReplica = IntRect(200, 50, 30 - outsetLeft, 50);
+ IntRect occlusionBesideSurface = IntRect(90, 40, 10, 10);
+ IntRect occlusionBesideReplica = IntRect(200, 40, 10, 10);
+
+ Region expectedOcclusion;
+ expectedOcclusion.unite(occlusionAboveSurface);
+ expectedOcclusion.unite(occlusionAboveReplica);
+ expectedOcclusion.unite(occlusionBesideSurface);
+ expectedOcclusion.unite(occlusionBesideReplica);
+
+ ASSERT_EQ(expectedOcclusion.rects().size(), occlusion.occlusionInTargetSurface().rects().size());
+ ASSERT_EQ(expectedOcclusion.rects().size(), occlusion.occlusionInScreenSpace().rects().size());
+
+ for (size_t i = 0; i < expectedOcclusion.rects().size(); ++i) {
+ IntRect expectedRect = expectedOcclusion.rects()[i];
+ IntRect screenRect = occlusion.occlusionInScreenSpace().rects()[i];
+ IntRect targetRect = occlusion.occlusionInTargetSurface().rects()[i];
+ EXPECT_EQ(expectedRect, screenRect);
+ EXPECT_EQ(expectedRect, targetRect);
+ }
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReduceOcclusionWhenBackgroundFilterIsPartiallyOccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestMinimumTrackingSize : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+ void runMyTest()
+ {
+ IntSize trackingSize(100, 100);
+ IntSize belowTrackingSize(99, 99);
+
+ typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(400, 400));
+ typename Types::LayerType* large = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), trackingSize, true);
+ typename Types::LayerType* small = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), belowTrackingSize, true);
+ this->calcDrawEtc(parent);
+
+ TestCCOcclusionTrackerWithClip<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+ occlusion.setLayerClipRect(IntRect(0, 0, 1000, 1000));
+ occlusion.setMinimumTrackingSize(trackingSize);
+
+ // The small layer is not tracked because it is too small.
+ this->visitLayer(small, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+ // The large layer is tracked as it is large enough.
+ this->visitLayer(large, occlusion);
+
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(), trackingSize), occlusion.occlusionInScreenSpace().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(), trackingSize), occlusion.occlusionInTargetSurface().bounds());
+ EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+ }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestMinimumTrackingSize);
+
+} // namespace
diff --git a/cc/CCOverdrawMetrics.cpp b/cc/CCOverdrawMetrics.cpp
new file mode 100644
index 0000000..51fbae6
--- /dev/null
+++ b/cc/CCOverdrawMetrics.cpp
@@ -0,0 +1,190 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCOverdrawMetrics.h"
+
+#include "CCLayerTreeHost.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCMathUtil.h"
+#include "FloatQuad.h"
+#include "IntRect.h"
+#include "TraceEvent.h"
+#include <public/Platform.h>
+#include <public/WebTransformationMatrix.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+CCOverdrawMetrics::CCOverdrawMetrics(bool recordMetricsForFrame)
+ : m_recordMetricsForFrame(recordMetricsForFrame)
+ , m_pixelsPainted(0)
+ , m_pixelsUploadedOpaque(0)
+ , m_pixelsUploadedTranslucent(0)
+ , m_tilesCulledForUpload(0)
+ , m_contentsTextureUseBytes(0)
+ , m_renderSurfaceTextureUseBytes(0)
+ , m_pixelsDrawnOpaque(0)
+ , m_pixelsDrawnTranslucent(0)
+ , m_pixelsCulledForDrawing(0)
+{
+}
+
+static inline float wedgeProduct(const FloatPoint& p1, const FloatPoint& p2)
+{
+ return p1.x() * p2.y() - p1.y() * p2.x();
+}
+
+// Calculates area of an arbitrary convex polygon with up to 8 points.
+static inline float polygonArea(const FloatPoint points[8], int numPoints)
+{
+ if (numPoints < 3)
+ return 0;
+
+ float area = 0;
+ for (int i = 0; i < numPoints; ++i)
+ area += wedgeProduct(points[i], points[(i+1)%numPoints]);
+ return fabs(0.5f * area);
+}
+
+// Takes a given quad, maps it by the given transformation, and gives the area of the resulting polygon.
+static inline float areaOfMappedQuad(const WebTransformationMatrix& transform, const FloatQuad& quad)
+{
+ FloatPoint clippedQuad[8];
+ int numVerticesInClippedQuad = 0;
+ CCMathUtil::mapClippedQuad(transform, quad, clippedQuad, numVerticesInClippedQuad);
+ return polygonArea(clippedQuad, numVerticesInClippedQuad);
+}
+
+void CCOverdrawMetrics::didPaint(const IntRect& paintedRect)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ m_pixelsPainted += static_cast<float>(paintedRect.width()) * paintedRect.height();
+}
+
+void CCOverdrawMetrics::didCullTileForUpload()
+{
+ if (m_recordMetricsForFrame)
+ ++m_tilesCulledForUpload;
+}
+
+void CCOverdrawMetrics::didUpload(const WebTransformationMatrix& transformToTarget, const IntRect& uploadRect, const IntRect& opaqueRect)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ float uploadArea = areaOfMappedQuad(transformToTarget, FloatQuad(uploadRect));
+ float uploadOpaqueArea = areaOfMappedQuad(transformToTarget, FloatQuad(intersection(opaqueRect, uploadRect)));
+
+ m_pixelsUploadedOpaque += uploadOpaqueArea;
+ m_pixelsUploadedTranslucent += uploadArea - uploadOpaqueArea;
+}
+
+void CCOverdrawMetrics::didUseContentsTextureMemoryBytes(size_t contentsTextureUseBytes)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ m_contentsTextureUseBytes += contentsTextureUseBytes;
+}
+
+void CCOverdrawMetrics::didUseRenderSurfaceTextureMemoryBytes(size_t renderSurfaceUseBytes)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ m_renderSurfaceTextureUseBytes += renderSurfaceUseBytes;
+}
+
+void CCOverdrawMetrics::didCullForDrawing(const WebTransformationMatrix& transformToTarget, const IntRect& beforeCullRect, const IntRect& afterCullRect)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ float beforeCullArea = areaOfMappedQuad(transformToTarget, FloatQuad(beforeCullRect));
+ float afterCullArea = areaOfMappedQuad(transformToTarget, FloatQuad(afterCullRect));
+
+ m_pixelsCulledForDrawing += beforeCullArea - afterCullArea;
+}
+
+void CCOverdrawMetrics::didDraw(const WebTransformationMatrix& transformToTarget, const IntRect& afterCullRect, const IntRect& opaqueRect)
+{
+ if (!m_recordMetricsForFrame)
+ return;
+
+ float afterCullArea = areaOfMappedQuad(transformToTarget, FloatQuad(afterCullRect));
+ float afterCullOpaqueArea = areaOfMappedQuad(transformToTarget, FloatQuad(intersection(opaqueRect, afterCullRect)));
+
+ m_pixelsDrawnOpaque += afterCullOpaqueArea;
+ m_pixelsDrawnTranslucent += afterCullArea - afterCullOpaqueArea;
+}
+
+void CCOverdrawMetrics::recordMetrics(const CCLayerTreeHost* layerTreeHost) const
+{
+ if (m_recordMetricsForFrame)
+ recordMetricsInternal<CCLayerTreeHost>(UpdateAndCommit, layerTreeHost);
+}
+
+void CCOverdrawMetrics::recordMetrics(const CCLayerTreeHostImpl* layerTreeHost) const
+{
+ if (m_recordMetricsForFrame)
+ recordMetricsInternal<CCLayerTreeHostImpl>(DrawingToScreen, layerTreeHost);
+}
+
+template<typename LayerTreeHostType>
+void CCOverdrawMetrics::recordMetricsInternal(MetricsType metricsType, const LayerTreeHostType* layerTreeHost) const
+{
+ // This gives approximately 10x the percentage of pixels to fill the viewport once.
+ float normalization = 1000.f / (layerTreeHost->deviceViewportSize().width() * layerTreeHost->deviceViewportSize().height());
+ // This gives approximately 100x the percentage of tiles to fill the viewport once, if all tiles were 256x256.
+ float tileNormalization = 10000.f / (layerTreeHost->deviceViewportSize().width() / 256.f * layerTreeHost->deviceViewportSize().height() / 256.f);
+ // This gives approximately 10x the percentage of bytes to fill the viewport once, assuming 4 bytes per pixel.
+ float byteNormalization = normalization / 4;
+
+ switch (metricsType) {
+ case DrawingToScreen:
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountOpaque_Draw", static_cast<int>(normalization * m_pixelsDrawnOpaque), 100, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountTranslucent_Draw", static_cast<int>(normalization * m_pixelsDrawnTranslucent), 100, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountCulled_Draw", static_cast<int>(normalization * m_pixelsCulledForDrawing), 100, 1000000, 50);
+
+ {
+ TRACE_COUNTER_ID1("cc", "DrawPixelsCulled", layerTreeHost, m_pixelsCulledForDrawing);
+ TRACE_EVENT2("cc", "CCOverdrawMetrics", "PixelsDrawnOpaque", m_pixelsDrawnOpaque, "PixelsDrawnTranslucent", m_pixelsDrawnTranslucent);
+ }
+ break;
+ case UpdateAndCommit:
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountPainted", static_cast<int>(normalization * m_pixelsPainted), 100, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountOpaque_Upload", static_cast<int>(normalization * m_pixelsUploadedOpaque), 100, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.pixelCountTranslucent_Upload", static_cast<int>(normalization * m_pixelsUploadedTranslucent), 100, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.tileCountCulled_Upload", static_cast<int>(tileNormalization * m_tilesCulledForUpload), 100, 10000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.renderSurfaceTextureBytes_ViewportScaled", static_cast<int>(byteNormalization * m_renderSurfaceTextureUseBytes), 10, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.renderSurfaceTextureBytes_Unscaled", static_cast<int>(m_renderSurfaceTextureUseBytes / 1000), 1000, 100000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.contentsTextureBytes_ViewportScaled", static_cast<int>(byteNormalization * m_contentsTextureUseBytes), 10, 1000000, 50);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.contentsTextureBytes_Unscaled", static_cast<int>(m_contentsTextureUseBytes / 1000), 1000, 100000000, 50);
+
+ {
+ TRACE_COUNTER_ID1("cc", "UploadTilesCulled", layerTreeHost, m_tilesCulledForUpload);
+ TRACE_EVENT2("cc", "CCOverdrawMetrics", "PixelsUploadedOpaque", m_pixelsUploadedOpaque, "PixelsUploadedTranslucent", m_pixelsUploadedTranslucent);
+ }
+ {
+ // This must be in a different scope than the TRACE_EVENT2 above.
+ TRACE_EVENT1("cc", "CCOverdrawPaintMetrics", "PixelsPainted", m_pixelsPainted);
+ }
+ {
+ // This must be in a different scope than the TRACE_EVENTs above.
+ TRACE_EVENT2("cc", "CCOverdrawPaintMetrics", "ContentsTextureBytes", m_contentsTextureUseBytes, "RenderSurfaceTextureBytes", m_renderSurfaceTextureUseBytes);
+ }
+ break;
+ }
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCOverdrawMetrics.h b/cc/CCOverdrawMetrics.h
new file mode 100644
index 0000000..3c1a386
--- /dev/null
+++ b/cc/CCOverdrawMetrics.h
@@ -0,0 +1,97 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCOverdrawMetrics_h
+#define CCOverdrawMetrics_h
+
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+class WebTransformationMatrix;
+}
+
+namespace WebCore {
+class IntRect;
+class CCLayerTreeHost;
+class CCLayerTreeHostImpl;
+
+// FIXME: compute overdraw metrics only occasionally, not every frame.
+class CCOverdrawMetrics {
+public:
+ static PassOwnPtr<CCOverdrawMetrics> create(bool recordMetricsForFrame) { return adoptPtr(new CCOverdrawMetrics(recordMetricsForFrame)); }
+
+ // These methods are used for saving metrics during update/commit.
+
+ // Record pixels painted by WebKit into the texture updater, but does not mean the pixels were rasterized in main memory.
+ void didPaint(const IntRect& paintedRect);
+ // Records that an invalid tile was culled and did not need to be painted/uploaded, and did not contribute to other tiles needing to be painted.
+ void didCullTileForUpload();
+ // Records pixels that were uploaded to texture memory.
+ void didUpload(const WebKit::WebTransformationMatrix& transformToTarget, const IntRect& uploadRect, const IntRect& opaqueRect);
+ // Record contents texture(s) behind present using the given number of bytes.
+ void didUseContentsTextureMemoryBytes(size_t contentsTextureUseBytes);
+ // Record RenderSurface texture(s) being present using the given number of bytes.
+ void didUseRenderSurfaceTextureMemoryBytes(size_t renderSurfaceUseBytes);
+
+ // These methods are used for saving metrics during draw.
+
+ // Record pixels that were not drawn to screen.
+ void didCullForDrawing(const WebKit::WebTransformationMatrix& transformToTarget, const IntRect& beforeCullRect, const IntRect& afterCullRect);
+ // Record pixels that were drawn to screen.
+ void didDraw(const WebKit::WebTransformationMatrix& transformToTarget, const IntRect& afterCullRect, const IntRect& opaqueRect);
+
+ void recordMetrics(const CCLayerTreeHost*) const;
+ void recordMetrics(const CCLayerTreeHostImpl*) const;
+
+ // Accessors for tests.
+ float pixelsDrawnOpaque() const { return m_pixelsDrawnOpaque; }
+ float pixelsDrawnTranslucent() const { return m_pixelsDrawnTranslucent; }
+ float pixelsCulledForDrawing() const { return m_pixelsCulledForDrawing; }
+ float pixelsPainted() const { return m_pixelsPainted; }
+ float pixelsUploadedOpaque() const { return m_pixelsUploadedOpaque; }
+ float pixelsUploadedTranslucent() const { return m_pixelsUploadedTranslucent; }
+ int tilesCulledForUpload() const { return m_tilesCulledForUpload; }
+
+private:
+ enum MetricsType {
+ UpdateAndCommit,
+ DrawingToScreen
+ };
+
+ explicit CCOverdrawMetrics(bool recordMetricsForFrame);
+
+ template<typename LayerTreeHostType>
+ void recordMetricsInternal(MetricsType, const LayerTreeHostType*) const;
+
+ // When false this class is a giant no-op.
+ bool m_recordMetricsForFrame;
+
+ // These values are used for saving metrics during update/commit.
+
+ // Count of pixels that were painted due to invalidation.
+ float m_pixelsPainted;
+ // Count of pixels uploaded to textures and known to be opaque.
+ float m_pixelsUploadedOpaque;
+ // Count of pixels uploaded to textures and not known to be opaque.
+ float m_pixelsUploadedTranslucent;
+ // Count of tiles that were invalidated but not uploaded.
+ int m_tilesCulledForUpload;
+ // Count the number of bytes in contents textures.
+ unsigned long long m_contentsTextureUseBytes;
+ // Count the number of bytes in RenderSurface textures.
+ unsigned long long m_renderSurfaceTextureUseBytes;
+
+ // These values are used for saving metrics during draw.
+
+ // Count of pixels that are opaque (and thus occlude). Ideally this is no more than wiewport width x height.
+ float m_pixelsDrawnOpaque;
+ // Count of pixels that are possibly translucent, and cannot occlude.
+ float m_pixelsDrawnTranslucent;
+ // Count of pixels not drawn as they are occluded by somthing opaque.
+ float m_pixelsCulledForDrawing;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCPageScaleAnimation.cpp b/cc/CCPageScaleAnimation.cpp
new file mode 100644
index 0000000..ab031924
--- /dev/null
+++ b/cc/CCPageScaleAnimation.cpp
@@ -0,0 +1,159 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCPageScaleAnimation.h"
+
+#include "FloatRect.h"
+#include "FloatSize.h"
+
+#include <math.h>
+
+namespace WebCore {
+
+PassOwnPtr<CCPageScaleAnimation> CCPageScaleAnimation::create(const IntSize& scrollStart, float pageScaleStart, const IntSize& windowSize, const IntSize& contentSize, double startTime)
+{
+ return adoptPtr(new CCPageScaleAnimation(scrollStart, pageScaleStart, windowSize, contentSize, startTime));
+}
+
+
+CCPageScaleAnimation::CCPageScaleAnimation(const IntSize& scrollStart, float pageScaleStart, const IntSize& windowSize, const IntSize& contentSize, double startTime)
+ : m_scrollStart(scrollStart)
+ , m_pageScaleStart(pageScaleStart)
+ , m_windowSize(windowSize)
+ , m_contentSize(contentSize)
+ , m_anchorMode(false)
+ , m_scrollEnd(scrollStart)
+ , m_pageScaleEnd(pageScaleStart)
+ , m_startTime(startTime)
+ , m_duration(0)
+{
+}
+
+void CCPageScaleAnimation::zoomTo(const IntSize& finalScroll, float finalPageScale, double duration)
+{
+ if (m_pageScaleStart != finalPageScale) {
+ // For uniform-looking zooming, infer the anchor (point that remains in
+ // place throughout the zoom) from the start and end rects.
+ FloatRect startRect(IntPoint(m_scrollStart), m_windowSize);
+ FloatRect endRect(IntPoint(finalScroll), m_windowSize);
+ endRect.scale(m_pageScaleStart / finalPageScale);
+
+ // The anchor is the point which is at the same ratio of the sides of
+ // both startRect and endRect. For example, a zoom-in double-tap to a
+ // perfectly centered rect will have anchor ratios (0.5, 0.5), while one
+ // to a rect touching the bottom-right of the screen will have anchor
+ // ratios (1.0, 1.0). In other words, it obeys the equations:
+ // anchorX = start_width * ratioX + start_x
+ // anchorX = end_width * ratioX + end_x
+ // anchorY = start_height * ratioY + start_y
+ // anchorY = end_height * ratioY + end_y
+ // where both anchor{x,y} and ratio{x,y} begin as unknowns. Solving
+ // for the ratios, we get the following formulas:
+ float ratioX = (startRect.x() - endRect.x()) / (endRect.width() - startRect.width());
+ float ratioY = (startRect.y() - endRect.y()) / (endRect.height() - startRect.height());
+
+ IntSize anchor(m_windowSize.width() * ratioX, m_windowSize.height() * ratioY);
+ zoomWithAnchor(anchor, finalPageScale, duration);
+ } else {
+ // If this is a pure translation, then there exists no anchor. Linearly
+ // interpolate the scroll offset instead.
+ m_scrollEnd = finalScroll;
+ m_pageScaleEnd = finalPageScale;
+ m_duration = duration;
+ m_anchorMode = false;
+ }
+}
+
+void CCPageScaleAnimation::zoomWithAnchor(const IntSize& anchor, float finalPageScale, double duration)
+{
+ m_scrollEnd = m_scrollStart + anchor;
+ m_scrollEnd.scale(finalPageScale / m_pageScaleStart);
+ m_scrollEnd -= anchor;
+
+ m_scrollEnd.clampNegativeToZero();
+ FloatSize scaledContentSize(m_contentSize);
+ scaledContentSize.scale(finalPageScale / m_pageScaleStart);
+ IntSize maxScrollPosition = roundedIntSize(scaledContentSize - m_windowSize);
+ m_scrollEnd = m_scrollEnd.shrunkTo(maxScrollPosition);
+
+ m_anchor = anchor;
+ m_pageScaleEnd = finalPageScale;
+ m_duration = duration;
+ m_anchorMode = true;
+}
+
+IntSize CCPageScaleAnimation::scrollOffsetAtTime(double time) const
+{
+ return scrollOffsetAtRatio(progressRatioForTime(time));
+}
+
+float CCPageScaleAnimation::pageScaleAtTime(double time) const
+{
+ return pageScaleAtRatio(progressRatioForTime(time));
+}
+
+bool CCPageScaleAnimation::isAnimationCompleteAtTime(double time) const
+{
+ return time >= endTime();
+}
+
+float CCPageScaleAnimation::progressRatioForTime(double time) const
+{
+ if (isAnimationCompleteAtTime(time))
+ return 1;
+
+ return (time - m_startTime) / m_duration;
+}
+
+IntSize CCPageScaleAnimation::scrollOffsetAtRatio(float ratio) const
+{
+ if (ratio <= 0)
+ return m_scrollStart;
+ if (ratio >= 1)
+ return m_scrollEnd;
+
+ float currentPageScale = pageScaleAtRatio(ratio);
+ IntSize currentScrollOffset;
+ if (m_anchorMode) {
+ // Keep the anchor stable on the screen at the current scale.
+ IntSize documentAnchor = m_scrollStart + m_anchor;
+ documentAnchor.scale(currentPageScale / m_pageScaleStart);
+ currentScrollOffset = documentAnchor - m_anchor;
+ } else {
+ // First move both scroll offsets to the current coordinate space.
+ FloatSize scaledStartScroll(m_scrollStart);
+ scaledStartScroll.scale(currentPageScale / m_pageScaleStart);
+ FloatSize scaledEndScroll(m_scrollEnd);
+ scaledEndScroll.scale(currentPageScale / m_pageScaleEnd);
+
+ // Linearly interpolate between them.
+ FloatSize delta = scaledEndScroll - scaledStartScroll;
+ delta.scale(ratio);
+ currentScrollOffset = roundedIntSize(scaledStartScroll + delta);
+ }
+
+ return currentScrollOffset;
+}
+
+float CCPageScaleAnimation::pageScaleAtRatio(float ratio) const
+{
+ if (ratio <= 0)
+ return m_pageScaleStart;
+ if (ratio >= 1)
+ return m_pageScaleEnd;
+
+ // Linearly interpolate the magnitude in log scale.
+ // Log scale is needed to maintain the appearance of uniform zoom. For
+ // example, if we zoom from 0.5 to 4.0 in 3 seconds, then we should
+ // be zooming by 2x every second.
+ float diff = m_pageScaleEnd / m_pageScaleStart;
+ float logDiff = log(diff);
+ logDiff *= ratio;
+ diff = exp(logDiff);
+ return m_pageScaleStart * diff;
+}
+
+} // namespace WebCore
diff --git a/cc/CCPageScaleAnimation.h b/cc/CCPageScaleAnimation.h
new file mode 100644
index 0000000..3e1c900
--- /dev/null
+++ b/cc/CCPageScaleAnimation.h
@@ -0,0 +1,74 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCPageScaleAnimation_h
+#define CCPageScaleAnimation_h
+
+#include "IntSize.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+// A small helper class that does the math for zoom animations, primarily for
+// double-tap zoom. Initialize it with starting and ending scroll/page scale
+// positions and an animation length time, then call ...AtTime() at every frame
+// to obtain the current interpolated position.
+class CCPageScaleAnimation {
+public:
+ // Construct with the starting page scale and scroll offset (which is in
+ // pageScaleStart space). The window size is the user-viewable area
+ // in pixels.
+ static PassOwnPtr<CCPageScaleAnimation> create(const IntSize& scrollStart, float pageScaleStart, const IntSize& windowSize, const IntSize& contentSize, double startTime);
+
+ // The following methods initialize the animation. Call one of them
+ // immediately after construction to set the final scroll and page scale.
+
+ // Zoom while explicitly specifying the top-left scroll position. The
+ // scroll offset is in finalPageScale coordinates.
+ void zoomTo(const IntSize& finalScroll, float finalPageScale, double duration);
+
+ // Zoom based on a specified onscreen anchor, which will remain at the same
+ // position on the screen throughout the animation. The anchor is in local
+ // space relative to scrollStart.
+ void zoomWithAnchor(const IntSize& anchor, float finalPageScale, double duration);
+
+ // Call these functions while the animation is in progress to output the
+ // current state.
+ IntSize scrollOffsetAtTime(double time) const;
+ float pageScaleAtTime(double time) const;
+ bool isAnimationCompleteAtTime(double time) const;
+
+ // The following methods return state which is invariant throughout the
+ // course of the animation.
+ double startTime() const { return m_startTime; }
+ double duration() const { return m_duration; }
+ double endTime() const { return m_startTime + m_duration; }
+ const IntSize& finalScrollOffset() const { return m_scrollEnd; }
+ float finalPageScale() const { return m_pageScaleEnd; }
+
+protected:
+ CCPageScaleAnimation(const IntSize& scrollStart, float pageScaleStart, const IntSize& windowSize, const IntSize& contentSize, double startTime);
+
+private:
+ float progressRatioForTime(double time) const;
+ IntSize scrollOffsetAtRatio(float ratio) const;
+ float pageScaleAtRatio(float ratio) const;
+
+ IntSize m_scrollStart;
+ float m_pageScaleStart;
+ IntSize m_windowSize;
+ IntSize m_contentSize;
+
+ bool m_anchorMode;
+ IntSize m_anchor;
+ IntSize m_scrollEnd;
+ float m_pageScaleEnd;
+
+ double m_startTime;
+ double m_duration;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCPrioritizedTexture.cpp b/cc/CCPrioritizedTexture.cpp
new file mode 100644
index 0000000..8a1e7a1
--- /dev/null
+++ b/cc/CCPrioritizedTexture.cpp
@@ -0,0 +1,122 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCPrioritizedTexture.h"
+
+#include "CCPrioritizedTextureManager.h"
+#include "CCPriorityCalculator.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace WebCore {
+
+CCPrioritizedTexture::CCPrioritizedTexture(CCPrioritizedTextureManager* manager, IntSize size, GC3Denum format)
+ : m_size(size)
+ , m_format(format)
+ , m_bytes(0)
+ , m_priority(CCPriorityCalculator::lowestPriority())
+ , m_isAbovePriorityCutoff(false)
+ , m_isSelfManaged(false)
+ , m_backing(0)
+ , m_manager(0)
+{
+ // m_manager is set in registerTexture() so validity can be checked.
+ ASSERT(format || size.isEmpty());
+ if (format)
+ m_bytes = CCTexture::memorySizeBytes(size, format);
+ if (manager)
+ manager->registerTexture(this);
+}
+
+CCPrioritizedTexture::~CCPrioritizedTexture()
+{
+ if (m_manager)
+ m_manager->unregisterTexture(this);
+}
+
+void CCPrioritizedTexture::setTextureManager(CCPrioritizedTextureManager* manager)
+{
+ if (m_manager == manager)
+ return;
+ if (m_manager)
+ m_manager->unregisterTexture(this);
+ if (manager)
+ manager->registerTexture(this);
+}
+
+void CCPrioritizedTexture::setDimensions(IntSize size, GC3Denum format)
+{
+ if (m_format != format || m_size != size) {
+ m_isAbovePriorityCutoff = false;
+ m_format = format;
+ m_size = size;
+ m_bytes = CCTexture::memorySizeBytes(size, format);
+ ASSERT(m_manager || !m_backing);
+ if (m_manager)
+ m_manager->returnBackingTexture(this);
+ }
+}
+
+bool CCPrioritizedTexture::requestLate()
+{
+ if (!m_manager)
+ return false;
+ return m_manager->requestLate(this);
+}
+
+void CCPrioritizedTexture::acquireBackingTexture(CCResourceProvider* resourceProvider)
+{
+ ASSERT(m_isAbovePriorityCutoff);
+ if (m_isAbovePriorityCutoff)
+ m_manager->acquireBackingTextureIfNeeded(this, resourceProvider);
+}
+
+CCResourceProvider::ResourceId CCPrioritizedTexture::resourceId() const
+{
+ if (m_backing)
+ return m_backing->id();
+ return 0;
+}
+
+void CCPrioritizedTexture::upload(CCResourceProvider* resourceProvider,
+ const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset)
+{
+ ASSERT(m_isAbovePriorityCutoff);
+ if (m_isAbovePriorityCutoff)
+ acquireBackingTexture(resourceProvider);
+ ASSERT(m_backing);
+ resourceProvider->upload(resourceId(), image, imageRect, sourceRect, destOffset);
+}
+
+void CCPrioritizedTexture::link(Backing* backing)
+{
+ ASSERT(backing);
+ ASSERT(!backing->m_owner);
+ ASSERT(!m_backing);
+
+ m_backing = backing;
+ m_backing->m_owner = this;
+}
+
+void CCPrioritizedTexture::unlink()
+{
+ ASSERT(m_backing);
+ ASSERT(m_backing->m_owner == this);
+
+ m_backing->m_owner = 0;
+ m_backing = 0;
+}
+
+void CCPrioritizedTexture::setToSelfManagedMemoryPlaceholder(size_t bytes)
+{
+ setDimensions(IntSize(), GraphicsContext3D::RGBA);
+ setIsSelfManaged(true);
+ m_bytes = bytes;
+}
+
+} // namespace WebCore
diff --git a/cc/CCPrioritizedTexture.h b/cc/CCPrioritizedTexture.h
new file mode 100644
index 0000000..ec90a49
--- /dev/null
+++ b/cc/CCPrioritizedTexture.h
@@ -0,0 +1,121 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCPrioritizedTexture_h
+#define CCPrioritizedTexture_h
+
+#include "CCPriorityCalculator.h"
+#include "CCResourceProvider.h"
+#include "CCTexture.h"
+#include "GraphicsContext3D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+
+namespace WebCore {
+
+class CCPrioritizedTextureManager;
+
+class CCPrioritizedTexture {
+ WTF_MAKE_NONCOPYABLE(CCPrioritizedTexture);
+public:
+ static PassOwnPtr<CCPrioritizedTexture> create(CCPrioritizedTextureManager* manager, IntSize size, GC3Denum format)
+ {
+ return adoptPtr(new CCPrioritizedTexture(manager, size, format));
+ }
+ static PassOwnPtr<CCPrioritizedTexture> create(CCPrioritizedTextureManager* manager)
+ {
+ return adoptPtr(new CCPrioritizedTexture(manager, IntSize(), 0));
+ }
+ ~CCPrioritizedTexture();
+
+ // Texture properties. Changing these causes the backing texture to be lost.
+ // Setting these to the same value is a no-op.
+ void setTextureManager(CCPrioritizedTextureManager*);
+ CCPrioritizedTextureManager* textureManager() { return m_manager; }
+ void setDimensions(IntSize, GC3Denum format);
+ GC3Denum format() const { return m_format; }
+ IntSize size() const { return m_size; }
+ size_t bytes() const { return m_bytes; }
+
+ // Set priority for the requested texture.
+ void setRequestPriority(int priority) { m_priority = priority; }
+ int requestPriority() const { return m_priority; }
+
+ // After CCPrioritizedTexture::prioritizeTextures() is called, this returns
+ // if the the request succeeded and this texture can be acquired for use.
+ bool canAcquireBackingTexture() const { return m_isAbovePriorityCutoff; }
+
+ // This returns whether we still have a backing texture. This can continue
+ // to be true even after canAcquireBackingTexture() becomes false. In this
+ // case the texture can be used but shouldn't be updated since it will get
+ // taken away "soon".
+ bool haveBackingTexture() const { return !!backing(); }
+
+ // If canAcquireBackingTexture() is true acquireBackingTexture() will acquire
+ // a backing texture for use. Call this whenever the texture is actually needed.
+ void acquireBackingTexture(CCResourceProvider*);
+
+ // FIXME: Request late is really a hack for when we are totally out of memory
+ // (all textures are visible) but we can still squeeze into the limit
+ // by not painting occluded textures. In this case the manager
+ // refuses all visible textures and requestLate() will enable
+ // canAcquireBackingTexture() on a call-order basis. We might want to
+ // just remove this in the future (carefully) and just make sure we don't
+ // regress OOMs situations.
+ bool requestLate();
+
+ // Uploads pixels into the backing resource. This functions will aquire the backing if needed.
+ void upload(CCResourceProvider*, const uint8_t* image, const IntRect& imageRect, const IntRect& sourceRect, const IntSize& destOffset);
+
+ CCResourceProvider::ResourceId resourceId() const;
+
+ // Self-managed textures are accounted for when prioritizing other textures,
+ // but they are not allocated/recycled/deleted, so this needs to be done
+ // externally. canAcquireBackingTexture() indicates if the texture would have
+ // been allowed given its priority.
+ void setIsSelfManaged(bool isSelfManaged) { m_isSelfManaged = isSelfManaged; }
+ bool isSelfManaged() { return m_isSelfManaged; }
+ void setToSelfManagedMemoryPlaceholder(size_t bytes);
+
+private:
+ friend class CCPrioritizedTextureManager;
+
+ class Backing : public CCTexture {
+ WTF_MAKE_NONCOPYABLE(Backing);
+ public:
+ Backing(unsigned id, IntSize size, GC3Denum format)
+ : CCTexture(id, size, format), m_owner(0) { }
+ ~Backing() { ASSERT(!m_owner); }
+
+ CCPrioritizedTexture* owner() { return m_owner; }
+ private:
+ friend class CCPrioritizedTexture;
+ CCPrioritizedTexture* m_owner;
+ };
+
+ CCPrioritizedTexture(CCPrioritizedTextureManager*, IntSize, GC3Denum format);
+
+ bool isAbovePriorityCutoff() { return m_isAbovePriorityCutoff; }
+ void setAbovePriorityCutoff(bool isAbovePriorityCutoff) { m_isAbovePriorityCutoff = isAbovePriorityCutoff; }
+ void setManagerInternal(CCPrioritizedTextureManager* manager) { m_manager = manager; }
+
+ Backing* backing() const { return m_backing; }
+ void link(Backing*);
+ void unlink();
+
+ IntSize m_size;
+ GC3Denum m_format;
+ size_t m_bytes;
+
+ size_t m_priority;
+ bool m_isAbovePriorityCutoff;
+ bool m_isSelfManaged;
+
+ Backing* m_backing;
+ CCPrioritizedTextureManager* m_manager;
+};
+
+} // WebCore
+
+#endif
diff --git a/cc/CCPrioritizedTextureManager.cpp b/cc/CCPrioritizedTextureManager.cpp
new file mode 100644
index 0000000..2678ec9
--- /dev/null
+++ b/cc/CCPrioritizedTextureManager.cpp
@@ -0,0 +1,339 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCPrioritizedTextureManager.h"
+
+#include "CCPrioritizedTexture.h"
+#include "CCPriorityCalculator.h"
+#include "TraceEvent.h"
+#include <algorithm>
+
+using namespace std;
+
+namespace WebCore {
+
+CCPrioritizedTextureManager::CCPrioritizedTextureManager(size_t maxMemoryLimitBytes, int, int pool)
+ : m_maxMemoryLimitBytes(maxMemoryLimitBytes)
+ , m_memoryUseBytes(0)
+ , m_memoryAboveCutoffBytes(0)
+ , m_memoryAvailableBytes(0)
+ , m_pool(pool)
+{
+}
+
+CCPrioritizedTextureManager::~CCPrioritizedTextureManager()
+{
+ while (m_textures.size() > 0)
+ unregisterTexture(*m_textures.begin());
+
+ // Each remaining backing is a leaked opengl texture. We don't have the resourceProvider
+ // to delete the textures at this time so clearMemory() needs to be called before this.
+ while (m_backings.size() > 0)
+ destroyBacking(*m_backings.begin(), 0);
+}
+
+void CCPrioritizedTextureManager::prioritizeTextures()
+{
+ TRACE_EVENT0("cc", "CCPrioritizedTextureManager::prioritizeTextures");
+
+#if !ASSERT_DISABLED
+ assertInvariants();
+#endif
+
+ // Sorting textures in this function could be replaced by a slightly
+ // modified O(n) quick-select to partition textures rather than
+ // sort them (if performance of the sort becomes an issue).
+
+ TextureVector& sortedTextures = m_tempTextureVector;
+ BackingVector& sortedBackings = m_tempBackingVector;
+ sortedTextures.clear();
+ sortedBackings.clear();
+
+ // Copy all textures into a vector and sort them.
+ for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it)
+ sortedTextures.append(*it);
+ std::sort(sortedTextures.begin(), sortedTextures.end(), compareTextures);
+
+ m_memoryAvailableBytes = m_maxMemoryLimitBytes;
+ m_priorityCutoff = CCPriorityCalculator::lowestPriority();
+ size_t memoryBytes = 0;
+ for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
+ if ((*it)->requestPriority() == CCPriorityCalculator::lowestPriority())
+ break;
+
+ if ((*it)->isSelfManaged()) {
+ // Account for self-managed memory immediately by reducing the memory
+ // available (since it never gets acquired).
+ size_t newMemoryBytes = memoryBytes + (*it)->bytes();
+ if (newMemoryBytes > m_memoryAvailableBytes) {
+ m_priorityCutoff = (*it)->requestPriority();
+ m_memoryAvailableBytes = memoryBytes;
+ break;
+ }
+ m_memoryAvailableBytes -= (*it)->bytes();
+ } else {
+ size_t newMemoryBytes = memoryBytes + (*it)->bytes();
+ if (newMemoryBytes > m_memoryAvailableBytes) {
+ m_priorityCutoff = (*it)->requestPriority();
+ break;
+ }
+ memoryBytes = newMemoryBytes;
+ }
+ }
+
+ // Only allow textures if they are higher than the cutoff. All textures
+ // of the same priority are accepted or rejected together, rather than
+ // being partially allowed randomly.
+ m_memoryAboveCutoffBytes = 0;
+ for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
+ bool isAbovePriorityCutoff = CCPriorityCalculator::priorityIsHigher((*it)->requestPriority(), m_priorityCutoff);
+ (*it)->setAbovePriorityCutoff(isAbovePriorityCutoff);
+ if (isAbovePriorityCutoff && !(*it)->isSelfManaged())
+ m_memoryAboveCutoffBytes += (*it)->bytes();
+ }
+ ASSERT(m_memoryAboveCutoffBytes <= m_memoryAvailableBytes);
+
+ // Put backings in eviction/recycling order.
+ for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
+ sortedBackings.append(*it);
+ std::sort(sortedBackings.begin(), sortedBackings.end(), compareBackings);
+
+ for (BackingVector::iterator it = sortedBackings.begin(); it != sortedBackings.end(); ++it) {
+ m_backings.remove(*it);
+ m_backings.add(*it);
+ }
+
+ sortedTextures.clear();
+ sortedBackings.clear();
+
+#if !ASSERT_DISABLED
+ assertInvariants();
+ ASSERT(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
+#endif
+}
+
+void CCPrioritizedTextureManager::clearPriorities()
+{
+ for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
+ // FIXME: We should remove this and just set all priorities to
+ // CCPriorityCalculator::lowestPriority() once we have priorities
+ // for all textures (we can't currently calculate distances for
+ // off-screen textures).
+ (*it)->setRequestPriority(CCPriorityCalculator::lingeringPriority((*it)->requestPriority()));
+ }
+}
+
+bool CCPrioritizedTextureManager::requestLate(CCPrioritizedTexture* texture)
+{
+ // This is already above cutoff, so don't double count it's memory below.
+ if (texture->isAbovePriorityCutoff())
+ return true;
+
+ if (CCPriorityCalculator::priorityIsLower(texture->requestPriority(), m_priorityCutoff))
+ return false;
+
+ size_t newMemoryBytes = m_memoryAboveCutoffBytes + texture->bytes();
+ if (newMemoryBytes > m_memoryAvailableBytes)
+ return false;
+
+ m_memoryAboveCutoffBytes = newMemoryBytes;
+ texture->setAbovePriorityCutoff(true);
+ if (texture->backing()) {
+ m_backings.remove(texture->backing());
+ m_backings.add(texture->backing());
+ }
+ return true;
+}
+
+void CCPrioritizedTextureManager::acquireBackingTextureIfNeeded(CCPrioritizedTexture* texture, CCResourceProvider* resourceProvider)
+{
+ ASSERT(!texture->isSelfManaged());
+ ASSERT(texture->isAbovePriorityCutoff());
+ if (texture->backing() || !texture->isAbovePriorityCutoff())
+ return;
+
+ // Find a backing below, by either recycling or allocating.
+ CCPrioritizedTexture::Backing* backing = 0;
+
+ // First try to recycle
+ for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
+ if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
+ break;
+ if ((*it)->size() == texture->size() && (*it)->format() == texture->format()) {
+ backing = (*it);
+ break;
+ }
+ }
+
+ // Otherwise reduce memory and just allocate a new backing texures.
+ if (!backing) {
+ reduceMemory(m_memoryAvailableBytes - texture->bytes(), resourceProvider);
+ backing = createBacking(texture->size(), texture->format(), resourceProvider);
+ }
+
+ // Move the used backing texture to the end of the eviction list.
+ if (backing->owner())
+ backing->owner()->unlink();
+ texture->link(backing);
+ m_backings.remove(backing);
+ m_backings.add(backing);
+}
+
+void CCPrioritizedTextureManager::reduceMemory(size_t limitBytes, CCResourceProvider* resourceProvider)
+{
+ if (memoryUseBytes() <= limitBytes)
+ return;
+ // Destroy backings until we are below the limit,
+ // or until all backings remaining are above the cutoff.
+ while (memoryUseBytes() > limitBytes && m_backings.size() > 0) {
+ BackingSet::iterator it = m_backings.begin();
+ if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
+ break;
+ destroyBacking((*it), resourceProvider);
+ }
+}
+
+void CCPrioritizedTextureManager::reduceMemory(CCResourceProvider* resourceProvider)
+{
+ reduceMemory(m_memoryAvailableBytes, resourceProvider);
+ ASSERT(memoryUseBytes() <= maxMemoryLimitBytes());
+
+ // We currently collect backings from deleted textures for later recycling.
+ // However, if we do that forever we will always use the max limit even if
+ // we really need very little memory. This should probably be solved by reducing the
+ // limit externally, but until then this just does some "clean up" of unused
+ // backing textures (any more than 10%).
+ size_t wastedMemory = 0;
+ for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
+ if ((*it)->owner())
+ break;
+ wastedMemory += (*it)->bytes();
+ }
+ size_t tenPercentOfMemory = m_memoryAvailableBytes / 10;
+ if (wastedMemory <= tenPercentOfMemory)
+ return;
+ reduceMemory(memoryUseBytes() - (wastedMemory - tenPercentOfMemory), resourceProvider);
+}
+
+void CCPrioritizedTextureManager::clearAllMemory(CCResourceProvider* resourceProvider)
+{
+ // Unlink and destroy all backing textures.
+ while (m_backings.size() > 0) {
+ BackingSet::iterator it = m_backings.begin();
+ if ((*it)->owner())
+ (*it)->owner()->unlink();
+ destroyBacking((*it), resourceProvider);
+ }
+}
+
+void CCPrioritizedTextureManager::allBackingTexturesWereDeleted()
+{
+ // Same as clearAllMemory, except all our textures were already
+ // deleted externally, so we don't delete them. Passing no
+ // resourceProvider results in leaking the (now invalid) texture ids.
+ clearAllMemory(0);
+}
+
+void CCPrioritizedTextureManager::registerTexture(CCPrioritizedTexture* texture)
+{
+ ASSERT(texture);
+ ASSERT(!texture->textureManager());
+ ASSERT(!texture->backing());
+ ASSERT(m_textures.find(texture) == m_textures.end());
+
+ texture->setManagerInternal(this);
+ m_textures.add(texture);
+
+}
+
+void CCPrioritizedTextureManager::unregisterTexture(CCPrioritizedTexture* texture)
+{
+ ASSERT(texture);
+ ASSERT(m_textures.find(texture) != m_textures.end());
+
+ returnBackingTexture(texture);
+ texture->setManagerInternal(0);
+ m_textures.remove(texture);
+ texture->setAbovePriorityCutoff(false);
+}
+
+
+void CCPrioritizedTextureManager::returnBackingTexture(CCPrioritizedTexture* texture)
+{
+ if (texture->backing()) {
+ // Move the backing texture to the front for eviction/recycling and unlink it.
+ m_backings.remove(texture->backing());
+ m_backings.insertBefore(m_backings.begin(), texture->backing());
+ texture->unlink();
+ }
+}
+
+CCPrioritizedTexture::Backing* CCPrioritizedTextureManager::createBacking(IntSize size, GC3Denum format, CCResourceProvider* resourceProvider)
+{
+ ASSERT(resourceProvider);
+
+ CCResourceProvider::ResourceId resourceId = resourceProvider->createResource(m_pool, size, format, CCResourceProvider::TextureUsageAny);
+ CCPrioritizedTexture::Backing* backing = new CCPrioritizedTexture::Backing(resourceId, size, format);
+ m_memoryUseBytes += backing->bytes();
+ // Put backing texture at the front for eviction, since it isn't in use yet.
+ m_backings.insertBefore(m_backings.begin(), backing);
+ return backing;
+}
+
+void CCPrioritizedTextureManager::destroyBacking(CCPrioritizedTexture::Backing* backing, CCResourceProvider* resourceProvider)
+{
+ ASSERT(backing);
+ ASSERT(!backing->owner() || !backing->owner()->isAbovePriorityCutoff());
+ ASSERT(!backing->owner() || !backing->owner()->isSelfManaged());
+ ASSERT(m_backings.find(backing) != m_backings.end());
+
+ if (resourceProvider)
+ resourceProvider->deleteResource(backing->id());
+ if (backing->owner())
+ backing->owner()->unlink();
+ m_memoryUseBytes -= backing->bytes();
+ m_backings.remove(backing);
+
+ delete backing;
+}
+
+
+#if !ASSERT_DISABLED
+void CCPrioritizedTextureManager::assertInvariants()
+{
+ // If we hit any of these asserts, there is a bug in this class. To see
+ // where the bug is, call this function at the beginning and end of
+ // every public function.
+
+ // Backings/textures must be doubly-linked and only to other backings/textures in this manager.
+ for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
+ if ((*it)->owner()) {
+ ASSERT(m_textures.find((*it)->owner()) != m_textures.end());
+ ASSERT((*it)->owner()->backing() == (*it));
+ }
+ }
+ for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
+ if ((*it)->backing()) {
+ ASSERT(m_backings.find((*it)->backing()) != m_backings.end());
+ ASSERT((*it)->backing()->owner() == (*it));
+ }
+ }
+
+ // At all times, backings that can be evicted must always come before
+ // backings that can't be evicted in the backing texture list (otherwise
+ // reduceMemory will not find all textures available for eviction/recycling).
+ bool reachedProtected = false;
+ for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
+ if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
+ reachedProtected = true;
+ if (reachedProtected)
+ ASSERT((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff());
+ }
+}
+#endif
+
+
+} // namespace WebCore
diff --git a/cc/CCPrioritizedTextureManager.h b/cc/CCPrioritizedTextureManager.h
new file mode 100644
index 0000000..42a10c8
--- /dev/null
+++ b/cc/CCPrioritizedTextureManager.h
@@ -0,0 +1,119 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCPrioritizedTextureManager_h
+#define CCPrioritizedTextureManager_h
+
+#include "CCPrioritizedTexture.h"
+#include "CCPriorityCalculator.h"
+#include "CCTexture.h"
+#include "GraphicsContext3D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCPrioritizedTexture;
+class CCPriorityCalculator;
+
+class CCPrioritizedTextureManager {
+ WTF_MAKE_NONCOPYABLE(CCPrioritizedTextureManager);
+public:
+ static PassOwnPtr<CCPrioritizedTextureManager> create(size_t maxMemoryLimitBytes, int maxTextureSize, int pool)
+ {
+ return adoptPtr(new CCPrioritizedTextureManager(maxMemoryLimitBytes, maxTextureSize, pool));
+ }
+ PassOwnPtr<CCPrioritizedTexture> createTexture(IntSize size, GC3Denum format)
+ {
+ return adoptPtr(new CCPrioritizedTexture(this, size, format));
+ }
+ ~CCPrioritizedTextureManager();
+
+ // FIXME (http://crbug.com/137094): This 64MB default is a straggler from the
+ // old texture manager and is just to give us a default memory allocation before
+ // we get a callback from the GPU memory manager. We should probaby either:
+ // - wait for the callback before rendering anything instead
+ // - push this into the GPU memory manager somehow.
+ static size_t defaultMemoryAllocationLimit() { return 64 * 1024 * 1024; }
+
+ // memoryUseBytes() describes the number of bytes used by existing allocated textures.
+ // memoryAboveCutoffBytes() describes the number of bytes that would be used if all
+ // textures that are above the cutoff were allocated.
+ // memoryUseBytes() <= memoryAboveCutoffBytes() should always be true.
+ size_t memoryUseBytes() const { return m_memoryUseBytes; }
+ size_t memoryAboveCutoffBytes() const { return m_memoryAboveCutoffBytes; }
+ size_t memoryForSelfManagedTextures() const { return m_maxMemoryLimitBytes - m_memoryAvailableBytes; }
+
+ void setMaxMemoryLimitBytes(size_t bytes) { m_maxMemoryLimitBytes = bytes; }
+ size_t maxMemoryLimitBytes() const { return m_maxMemoryLimitBytes; }
+
+ void prioritizeTextures();
+ void clearPriorities();
+
+ bool requestLate(CCPrioritizedTexture*);
+
+ void reduceMemory(CCResourceProvider*);
+ void clearAllMemory(CCResourceProvider*);
+ void allBackingTexturesWereDeleted();
+
+ void acquireBackingTextureIfNeeded(CCPrioritizedTexture*, CCResourceProvider*);
+
+ void registerTexture(CCPrioritizedTexture*);
+ void unregisterTexture(CCPrioritizedTexture*);
+ void returnBackingTexture(CCPrioritizedTexture*);
+
+#if !ASSERT_DISABLED
+ void assertInvariants();
+#endif
+
+private:
+ // Compare textures. Highest priority first.
+ static inline bool compareTextures(CCPrioritizedTexture* a, CCPrioritizedTexture* b)
+ {
+ if (a->requestPriority() == b->requestPriority())
+ return a < b;
+ return CCPriorityCalculator::priorityIsHigher(a->requestPriority(), b->requestPriority());
+ }
+ // Compare backings. Lowest priority first.
+ static inline bool compareBackings(CCPrioritizedTexture::Backing* a, CCPrioritizedTexture::Backing* b)
+ {
+ int priorityA = a->owner() ? a->owner()->requestPriority() : CCPriorityCalculator::lowestPriority();
+ int priorityB = b->owner() ? b->owner()->requestPriority() : CCPriorityCalculator::lowestPriority();
+ if (priorityA == priorityB)
+ return a < b;
+ return CCPriorityCalculator::priorityIsLower(priorityA, priorityB);
+ }
+
+ CCPrioritizedTextureManager(size_t maxMemoryLimitBytes, int maxTextureSize, int pool);
+
+ void reduceMemory(size_t limit, CCResourceProvider*);
+
+ CCPrioritizedTexture::Backing* createBacking(IntSize, GC3Denum format, CCResourceProvider*);
+ void destroyBacking(CCPrioritizedTexture::Backing*, CCResourceProvider*);
+
+ size_t m_maxMemoryLimitBytes;
+ unsigned m_priorityCutoff;
+ size_t m_memoryUseBytes;
+ size_t m_memoryAboveCutoffBytes;
+ size_t m_memoryAvailableBytes;
+ int m_pool;
+
+ typedef HashSet<CCPrioritizedTexture*> TextureSet;
+ typedef ListHashSet<CCPrioritizedTexture::Backing*> BackingSet;
+ typedef Vector<CCPrioritizedTexture*> TextureVector;
+ typedef Vector<CCPrioritizedTexture::Backing*> BackingVector;
+
+ TextureSet m_textures;
+ BackingSet m_backings;
+
+ TextureVector m_tempTextureVector;
+ BackingVector m_tempBackingVector;
+};
+
+} // WebCore
+
+#endif
diff --git a/cc/CCPrioritizedTextureTest.cpp b/cc/CCPrioritizedTextureTest.cpp
new file mode 100644
index 0000000..0594d6b
--- /dev/null
+++ b/cc/CCPrioritizedTextureTest.cpp
@@ -0,0 +1,456 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCPrioritizedTexture.h"
+
+#include "CCPrioritizedTextureManager.h"
+#include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
+#include "CCTexture.h"
+#include "CCTiledLayerTestCommon.h"
+#include "FakeCCGraphicsContext.h"
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace WebKitTests;
+using namespace WTF;
+
+namespace {
+
+class CCPrioritizedTextureTest : public testing::Test {
+public:
+ CCPrioritizedTextureTest()
+ : m_textureSize(256, 256)
+ , m_textureFormat(GraphicsContext3D::RGBA)
+ , m_context(WebKit::createFakeCCGraphicsContext())
+ {
+ DebugScopedSetImplThread implThread;
+ m_resourceProvider = CCResourceProvider::create(m_context.get());
+ }
+
+ virtual ~CCPrioritizedTextureTest()
+ {
+ DebugScopedSetImplThread implThread;
+ m_resourceProvider.clear();
+ }
+
+ size_t texturesMemorySize(size_t textureCount)
+ {
+ return CCTexture::memorySizeBytes(m_textureSize, m_textureFormat) * textureCount;
+ }
+
+ PassOwnPtr<CCPrioritizedTextureManager> createManager(size_t maxTextures)
+ {
+ return CCPrioritizedTextureManager::create(texturesMemorySize(maxTextures), 1024, 0);
+ }
+
+ bool validateTexture(OwnPtr<CCPrioritizedTexture>& texture, bool requestLate)
+ {
+ DebugScopedSetImplThread implThread;
+#if !ASSERT_DISABLED
+ texture->textureManager()->assertInvariants();
+#endif
+ if (requestLate)
+ texture->requestLate();
+ bool success = texture->canAcquireBackingTexture();
+ if (success)
+ texture->acquireBackingTexture(resourceProvider());
+ return success;
+ }
+
+ CCResourceProvider* resourceProvider()
+ {
+ return m_resourceProvider.get();
+ }
+
+protected:
+ const IntSize m_textureSize;
+ const GC3Denum m_textureFormat;
+ OwnPtr<CCGraphicsContext> m_context;
+ OwnPtr<CCResourceProvider> m_resourceProvider;
+};
+
+TEST_F(CCPrioritizedTextureTest, requestTextureExceedingMaxLimit)
+{
+ const size_t maxTextures = 8;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+
+ // Create textures for double our memory limit.
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures*2];
+
+ for (size_t i = 0; i < maxTextures*2; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // Set decreasing priorities
+ for (size_t i = 0; i < maxTextures*2; ++i)
+ textures[i]->setRequestPriority(100 + i);
+
+ // Only lower half should be available.
+ textureManager->prioritizeTextures();
+ EXPECT_TRUE(validateTexture(textures[0], false));
+ EXPECT_TRUE(validateTexture(textures[7], false));
+ EXPECT_FALSE(validateTexture(textures[8], false));
+ EXPECT_FALSE(validateTexture(textures[15], false));
+
+ // Set increasing priorities
+ for (size_t i = 0; i < maxTextures*2; ++i)
+ textures[i]->setRequestPriority(100 - i);
+
+ // Only upper half should be available.
+ textureManager->prioritizeTextures();
+ EXPECT_FALSE(validateTexture(textures[0], false));
+ EXPECT_FALSE(validateTexture(textures[7], false));
+ EXPECT_TRUE(validateTexture(textures[8], false));
+ EXPECT_TRUE(validateTexture(textures[15], false));
+
+ EXPECT_EQ(texturesMemorySize(maxTextures), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, changeMemoryLimits)
+{
+ const size_t maxTextures = 8;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures];
+
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i]->setRequestPriority(100 + i);
+
+ // Set max limit to 8 textures
+ textureManager->setMaxMemoryLimitBytes(texturesMemorySize(8));
+ textureManager->prioritizeTextures();
+ for (size_t i = 0; i < maxTextures; ++i)
+ validateTexture(textures[i], false);
+ {
+ DebugScopedSetImplThread implThread;
+ textureManager->reduceMemory(resourceProvider());
+ }
+
+ EXPECT_EQ(texturesMemorySize(8), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ // Set max limit to 5 textures
+ textureManager->setMaxMemoryLimitBytes(texturesMemorySize(5));
+ textureManager->prioritizeTextures();
+ for (size_t i = 0; i < maxTextures; ++i)
+ EXPECT_EQ(validateTexture(textures[i], false), i < 5);
+ {
+ DebugScopedSetImplThread implThread;
+ textureManager->reduceMemory(resourceProvider());
+ }
+
+ EXPECT_EQ(texturesMemorySize(5), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ // Set max limit to 4 textures
+ textureManager->setMaxMemoryLimitBytes(texturesMemorySize(4));
+ textureManager->prioritizeTextures();
+ for (size_t i = 0; i < maxTextures; ++i)
+ EXPECT_EQ(validateTexture(textures[i], false), i < 4);
+ {
+ DebugScopedSetImplThread implThread;
+ textureManager->reduceMemory(resourceProvider());
+ }
+
+ EXPECT_EQ(texturesMemorySize(4), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, textureManagerPartialUpdateTextures)
+{
+ const size_t maxTextures = 4;
+ const size_t numTextures = 4;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+ OwnPtr<CCPrioritizedTexture> textures[numTextures];
+ OwnPtr<CCPrioritizedTexture> moreTextures[numTextures];
+
+ for (size_t i = 0; i < numTextures; ++i) {
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+ moreTextures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+ }
+
+ for (size_t i = 0; i < numTextures; ++i)
+ textures[i]->setRequestPriority(200 + i);
+ textureManager->prioritizeTextures();
+
+ // Allocate textures which are currently high priority.
+ EXPECT_TRUE(validateTexture(textures[0], false));
+ EXPECT_TRUE(validateTexture(textures[1], false));
+ EXPECT_TRUE(validateTexture(textures[2], false));
+ EXPECT_TRUE(validateTexture(textures[3], false));
+
+ EXPECT_TRUE(textures[0]->haveBackingTexture());
+ EXPECT_TRUE(textures[1]->haveBackingTexture());
+ EXPECT_TRUE(textures[2]->haveBackingTexture());
+ EXPECT_TRUE(textures[3]->haveBackingTexture());
+
+ for (size_t i = 0; i < numTextures; ++i)
+ moreTextures[i]->setRequestPriority(100 + i);
+ textureManager->prioritizeTextures();
+
+ // Textures are now below cutoff.
+ EXPECT_FALSE(validateTexture(textures[0], false));
+ EXPECT_FALSE(validateTexture(textures[1], false));
+ EXPECT_FALSE(validateTexture(textures[2], false));
+ EXPECT_FALSE(validateTexture(textures[3], false));
+
+ // But they are still valid to use.
+ EXPECT_TRUE(textures[0]->haveBackingTexture());
+ EXPECT_TRUE(textures[1]->haveBackingTexture());
+ EXPECT_TRUE(textures[2]->haveBackingTexture());
+ EXPECT_TRUE(textures[3]->haveBackingTexture());
+
+ // Higher priority textures are finally needed.
+ EXPECT_TRUE(validateTexture(moreTextures[0], false));
+ EXPECT_TRUE(validateTexture(moreTextures[1], false));
+ EXPECT_TRUE(validateTexture(moreTextures[2], false));
+ EXPECT_TRUE(validateTexture(moreTextures[3], false));
+
+ // Lower priority have been fully evicted.
+ EXPECT_FALSE(textures[0]->haveBackingTexture());
+ EXPECT_FALSE(textures[1]->haveBackingTexture());
+ EXPECT_FALSE(textures[2]->haveBackingTexture());
+ EXPECT_FALSE(textures[3]->haveBackingTexture());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, textureManagerPrioritiesAreEqual)
+{
+ const size_t maxTextures = 16;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures];
+
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // All 16 textures have the same priority except 2 higher priority.
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i]->setRequestPriority(100);
+ textures[0]->setRequestPriority(99);
+ textures[1]->setRequestPriority(99);
+
+ // Set max limit to 8 textures
+ textureManager->setMaxMemoryLimitBytes(texturesMemorySize(8));
+ textureManager->prioritizeTextures();
+
+ // The two high priority textures should be available, others should not.
+ for (size_t i = 0; i < 2; ++i)
+ EXPECT_TRUE(validateTexture(textures[i], false));
+ for (size_t i = 2; i < maxTextures; ++i)
+ EXPECT_FALSE(validateTexture(textures[i], false));
+ EXPECT_EQ(texturesMemorySize(2), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ // Manually reserving textures should only succeed on the higher priority textures,
+ // and on remaining textures up to the memory limit.
+ for (size_t i = 0; i < 8; i++)
+ EXPECT_TRUE(validateTexture(textures[i], true));
+ for (size_t i = 9; i < maxTextures; i++)
+ EXPECT_FALSE(validateTexture(textures[i], true));
+ EXPECT_EQ(texturesMemorySize(8), textureManager->memoryAboveCutoffBytes());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, textureManagerDestroyedFirst)
+{
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(1);
+ OwnPtr<CCPrioritizedTexture> texture = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // Texture is initially invalid, but it will become available.
+ EXPECT_FALSE(texture->haveBackingTexture());
+
+ texture->setRequestPriority(100);
+ textureManager->prioritizeTextures();
+
+ EXPECT_TRUE(validateTexture(texture, false));
+ EXPECT_TRUE(texture->canAcquireBackingTexture());
+ EXPECT_TRUE(texture->haveBackingTexture());
+
+ {
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+ }
+ textureManager.clear();
+
+ EXPECT_FALSE(texture->canAcquireBackingTexture());
+ EXPECT_FALSE(texture->haveBackingTexture());
+}
+
+TEST_F(CCPrioritizedTextureTest, textureMovedToNewManager)
+{
+ OwnPtr<CCPrioritizedTextureManager> textureManagerOne = createManager(1);
+ OwnPtr<CCPrioritizedTextureManager> textureManagerTwo = createManager(1);
+ OwnPtr<CCPrioritizedTexture> texture = textureManagerOne->createTexture(m_textureSize, m_textureFormat);
+
+ // Texture is initially invalid, but it will become available.
+ EXPECT_FALSE(texture->haveBackingTexture());
+
+ texture->setRequestPriority(100);
+ textureManagerOne->prioritizeTextures();
+
+ EXPECT_TRUE(validateTexture(texture, false));
+ EXPECT_TRUE(texture->canAcquireBackingTexture());
+ EXPECT_TRUE(texture->haveBackingTexture());
+
+ texture->setTextureManager(0);
+
+ {
+ DebugScopedSetImplThread implThread;
+ textureManagerOne->clearAllMemory(resourceProvider());
+ }
+ textureManagerOne.clear();
+
+ EXPECT_FALSE(texture->canAcquireBackingTexture());
+ EXPECT_FALSE(texture->haveBackingTexture());
+
+ texture->setTextureManager(textureManagerTwo.get());
+
+ textureManagerTwo->prioritizeTextures();
+
+ EXPECT_TRUE(validateTexture(texture, false));
+ EXPECT_TRUE(texture->canAcquireBackingTexture());
+ EXPECT_TRUE(texture->haveBackingTexture());
+
+ DebugScopedSetImplThread implThread;
+ textureManagerTwo->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, renderSurfacesReduceMemoryAvailableOutsideRootSurface)
+{
+ const size_t maxTextures = 8;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+
+ // Half of the memory is taken by surfaces (with high priority place-holder)
+ OwnPtr<CCPrioritizedTexture> renderSurfacePlaceHolder = textureManager->createTexture(m_textureSize, m_textureFormat);
+ renderSurfacePlaceHolder->setToSelfManagedMemoryPlaceholder(texturesMemorySize(4));
+ renderSurfacePlaceHolder->setRequestPriority(CCPriorityCalculator::renderSurfacePriority());
+
+ // Create textures to fill our memory limit.
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures];
+
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // Set decreasing non-visible priorities outside root surface.
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i]->setRequestPriority(100 + i);
+
+ // Only lower half should be available.
+ textureManager->prioritizeTextures();
+ EXPECT_TRUE(validateTexture(textures[0], false));
+ EXPECT_TRUE(validateTexture(textures[3], false));
+ EXPECT_FALSE(validateTexture(textures[4], false));
+ EXPECT_FALSE(validateTexture(textures[7], false));
+
+ // Set increasing non-visible priorities outside root surface.
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i]->setRequestPriority(100 - i);
+
+ // Only upper half should be available.
+ textureManager->prioritizeTextures();
+ EXPECT_FALSE(validateTexture(textures[0], false));
+ EXPECT_FALSE(validateTexture(textures[3], false));
+ EXPECT_TRUE(validateTexture(textures[4], false));
+ EXPECT_TRUE(validateTexture(textures[7], false));
+
+ EXPECT_EQ(texturesMemorySize(4), textureManager->memoryAboveCutoffBytes());
+ EXPECT_EQ(texturesMemorySize(4), textureManager->memoryForSelfManagedTextures());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, renderSurfacesReduceMemoryAvailableForRequestLate)
+{
+ const size_t maxTextures = 8;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+
+ // Half of the memory is taken by surfaces (with high priority place-holder)
+ OwnPtr<CCPrioritizedTexture> renderSurfacePlaceHolder = textureManager->createTexture(m_textureSize, m_textureFormat);
+ renderSurfacePlaceHolder->setToSelfManagedMemoryPlaceholder(texturesMemorySize(4));
+ renderSurfacePlaceHolder->setRequestPriority(CCPriorityCalculator::renderSurfacePriority());
+
+ // Create textures to fill our memory limit.
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures];
+
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // Set equal priorities.
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i]->setRequestPriority(100);
+
+ // The first four to be requested late will be available.
+ textureManager->prioritizeTextures();
+ for (unsigned i = 0; i < maxTextures; ++i)
+ EXPECT_FALSE(validateTexture(textures[i], false));
+ for (unsigned i = 0; i < maxTextures; i += 2)
+ EXPECT_TRUE(validateTexture(textures[i], true));
+ for (unsigned i = 1; i < maxTextures; i += 2)
+ EXPECT_FALSE(validateTexture(textures[i], true));
+
+ EXPECT_EQ(texturesMemorySize(4), textureManager->memoryAboveCutoffBytes());
+ EXPECT_EQ(texturesMemorySize(4), textureManager->memoryForSelfManagedTextures());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+TEST_F(CCPrioritizedTextureTest, whenRenderSurfaceNotAvailableTexturesAlsoNotAvailable)
+{
+ const size_t maxTextures = 8;
+ OwnPtr<CCPrioritizedTextureManager> textureManager = createManager(maxTextures);
+
+ // Half of the memory is taken by surfaces (with high priority place-holder)
+ OwnPtr<CCPrioritizedTexture> renderSurfacePlaceHolder = textureManager->createTexture(m_textureSize, m_textureFormat);
+ renderSurfacePlaceHolder->setToSelfManagedMemoryPlaceholder(texturesMemorySize(4));
+ renderSurfacePlaceHolder->setRequestPriority(CCPriorityCalculator::renderSurfacePriority());
+
+ // Create textures to fill our memory limit.
+ OwnPtr<CCPrioritizedTexture> textures[maxTextures];
+
+ for (size_t i = 0; i < maxTextures; ++i)
+ textures[i] = textureManager->createTexture(m_textureSize, m_textureFormat);
+
+ // Set 6 visible textures in the root surface, and 2 in a child surface.
+ for (size_t i = 0; i < 6; ++i)
+ textures[i]->setRequestPriority(CCPriorityCalculator::visiblePriority(true));
+ for (size_t i = 6; i < 8; ++i)
+ textures[i]->setRequestPriority(CCPriorityCalculator::visiblePriority(false));
+
+ textureManager->prioritizeTextures();
+
+ // Unable to requestLate textures in the child surface.
+ EXPECT_FALSE(validateTexture(textures[6], true));
+ EXPECT_FALSE(validateTexture(textures[7], true));
+
+ // Root surface textures are valid.
+ for (size_t i = 0; i < 6; ++i)
+ EXPECT_TRUE(validateTexture(textures[i], false));
+
+ EXPECT_EQ(texturesMemorySize(6), textureManager->memoryAboveCutoffBytes());
+ EXPECT_EQ(texturesMemorySize(2), textureManager->memoryForSelfManagedTextures());
+ EXPECT_LE(textureManager->memoryUseBytes(), textureManager->memoryAboveCutoffBytes());
+
+ DebugScopedSetImplThread implThread;
+ textureManager->clearAllMemory(resourceProvider());
+}
+
+} // namespace
diff --git a/cc/CCPriorityCalculator.cpp b/cc/CCPriorityCalculator.cpp
new file mode 100644
index 0000000..7e769f7
--- /dev/null
+++ b/cc/CCPriorityCalculator.cpp
@@ -0,0 +1,72 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCPriorityCalculator.h"
+
+using namespace std;
+
+namespace WebCore {
+
+// static
+int CCPriorityCalculator::uiPriority(bool drawsToRootSurface)
+{
+ return drawsToRootSurface ? -1 : 2;
+}
+
+// static
+int CCPriorityCalculator::visiblePriority(bool drawsToRootSurface)
+{
+ return drawsToRootSurface ? 0 : 3;
+}
+
+// static
+int CCPriorityCalculator::renderSurfacePriority()
+{
+ return 1;
+}
+
+// static
+int CCPriorityCalculator::lingeringPriority(int previousPriority)
+{
+ // FIXME: We should remove this once we have priorities for all
+ // textures (we can't currently calculate distances for
+ // off-screen textures).
+ int lingeringPriority = 1000000;
+ return min(numeric_limits<int>::max() - 1,
+ max(lingeringPriority, previousPriority)) + 1;
+}
+
+namespace {
+unsigned manhattanDistance(const IntRect& a, const IntRect& b)
+{
+ IntRect c = unionRect(a, b);
+ int x = max(0, c.width() - a.width() - b.width() + 1);
+ int y = max(0, c.height() - a.height() - b.height() + 1);
+ return (x + y);
+}
+}
+
+int CCPriorityCalculator::priorityFromDistance(const IntRect& visibleRect, const IntRect& textureRect, bool drawsToRootSurface) const
+{
+ unsigned distance = manhattanDistance(visibleRect, textureRect);
+ if (!distance)
+ return visiblePriority(drawsToRootSurface);
+ return visiblePriority(false) + distance;
+}
+
+int CCPriorityCalculator::priorityFromDistance(unsigned pixels, bool drawsToRootSurface) const
+{
+ if (!pixels)
+ return visiblePriority(drawsToRootSurface);
+ return visiblePriority(false) + pixels;
+}
+
+int CCPriorityCalculator::priorityFromVisibility(bool visible, bool drawsToRootSurface) const
+{
+ return visible ? visiblePriority(drawsToRootSurface) : lowestPriority();
+}
+
+} // WebCore
diff --git a/cc/CCPriorityCalculator.h b/cc/CCPriorityCalculator.h
new file mode 100644
index 0000000..91ad766
--- /dev/null
+++ b/cc/CCPriorityCalculator.h
@@ -0,0 +1,33 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCPriorityCalculator_h
+#define CCPriorityCalculator_h
+
+#include "GraphicsContext3D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+
+namespace WebCore {
+
+class CCPriorityCalculator {
+public:
+ static int uiPriority(bool drawsToRootSurface);
+ static int visiblePriority(bool drawsToRootSurface);
+ static int renderSurfacePriority();
+ static int lingeringPriority(int previousPriority);
+ int priorityFromDistance(const IntRect& visibleRect, const IntRect& textureRect, bool drawsToRootSurface) const;
+ int priorityFromDistance(unsigned pixels, bool drawsToRootSurface) const;
+ int priorityFromVisibility(bool visible, bool drawsToRootSurface) const;
+
+ static inline int highestPriority() { return std::numeric_limits<int>::min(); }
+ static inline int lowestPriority() { return std::numeric_limits<int>::max(); }
+ static inline bool priorityIsLower(int a, int b) { return a > b; }
+ static inline bool priorityIsHigher(int a, int b) { return a < b; }
+ static inline bool maxPriority(int a, int b) { return priorityIsHigher(a, b) ? a : b; }
+};
+
+}
+
+#endif
diff --git a/cc/CCProxy.cpp b/cc/CCProxy.cpp
new file mode 100644
index 0000000..2e89398
--- /dev/null
+++ b/cc/CCProxy.cpp
@@ -0,0 +1,106 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCProxy.h"
+
+#include "CCThreadTask.h"
+#include <wtf/MainThread.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+namespace {
+#ifndef NDEBUG
+bool implThreadIsOverridden = false;
+bool s_isMainThreadBlocked = false;
+ThreadIdentifier threadIDOverridenToBeImplThread;
+#endif
+CCThread* s_mainThread = 0;
+CCThread* s_implThread = 0;
+}
+
+void CCProxy::setMainThread(CCThread* thread)
+{
+ s_mainThread = thread;
+}
+
+CCThread* CCProxy::mainThread()
+{
+ return s_mainThread;
+}
+
+bool CCProxy::hasImplThread()
+{
+ return s_implThread;
+}
+
+void CCProxy::setImplThread(CCThread* thread)
+{
+ s_implThread = thread;
+}
+
+CCThread* CCProxy::implThread()
+{
+ return s_implThread;
+}
+
+CCThread* CCProxy::currentThread()
+{
+ ThreadIdentifier currentThreadIdentifier = WTF::currentThread();
+ if (s_mainThread && s_mainThread->threadID() == currentThreadIdentifier)
+ return s_mainThread;
+ if (s_implThread && s_implThread->threadID() == currentThreadIdentifier)
+ return s_implThread;
+ return 0;
+}
+
+#ifndef NDEBUG
+bool CCProxy::isMainThread()
+{
+ ASSERT(s_mainThread);
+ if (implThreadIsOverridden && WTF::currentThread() == threadIDOverridenToBeImplThread)
+ return false;
+ return WTF::currentThread() == s_mainThread->threadID();
+}
+
+bool CCProxy::isImplThread()
+{
+ WTF::ThreadIdentifier implThreadID = s_implThread ? s_implThread->threadID() : 0;
+ if (implThreadIsOverridden && WTF::currentThread() == threadIDOverridenToBeImplThread)
+ return true;
+ return WTF::currentThread() == implThreadID;
+}
+
+void CCProxy::setCurrentThreadIsImplThread(bool isImplThread)
+{
+ implThreadIsOverridden = isImplThread;
+ if (isImplThread)
+ threadIDOverridenToBeImplThread = WTF::currentThread();
+}
+
+bool CCProxy::isMainThreadBlocked()
+{
+ return s_isMainThreadBlocked;
+}
+
+void CCProxy::setMainThreadBlocked(bool isMainThreadBlocked)
+{
+ s_isMainThreadBlocked = isMainThreadBlocked;
+}
+#endif
+
+CCProxy::CCProxy()
+{
+ ASSERT(isMainThread());
+}
+
+CCProxy::~CCProxy()
+{
+ ASSERT(isMainThread());
+}
+
+}
diff --git a/cc/CCProxy.h b/cc/CCProxy.h
new file mode 100644
index 0000000..03913f2
--- /dev/null
+++ b/cc/CCProxy.h
@@ -0,0 +1,129 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCProxy_h
+#define CCProxy_h
+
+#include "IntRect.h"
+#include <public/WebCompositorOutputSurface.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class CCThread;
+struct CCRenderingStats;
+struct RendererCapabilities;
+
+// Abstract class responsible for proxying commands from the main-thread side of
+// the compositor over to the compositor implementation.
+class CCProxy {
+ WTF_MAKE_NONCOPYABLE(CCProxy);
+public:
+ static void setMainThread(CCThread*);
+ static CCThread* mainThread();
+
+ static bool hasImplThread();
+ static void setImplThread(CCThread*);
+ static CCThread* implThread();
+
+ // Returns 0 if the current thread is neither the main thread nor the impl thread.
+ static CCThread* currentThread();
+
+ virtual ~CCProxy();
+
+ virtual bool compositeAndReadback(void *pixels, const IntRect&) = 0;
+
+ virtual void startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double durationSec) = 0;
+
+ virtual void finishAllRendering() = 0;
+
+ virtual bool isStarted() const = 0;
+
+ // Attempts to initialize a context to use for rendering. Returns false if the context could not be created.
+ // The context will not be used and no frames may be produced until initializeRenderer() is called.
+ virtual bool initializeContext() = 0;
+
+ // Indicates that the compositing surface associated with our context is ready to use.
+ virtual void setSurfaceReady() = 0;
+
+ virtual void setVisible(bool) = 0;
+
+ // Attempts to initialize the layer renderer. Returns false if the context isn't usable for compositing.
+ virtual bool initializeRenderer() = 0;
+
+ // Attempts to recreate the context and layer renderer after a context lost. Returns false if the renderer couldn't be
+ // reinitialized.
+ virtual bool recreateContext() = 0;
+
+ virtual int compositorIdentifier() const = 0;
+
+ virtual void implSideRenderingStats(CCRenderingStats&) = 0;
+
+ virtual const RendererCapabilities& rendererCapabilities() const = 0;
+
+ virtual void setNeedsAnimate() = 0;
+ virtual void setNeedsCommit() = 0;
+ virtual void setNeedsRedraw() = 0;
+
+ virtual void didAddAnimation() = 0;
+
+ virtual bool commitRequested() const = 0;
+
+ virtual void start() = 0; // Must be called before using the proxy.
+ virtual void stop() = 0; // Must be called before deleting the proxy.
+
+ // Forces 3D commands on all contexts to wait for all previous SwapBuffers to finish before executing in the GPU
+ // process.
+ virtual void forceSerializeOnSwapBuffers() = 0;
+
+ // Maximum number of sub-region texture updates supported for each commit.
+ virtual size_t maxPartialTextureUpdates() const = 0;
+
+ virtual void acquireLayerTextures() = 0;
+
+ // Debug hooks
+#ifndef NDEBUG
+ static bool isMainThread();
+ static bool isImplThread();
+ static bool isMainThreadBlocked();
+ static void setMainThreadBlocked(bool);
+#endif
+
+ // Testing hooks
+ virtual void loseContext() = 0;
+
+#ifndef NDEBUG
+ static void setCurrentThreadIsImplThread(bool);
+#endif
+
+protected:
+ CCProxy();
+ friend class DebugScopedSetImplThread;
+ friend class DebugScopedSetMainThreadBlocked;
+};
+
+class DebugScopedSetMainThreadBlocked {
+public:
+ DebugScopedSetMainThreadBlocked()
+ {
+#if !ASSERT_DISABLED
+ ASSERT(!CCProxy::isMainThreadBlocked());
+ CCProxy::setMainThreadBlocked(true);
+#endif
+ }
+ ~DebugScopedSetMainThreadBlocked()
+ {
+#if !ASSERT_DISABLED
+ ASSERT(CCProxy::isMainThreadBlocked());
+ CCProxy::setMainThreadBlocked(false);
+#endif
+ }
+};
+
+}
+
+#endif
diff --git a/cc/CCQuadCuller.cpp b/cc/CCQuadCuller.cpp
new file mode 100644
index 0000000..3197f11
--- /dev/null
+++ b/cc/CCQuadCuller.cpp
@@ -0,0 +1,95 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCQuadCuller.h"
+
+#include "CCDebugBorderDrawQuad.h"
+#include "CCLayerImpl.h"
+#include "CCOcclusionTracker.h"
+#include "CCOverdrawMetrics.h"
+#include "CCRenderPass.h"
+#include "Region.h"
+#include "SkColor.h"
+#include <public/WebTransformationMatrix.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static const int debugTileBorderWidth = 1;
+static const int debugTileBorderAlpha = 120;
+static const int debugTileBorderColorRed = 160;
+static const int debugTileBorderColorGreen = 100;
+static const int debugTileBorderColorBlue = 0;
+
+CCQuadCuller::CCQuadCuller(CCQuadList& quadList, CCSharedQuadStateList& sharedQuadStateList, CCLayerImpl* layer, const CCOcclusionTrackerImpl* occlusionTracker, bool showCullingWithDebugBorderQuads, bool forSurface)
+ : m_quadList(quadList)
+ , m_sharedQuadStateList(sharedQuadStateList)
+ , m_currentSharedQuadState(0)
+ , m_layer(layer)
+ , m_occlusionTracker(occlusionTracker)
+ , m_showCullingWithDebugBorderQuads(showCullingWithDebugBorderQuads)
+ , m_forSurface(forSurface)
+ , m_hasOcclusionFromOutsideTargetSurface(false)
+{
+}
+
+CCSharedQuadState* CCQuadCuller::useSharedQuadState(PassOwnPtr<CCSharedQuadState> passSharedQuadState)
+{
+ OwnPtr<CCSharedQuadState> sharedQuadState(passSharedQuadState);
+ sharedQuadState->id = m_sharedQuadStateList.size();
+
+ // FIXME: If all quads are culled for the sharedQuadState, we can drop it from the list.
+ m_currentSharedQuadState = sharedQuadState.get();
+ m_sharedQuadStateList.append(sharedQuadState.release());
+ return m_currentSharedQuadState;
+}
+
+static inline bool appendQuadInternal(PassOwnPtr<CCDrawQuad> passDrawQuad, const IntRect& culledRect, CCQuadList& quadList, const CCOcclusionTrackerImpl& occlusionTracker, bool createDebugBorderQuads)
+{
+ OwnPtr<CCDrawQuad> drawQuad(passDrawQuad);
+ bool keepQuad = !culledRect.isEmpty();
+ if (keepQuad)
+ drawQuad->setQuadVisibleRect(culledRect);
+
+ occlusionTracker.overdrawMetrics().didCullForDrawing(drawQuad->quadTransform(), drawQuad->quadRect(), culledRect);
+ occlusionTracker.overdrawMetrics().didDraw(drawQuad->quadTransform(), culledRect, drawQuad->opaqueRect());
+
+ if (keepQuad) {
+ if (createDebugBorderQuads && !drawQuad->isDebugQuad() && drawQuad->quadVisibleRect() != drawQuad->quadRect()) {
+ SkColor borderColor = SkColorSetARGB(debugTileBorderAlpha, debugTileBorderColorRed, debugTileBorderColorGreen, debugTileBorderColorBlue);
+ quadList.append(CCDebugBorderDrawQuad::create(drawQuad->sharedQuadState(), drawQuad->quadVisibleRect(), borderColor, debugTileBorderWidth));
+ }
+
+ // Release the quad after we're done using it.
+ quadList.append(drawQuad.release());
+ }
+ return keepQuad;
+}
+
+bool CCQuadCuller::append(PassOwnPtr<CCDrawQuad> passDrawQuad)
+{
+ ASSERT(passDrawQuad->sharedQuadState() == m_currentSharedQuadState);
+ ASSERT(passDrawQuad->sharedQuadStateId() == m_currentSharedQuadState->id);
+ ASSERT(!m_sharedQuadStateList.isEmpty());
+ ASSERT(m_sharedQuadStateList.last().get() == m_currentSharedQuadState);
+
+ IntRect culledRect;
+ bool hasOcclusionFromOutsideTargetSurface;
+
+ if (m_forSurface)
+ culledRect = m_occlusionTracker->unoccludedContributingSurfaceContentRect(m_layer, false, passDrawQuad->quadRect(), &hasOcclusionFromOutsideTargetSurface);
+ else
+ culledRect = m_occlusionTracker->unoccludedContentRect(m_layer, passDrawQuad->quadRect(), &hasOcclusionFromOutsideTargetSurface);
+ m_hasOcclusionFromOutsideTargetSurface |= hasOcclusionFromOutsideTargetSurface;
+
+ return appendQuadInternal(passDrawQuad, culledRect, m_quadList, *m_occlusionTracker, m_showCullingWithDebugBorderQuads);
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCQuadCuller.h b/cc/CCQuadCuller.h
new file mode 100644
index 0000000..12adaaa
--- /dev/null
+++ b/cc/CCQuadCuller.h
@@ -0,0 +1,40 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCQuadCuller_h
+#define CCQuadCuller_h
+
+#include "CCQuadSink.h"
+#include "CCRenderPass.h"
+
+namespace WebCore {
+class CCLayerImpl;
+class CCRenderSurface;
+template<typename LayerType, typename SurfaceType>
+class CCOcclusionTrackerBase;
+
+class CCQuadCuller : public CCQuadSink {
+public:
+ CCQuadCuller(CCQuadList&, CCSharedQuadStateList&, CCLayerImpl*, const CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>*, bool showCullingWithDebugBorderQuads, bool forSurface);
+ virtual ~CCQuadCuller() { }
+
+ // CCQuadSink implementation.
+ virtual CCSharedQuadState* useSharedQuadState(PassOwnPtr<CCSharedQuadState>) OVERRIDE;
+ virtual bool append(PassOwnPtr<CCDrawQuad>) OVERRIDE;
+
+ bool hasOcclusionFromOutsideTargetSurface() { return m_hasOcclusionFromOutsideTargetSurface; }
+
+private:
+ CCQuadList& m_quadList;
+ CCSharedQuadStateList& m_sharedQuadStateList;
+ CCSharedQuadState* m_currentSharedQuadState;
+ CCLayerImpl* m_layer;
+ const CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>* m_occlusionTracker;
+ bool m_showCullingWithDebugBorderQuads;
+ bool m_forSurface;
+ bool m_hasOcclusionFromOutsideTargetSurface;
+};
+
+}
+#endif // CCQuadCuller_h
diff --git a/cc/CCQuadCullerTest.cpp b/cc/CCQuadCullerTest.cpp
new file mode 100644
index 0000000..76dea20
--- /dev/null
+++ b/cc/CCQuadCullerTest.cpp
@@ -0,0 +1,470 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCQuadCuller.h"
+
+#include "CCLayerTilingData.h"
+#include "CCMathUtil.h"
+#include "CCOcclusionTracker.h"
+#include "CCOverdrawMetrics.h"
+#include "CCSingleThreadProxy.h"
+#include "CCTileDrawQuad.h"
+#include "CCTiledLayerImpl.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+class TestCCOcclusionTrackerImpl : public CCOcclusionTrackerImpl {
+public:
+ TestCCOcclusionTrackerImpl(const IntRect& scissorRectInScreen, bool recordMetricsForFrame = true)
+ : CCOcclusionTrackerImpl(scissorRectInScreen, recordMetricsForFrame)
+ , m_scissorRectInScreen(scissorRectInScreen)
+ {
+ }
+
+protected:
+ virtual IntRect layerScissorRectInTargetSurface(const CCLayerImpl* layer) const { return m_scissorRectInScreen; }
+
+private:
+ IntRect m_scissorRectInScreen;
+};
+
+typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType;
+
+static PassOwnPtr<CCTiledLayerImpl> makeLayer(CCTiledLayerImpl* parent, const WebTransformationMatrix& drawTransform, const IntRect& layerRect, float opacity, bool opaque, const IntRect& layerOpaqueRect, Vector<CCLayerImpl*>& surfaceLayerList)
+{
+ OwnPtr<CCTiledLayerImpl> layer = CCTiledLayerImpl::create(1);
+ OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(IntSize(100, 100), CCLayerTilingData::NoBorderTexels);
+ tiler->setBounds(layerRect.size());
+ layer->setTilingData(*tiler);
+ layer->setSkipsDraw(false);
+ layer->setDrawTransform(drawTransform);
+ layer->setScreenSpaceTransform(drawTransform);
+ layer->setVisibleContentRect(layerRect);
+ layer->setDrawOpacity(opacity);
+ layer->setOpaque(opaque);
+ layer->setBounds(layerRect.size());
+ layer->setContentBounds(layerRect.size());
+
+ CCResourceProvider::ResourceId resourceId = 1;
+ for (int i = 0; i < tiler->numTilesX(); ++i)
+ for (int j = 0; j < tiler->numTilesY(); ++j) {
+ IntRect tileOpaqueRect = opaque ? tiler->tileBounds(i, j) : intersection(tiler->tileBounds(i, j), layerOpaqueRect);
+ layer->pushTileProperties(i, j, resourceId++, tileOpaqueRect);
+ }
+
+ IntRect rectInTarget = CCMathUtil::mapClippedRect(layer->drawTransform(), layer->visibleContentRect());
+ if (!parent) {
+ layer->createRenderSurface();
+ surfaceLayerList.append(layer.get());
+ layer->renderSurface()->layerList().append(layer.get());
+ } else {
+ layer->setRenderTarget(parent->renderTarget());
+ parent->renderSurface()->layerList().append(layer.get());
+ rectInTarget.unite(CCMathUtil::mapClippedRect(parent->drawTransform(), parent->visibleContentRect()));
+ }
+ layer->setDrawableContentRect(rectInTarget);
+
+ return layer.release();
+}
+
+static void appendQuads(CCQuadList& quadList, CCSharedQuadStateList& sharedStateList, CCTiledLayerImpl* layer, CCLayerIteratorType& it, CCOcclusionTrackerImpl& occlusionTracker)
+{
+ occlusionTracker.enterLayer(it);
+ CCQuadCuller quadCuller(quadList, sharedStateList, layer, &occlusionTracker, false, false);
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ occlusionTracker.leaveLayer(it);
+ ++it;
+}
+
+#define DECLARE_AND_INITIALIZE_TEST_QUADS \
+ DebugScopedSetImplThread impl; \
+ CCQuadList quadList; \
+ CCSharedQuadStateList sharedStateList; \
+ Vector<CCLayerImpl*> renderSurfaceLayerList; \
+ WebTransformationMatrix childTransform; \
+ IntSize rootSize = IntSize(300, 300); \
+ IntRect rootRect = IntRect(IntPoint(), rootSize); \
+ IntSize childSize = IntSize(200, 200); \
+ IntRect childRect = IntRect(IntPoint(), childSize);
+
+TEST(CCQuadCullerTest, verifyNoCulling)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, false, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 13u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullChildLinesUpTopLeft)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 9u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 40000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullWhenChildOpacityNotOne)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 0.9f, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 13u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullWhenChildOpaqueFlagFalse)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, false, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 13u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullCenterTileOnly)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(50, 50);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ ASSERT_EQ(quadList.size(), 12u);
+
+ IntRect quadVisibleRect1 = quadList[5].get()->quadVisibleRect();
+ EXPECT_EQ(quadVisibleRect1.height(), 50);
+
+ IntRect quadVisibleRect3 = quadList[7].get()->quadVisibleRect();
+ EXPECT_EQ(quadVisibleRect3.width(), 50);
+
+ // Next index is 8, not 9, since centre quad culled.
+ IntRect quadVisibleRect4 = quadList[8].get()->quadVisibleRect();
+ EXPECT_EQ(quadVisibleRect4.width(), 50);
+ EXPECT_EQ(quadVisibleRect4.x(), 250);
+
+ IntRect quadVisibleRect6 = quadList[10].get()->quadVisibleRect();
+ EXPECT_EQ(quadVisibleRect6.height(), 50);
+ EXPECT_EQ(quadVisibleRect6.y(), 250);
+
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 100000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 30000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize1)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(100, 100);
+
+ // Make the root layer's quad have extent (99.1, 99.1) -> (200.9, 200.9) to make
+ // sure it doesn't get culled due to transform rounding.
+ WebTransformationMatrix rootTransform;
+ rootTransform.translate(99.1, 99.1);
+ rootTransform.scale(1.018);
+
+ rootRect = childRect = IntRect(0, 0, 100, 100);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, rootTransform, rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 2u);
+
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 20363, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize2)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ // Make the child's quad slightly smaller than, and centred over, the root layer tile.
+ // Verify the child does not cause the quad below to be culled due to rounding.
+ childTransform.translate(100.1, 100.1);
+ childTransform.scale(0.982);
+
+ WebTransformationMatrix rootTransform;
+ rootTransform.translate(100, 100);
+
+ rootRect = childRect = IntRect(0, 0, 100, 100);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, rootTransform, rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 2u);
+
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 19643, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullChildLinesUpBottomRight)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(100, 100);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 9u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 40000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullSubRegion)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(50, 50);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() / 2);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, false, childOpaqueRect, renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 12u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 30000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 10000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullSubRegion2)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(50, 10);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() * 3 / 4);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, false, childOpaqueRect, renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 12u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 25000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 15000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullSubRegionCheckOvercull)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ childTransform.translate(50, 49);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() / 2);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, false, childOpaqueRect, renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 13u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 30000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 10000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyNonAxisAlignedQuadsDontOcclude)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ // Use a small rotation so as to not disturb the geometry significantly.
+ childTransform.rotate(1);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 13u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 130000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+// This test requires some explanation: here we are rotating the quads to be culled.
+// The 2x2 tile child layer remains in the top-left corner, unrotated, but the 3x3
+// tile parent layer is rotated by 1 degree. Of the four tiles the child would
+// normally occlude, three will move (slightly) out from under the child layer, and
+// one moves further under the child. Only this last tile should be culled.
+TEST(CCQuadCullerTest, verifyNonAxisAlignedQuadsSafelyCulled)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ // Use a small rotation so as to not disturb the geometry significantly.
+ WebTransformationMatrix parentTransform;
+ parentTransform.rotate(1);
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, parentTransform, rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 12u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 100600, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 29400, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullOutsideScissorOverTile)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(200, 100, 100, 100));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 1u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 10000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 120000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullOutsideScissorOverCulledTile)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(100, 100, 100, 100));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 1u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 10000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 120000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullOutsideScissorOverPartialTiles)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(50, 50, 200, 200));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 9u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 40000, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 90000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyCullOutsideScissorOverNoTiles)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(500, 500, 100, 100));
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 0u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 130000, 1);
+}
+
+TEST(CCQuadCullerTest, verifyWithoutMetrics)
+{
+ DECLARE_AND_INITIALIZE_TEST_QUADS
+
+ OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, WebTransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList);
+ OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), WebTransformationMatrix(), childRect, 1, true, IntRect(), renderSurfaceLayerList);
+ TestCCOcclusionTrackerImpl occlusionTracker(IntRect(50, 50, 200, 200), false);
+ CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList);
+
+ appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker);
+ appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker);
+ EXPECT_EQ(quadList.size(), 9u);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1);
+ EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1);
+}
+
+
+} // namespace
diff --git a/cc/CCQuadSink.h b/cc/CCQuadSink.h
new file mode 100644
index 0000000..7f25fdd
--- /dev/null
+++ b/cc/CCQuadSink.h
@@ -0,0 +1,29 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCQuadSink_h
+#define CCQuadSink_h
+
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCDrawQuad;
+
+struct CCSharedQuadState;
+
+class CCQuadSink {
+public:
+ virtual ~CCQuadSink() { }
+
+ // Call this to add a SharedQuadState before appending quads that refer to it. Returns a pointer
+ // to the given SharedQuadState for convenience, that can be set on the quads to append.
+ virtual CCSharedQuadState* useSharedQuadState(PassOwnPtr<CCSharedQuadState>) = 0;
+
+ // Returns true if the quad is added to the list, and false if the quad is entirely culled.
+ virtual bool append(PassOwnPtr<CCDrawQuad> passDrawQuad) = 0;
+};
+
+}
+#endif // CCQuadCuller_h
diff --git a/cc/CCRenderPass.cpp b/cc/CCRenderPass.cpp
new file mode 100644
index 0000000..2ab855f
--- /dev/null
+++ b/cc/CCRenderPass.cpp
@@ -0,0 +1,92 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCRenderPass.h"
+
+#include "CCLayerImpl.h"
+#include "CCMathUtil.h"
+#include "CCOcclusionTracker.h"
+#include "CCQuadCuller.h"
+#include "CCSharedQuadState.h"
+#include "CCSolidColorDrawQuad.h"
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+PassOwnPtr<CCRenderPass> CCRenderPass::create(int id, IntRect outputRect, const WebKit::WebTransformationMatrix& transformToRootTarget)
+{
+ return adoptPtr(new CCRenderPass(id, outputRect, transformToRootTarget));
+}
+
+CCRenderPass::CCRenderPass(int id, IntRect outputRect, const WebKit::WebTransformationMatrix& transformToRootTarget)
+ : m_id(id)
+ , m_transformToRootTarget(transformToRootTarget)
+ , m_outputRect(outputRect)
+ , m_hasTransparentBackground(true)
+ , m_hasOcclusionFromOutsideTargetSurface(false)
+{
+ ASSERT(id > 0);
+}
+
+void CCRenderPass::appendQuadsForLayer(CCLayerImpl* layer, CCOcclusionTrackerImpl* occlusionTracker, bool& hadMissingTiles)
+{
+ const bool forSurface = false;
+ CCQuadCuller quadCuller(m_quadList, m_sharedQuadStateList, layer, occlusionTracker, layer->hasDebugBorders(), forSurface);
+
+ layer->appendQuads(quadCuller, hadMissingTiles);
+
+ m_hasOcclusionFromOutsideTargetSurface |= quadCuller.hasOcclusionFromOutsideTargetSurface();
+}
+
+void CCRenderPass::appendQuadsForRenderSurfaceLayer(CCLayerImpl* layer, const CCRenderPass* contributingRenderPass, CCOcclusionTrackerImpl* occlusionTracker)
+{
+ const bool forSurface = true;
+ CCQuadCuller quadCuller(m_quadList, m_sharedQuadStateList, layer, occlusionTracker, layer->hasDebugBorders(), forSurface);
+
+ bool isReplica = false;
+ layer->renderSurface()->appendQuads(quadCuller, isReplica, contributingRenderPass->id());
+
+ // Add replica after the surface so that it appears below the surface.
+ if (layer->hasReplica()) {
+ isReplica = true;
+ layer->renderSurface()->appendQuads(quadCuller, isReplica, contributingRenderPass->id());
+ }
+
+ m_hasOcclusionFromOutsideTargetSurface |= quadCuller.hasOcclusionFromOutsideTargetSurface();
+}
+
+void CCRenderPass::appendQuadsToFillScreen(CCLayerImpl* rootLayer, SkColor screenBackgroundColor, const CCOcclusionTrackerImpl& occlusionTracker)
+{
+ if (!rootLayer || !screenBackgroundColor)
+ return;
+
+ Region fillRegion = occlusionTracker.computeVisibleRegionInScreen();
+ if (fillRegion.isEmpty())
+ return;
+
+ bool forSurface = false;
+ CCQuadCuller quadCuller(m_quadList, m_sharedQuadStateList, rootLayer, &occlusionTracker, rootLayer->hasDebugBorders(), forSurface);
+
+ // Manually create the quad state for the gutter quads, as the root layer
+ // doesn't have any bounds and so can't generate this itself.
+ // FIXME: Make the gutter quads generated by the solid color layer (make it smarter about generating quads to fill unoccluded areas).
+ IntRect rootTargetRect = rootLayer->renderSurface()->contentRect();
+ float opacity = 1;
+ bool opaque = true;
+ CCSharedQuadState* sharedQuadState = quadCuller.useSharedQuadState(CCSharedQuadState::create(rootLayer->drawTransform(), rootTargetRect, rootTargetRect, opacity, opaque));
+ ASSERT(rootLayer->screenSpaceTransform().isInvertible());
+ WebTransformationMatrix transformToLayerSpace = rootLayer->screenSpaceTransform().inverse();
+ Vector<IntRect> fillRects = fillRegion.rects();
+ for (size_t i = 0; i < fillRects.size(); ++i) {
+ // The root layer transform is composed of translations and scales only, no perspective, so mapping is sufficient.
+ IntRect layerRect = CCMathUtil::mapClippedRect(transformToLayerSpace, fillRects[i]);
+ // Skip the quad culler and just append the quads directly to avoid occlusion checks.
+ m_quadList.append(CCSolidColorDrawQuad::create(sharedQuadState, layerRect, screenBackgroundColor));
+ }
+}
+
+}
diff --git a/cc/CCRenderPass.h b/cc/CCRenderPass.h
new file mode 100644
index 0000000..b2fa189
--- /dev/null
+++ b/cc/CCRenderPass.h
@@ -0,0 +1,91 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCRenderPass_h
+#define CCRenderPass_h
+
+#include "CCDrawQuad.h"
+#include "CCOcclusionTracker.h"
+#include "CCSharedQuadState.h"
+#include "SkColor.h"
+#include <public/WebFilterOperations.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/HashMap.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCLayerImpl;
+class CCRenderSurface;
+
+// A list of CCDrawQuad objects, sorted internally in front-to-back order.
+class CCQuadList : public Vector<OwnPtr<CCDrawQuad> > {
+public:
+ typedef reverse_iterator backToFrontIterator;
+ typedef const_reverse_iterator constBackToFrontIterator;
+
+ inline backToFrontIterator backToFrontBegin() { return rbegin(); }
+ inline backToFrontIterator backToFrontEnd() { return rend(); }
+ inline constBackToFrontIterator backToFrontBegin() const { return rbegin(); }
+ inline constBackToFrontIterator backToFrontEnd() const { return rend(); }
+};
+
+typedef Vector<OwnPtr<CCSharedQuadState> > CCSharedQuadStateList;
+
+class CCRenderPass {
+ WTF_MAKE_NONCOPYABLE(CCRenderPass);
+public:
+ static PassOwnPtr<CCRenderPass> create(int id, IntRect outputRect, const WebKit::WebTransformationMatrix& transformToRootTarget);
+
+ void appendQuadsForLayer(CCLayerImpl*, CCOcclusionTrackerImpl*, bool& hadMissingTiles);
+ void appendQuadsForRenderSurfaceLayer(CCLayerImpl*, const CCRenderPass* contributingRenderPass, CCOcclusionTrackerImpl*);
+ void appendQuadsToFillScreen(CCLayerImpl* rootLayer, SkColor screenBackgroundColor, const CCOcclusionTrackerImpl&);
+
+ const CCQuadList& quadList() const { return m_quadList; }
+
+ int id() const { return m_id; }
+
+ // FIXME: Modify this transform when merging the RenderPass into a parent compositor.
+ // Transforms from quad's original content space to the root target's content space.
+ const WebKit::WebTransformationMatrix& transformToRootTarget() const { return m_transformToRootTarget; }
+
+ // This denotes the bounds in physical pixels of the output generated by this RenderPass.
+ const IntRect& outputRect() const { return m_outputRect; }
+
+ FloatRect damageRect() const { return m_damageRect; }
+ void setDamageRect(FloatRect rect) { m_damageRect = rect; }
+
+ const WebKit::WebFilterOperations& filters() const { return m_filters; }
+ void setFilters(const WebKit::WebFilterOperations& filters) { m_filters = filters; }
+
+ const WebKit::WebFilterOperations& backgroundFilters() const { return m_backgroundFilters; }
+ void setBackgroundFilters(const WebKit::WebFilterOperations& filters) { m_backgroundFilters = filters; }
+
+ bool hasTransparentBackground() const { return m_hasTransparentBackground; }
+ void setHasTransparentBackground(bool transparent) { m_hasTransparentBackground = transparent; }
+
+ bool hasOcclusionFromOutsideTargetSurface() const { return m_hasOcclusionFromOutsideTargetSurface; }
+ void setHasOcclusionFromOutsideTargetSurface(bool hasOcclusionFromOutsideTargetSurface) { m_hasOcclusionFromOutsideTargetSurface = hasOcclusionFromOutsideTargetSurface; }
+protected:
+ CCRenderPass(int id, IntRect outputRect, const WebKit::WebTransformationMatrix& transformToRootTarget);
+
+ int m_id;
+ CCQuadList m_quadList;
+ CCSharedQuadStateList m_sharedQuadStateList;
+ WebKit::WebTransformationMatrix m_transformToRootTarget;
+ IntRect m_outputRect;
+ FloatRect m_damageRect;
+ bool m_hasTransparentBackground;
+ bool m_hasOcclusionFromOutsideTargetSurface;
+ WebKit::WebFilterOperations m_filters;
+ WebKit::WebFilterOperations m_backgroundFilters;
+};
+
+typedef Vector<CCRenderPass*> CCRenderPassList;
+typedef HashMap<int, OwnPtr<CCRenderPass> > CCRenderPassIdHashMap;
+
+}
+
+#endif
diff --git a/cc/CCRenderPassDrawQuad.cpp b/cc/CCRenderPassDrawQuad.cpp
new file mode 100644
index 0000000..96089c4
--- /dev/null
+++ b/cc/CCRenderPassDrawQuad.cpp
@@ -0,0 +1,36 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCRenderPassDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCRenderPassDrawQuad> CCRenderPassDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, int renderPassId, bool isReplica, const CCResourceProvider::ResourceId maskResourceId, const IntRect& contentsChangedSinceLastFrame, float maskTexCoordScaleX, float maskTexCoordScaleY, float maskTexCoordOffsetX, float maskTexCoordOffsetY)
+{
+ return adoptPtr(new CCRenderPassDrawQuad(sharedQuadState, quadRect, renderPassId, isReplica, maskResourceId, contentsChangedSinceLastFrame, maskTexCoordScaleX, maskTexCoordScaleY, maskTexCoordOffsetX, maskTexCoordOffsetY));
+}
+
+CCRenderPassDrawQuad::CCRenderPassDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, int renderPassId, bool isReplica, CCResourceProvider::ResourceId maskResourceId, const IntRect& contentsChangedSinceLastFrame, float maskTexCoordScaleX, float maskTexCoordScaleY, float maskTexCoordOffsetX, float maskTexCoordOffsetY)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::RenderPass, quadRect)
+ , m_renderPassId(renderPassId)
+ , m_isReplica(isReplica)
+ , m_maskResourceId(maskResourceId)
+ , m_contentsChangedSinceLastFrame(contentsChangedSinceLastFrame)
+ , m_maskTexCoordScaleX(maskTexCoordScaleX)
+ , m_maskTexCoordScaleY(maskTexCoordScaleY)
+ , m_maskTexCoordOffsetX(maskTexCoordOffsetX)
+ , m_maskTexCoordOffsetY(maskTexCoordOffsetY)
+{
+ ASSERT(m_renderPassId > 0);
+}
+
+const CCRenderPassDrawQuad* CCRenderPassDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::RenderPass);
+ return static_cast<const CCRenderPassDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCRenderPassDrawQuad.h b/cc/CCRenderPassDrawQuad.h
new file mode 100644
index 0000000..504e6e1
--- /dev/null
+++ b/cc/CCRenderPassDrawQuad.h
@@ -0,0 +1,48 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCRenderPassDrawQuad_h
+#define CCRenderPassDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "CCResourceProvider.h"
+#include "IntRect.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCRenderPass;
+
+class CCRenderPassDrawQuad : public CCDrawQuad {
+ WTF_MAKE_NONCOPYABLE(CCRenderPassDrawQuad);
+public:
+ static PassOwnPtr<CCRenderPassDrawQuad> create(const CCSharedQuadState*, const IntRect&, int renderPassId, bool isReplica, CCResourceProvider::ResourceId maskResourceId, const IntRect& contentsChangedSinceLastFrame, float maskTexCoordScaleX, float maskTexCoordScaleY, float maskTexCoordOffsetX, float maskTexCoordOffsetY);
+
+ int renderPassId() const { return m_renderPassId; }
+ bool isReplica() const { return m_isReplica; }
+ CCResourceProvider::ResourceId maskResourceId() const { return m_maskResourceId; }
+ const IntRect& contentsChangedSinceLastFrame() const { return m_contentsChangedSinceLastFrame; }
+
+ static const CCRenderPassDrawQuad* materialCast(const CCDrawQuad*);
+ float maskTexCoordScaleX() const { return m_maskTexCoordScaleX; }
+ float maskTexCoordScaleY() const { return m_maskTexCoordScaleY; }
+ float maskTexCoordOffsetX() const { return m_maskTexCoordOffsetX; }
+ float maskTexCoordOffsetY() const { return m_maskTexCoordOffsetY; }
+
+private:
+ CCRenderPassDrawQuad(const CCSharedQuadState*, const IntRect&, int renderPassId, bool isReplica, CCResourceProvider::ResourceId maskResourceId, const IntRect& contentsChangedSinceLastFrame, float maskTexCoordScaleX, float maskTexCoordScaleY, float maskTexCoordOffsetX, float maskTexCoordOffsetY);
+
+ int m_renderPassId;
+ bool m_isReplica;
+ CCResourceProvider::ResourceId m_maskResourceId;
+ IntRect m_contentsChangedSinceLastFrame;
+ float m_maskTexCoordScaleX;
+ float m_maskTexCoordScaleY;
+ float m_maskTexCoordOffsetX;
+ float m_maskTexCoordOffsetY;
+};
+
+}
+
+#endif
diff --git a/cc/CCRenderSurface.cpp b/cc/CCRenderSurface.cpp
new file mode 100644
index 0000000..8e26b73
--- /dev/null
+++ b/cc/CCRenderSurface.cpp
@@ -0,0 +1,213 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCRenderSurface.h"
+
+#include "CCDamageTracker.h"
+#include "CCDebugBorderDrawQuad.h"
+#include "CCLayerImpl.h"
+#include "CCMathUtil.h"
+#include "CCQuadSink.h"
+#include "CCRenderPassDrawQuad.h"
+#include "CCSharedQuadState.h"
+#include "TextStream.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/text/CString.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+static const int debugSurfaceBorderWidth = 2;
+static const int debugSurfaceBorderAlpha = 100;
+static const int debugSurfaceBorderColorRed = 0;
+static const int debugSurfaceBorderColorGreen = 0;
+static const int debugSurfaceBorderColorBlue = 255;
+static const int debugReplicaBorderColorRed = 160;
+static const int debugReplicaBorderColorGreen = 0;
+static const int debugReplicaBorderColorBlue = 255;
+
+CCRenderSurface::CCRenderSurface(CCLayerImpl* owningLayer)
+ : m_owningLayer(owningLayer)
+ , m_surfacePropertyChanged(false)
+ , m_drawOpacity(1)
+ , m_drawOpacityIsAnimating(false)
+ , m_targetSurfaceTransformsAreAnimating(false)
+ , m_screenSpaceTransformsAreAnimating(false)
+ , m_nearestAncestorThatMovesPixels(0)
+ , m_targetRenderSurfaceLayerIndexHistory(0)
+ , m_currentLayerIndexHistory(0)
+{
+ m_damageTracker = CCDamageTracker::create();
+}
+
+CCRenderSurface::~CCRenderSurface()
+{
+}
+
+FloatRect CCRenderSurface::drawableContentRect() const
+{
+ FloatRect drawableContentRect = CCMathUtil::mapClippedRect(m_drawTransform, m_contentRect);
+ if (m_owningLayer->hasReplica())
+ drawableContentRect.unite(CCMathUtil::mapClippedRect(m_replicaDrawTransform, m_contentRect));
+
+ return drawableContentRect;
+}
+
+String CCRenderSurface::name() const
+{
+ return String::format("RenderSurface(id=%i,owner=%s)", m_owningLayer->id(), m_owningLayer->debugName().utf8().data());
+}
+
+static void writeIndent(TextStream& ts, int indent)
+{
+ for (int i = 0; i != indent; ++i)
+ ts << " ";
+}
+
+void CCRenderSurface::dumpSurface(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << name() << "\n";
+
+ writeIndent(ts, indent+1);
+ ts << "contentRect: (" << m_contentRect.x() << ", " << m_contentRect.y() << ", " << m_contentRect.width() << ", " << m_contentRect.height() << "\n";
+
+ writeIndent(ts, indent+1);
+ ts << "drawTransform: ";
+ ts << m_drawTransform.m11() << ", " << m_drawTransform.m12() << ", " << m_drawTransform.m13() << ", " << m_drawTransform.m14() << " // ";
+ ts << m_drawTransform.m21() << ", " << m_drawTransform.m22() << ", " << m_drawTransform.m23() << ", " << m_drawTransform.m24() << " // ";
+ ts << m_drawTransform.m31() << ", " << m_drawTransform.m32() << ", " << m_drawTransform.m33() << ", " << m_drawTransform.m34() << " // ";
+ ts << m_drawTransform.m41() << ", " << m_drawTransform.m42() << ", " << m_drawTransform.m43() << ", " << m_drawTransform.m44() << "\n";
+
+ writeIndent(ts, indent+1);
+ ts << "damageRect is pos(" << m_damageTracker->currentDamageRect().x() << "," << m_damageTracker->currentDamageRect().y() << "), ";
+ ts << "size(" << m_damageTracker->currentDamageRect().width() << "," << m_damageTracker->currentDamageRect().height() << ")\n";
+}
+
+int CCRenderSurface::owningLayerId() const
+{
+ return m_owningLayer ? m_owningLayer->id() : 0;
+}
+
+
+void CCRenderSurface::setClipRect(const IntRect& clipRect)
+{
+ if (m_clipRect == clipRect)
+ return;
+
+ m_surfacePropertyChanged = true;
+ m_clipRect = clipRect;
+}
+
+bool CCRenderSurface::contentsChanged() const
+{
+ return !m_damageTracker->currentDamageRect().isEmpty();
+}
+
+void CCRenderSurface::setContentRect(const IntRect& contentRect)
+{
+ if (m_contentRect == contentRect)
+ return;
+
+ m_surfacePropertyChanged = true;
+ m_contentRect = contentRect;
+}
+
+bool CCRenderSurface::surfacePropertyChanged() const
+{
+ // Surface property changes are tracked as follows:
+ //
+ // - m_surfacePropertyChanged is flagged when the clipRect or contentRect change. As
+ // of now, these are the only two properties that can be affected by descendant layers.
+ //
+ // - all other property changes come from the owning layer (or some ancestor layer
+ // that propagates its change to the owning layer).
+ //
+ ASSERT(m_owningLayer);
+ return m_surfacePropertyChanged || m_owningLayer->layerPropertyChanged();
+}
+
+bool CCRenderSurface::surfacePropertyChangedOnlyFromDescendant() const
+{
+ return m_surfacePropertyChanged && !m_owningLayer->layerPropertyChanged();
+}
+
+static inline IntRect computeClippedRectInTarget(const CCLayerImpl* owningLayer)
+{
+ ASSERT(owningLayer->parent());
+
+ const CCLayerImpl* renderTarget = owningLayer->parent()->renderTarget();
+ const CCRenderSurface* self = owningLayer->renderSurface();
+
+ IntRect clippedRectInTarget = self->clipRect();
+ if (owningLayer->backgroundFilters().hasFilterThatMovesPixels()) {
+ // If the layer has background filters that move pixels, we cannot scissor as tightly.
+ // FIXME: this should be able to be a tighter scissor, perhaps expanded by the filter outsets?
+ clippedRectInTarget = renderTarget->renderSurface()->contentRect();
+ } else if (clippedRectInTarget.isEmpty()) {
+ // For surfaces, empty clipRect means that the surface does not clip anything.
+ clippedRectInTarget = enclosingIntRect(intersection(renderTarget->renderSurface()->contentRect(), self->drawableContentRect()));
+ } else
+ clippedRectInTarget.intersect(enclosingIntRect(self->drawableContentRect()));
+ return clippedRectInTarget;
+}
+
+void CCRenderSurface::appendQuads(CCQuadSink& quadSink, bool forReplica, int renderPassId)
+{
+ ASSERT(!forReplica || m_owningLayer->hasReplica());
+
+ IntRect clippedRectInTarget = computeClippedRectInTarget(m_owningLayer);
+ bool isOpaque = false;
+ const WebTransformationMatrix& drawTransform = forReplica ? m_replicaDrawTransform : m_drawTransform;
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(CCSharedQuadState::create(drawTransform, m_contentRect, clippedRectInTarget, m_drawOpacity, isOpaque));
+
+ if (m_owningLayer->hasDebugBorders()) {
+ int red = forReplica ? debugReplicaBorderColorRed : debugSurfaceBorderColorRed;
+ int green = forReplica ? debugReplicaBorderColorGreen : debugSurfaceBorderColorGreen;
+ int blue = forReplica ? debugReplicaBorderColorBlue : debugSurfaceBorderColorBlue;
+ SkColor color = SkColorSetARGB(debugSurfaceBorderAlpha, red, green, blue);
+ quadSink.append(CCDebugBorderDrawQuad::create(sharedQuadState, contentRect(), color, debugSurfaceBorderWidth));
+ }
+
+ // FIXME: By using the same RenderSurface for both the content and its reflection,
+ // it's currently not possible to apply a separate mask to the reflection layer
+ // or correctly handle opacity in reflections (opacity must be applied after drawing
+ // both the layer and its reflection). The solution is to introduce yet another RenderSurface
+ // to draw the layer and its reflection in. For now we only apply a separate reflection
+ // mask if the contents don't have a mask of their own.
+ CCLayerImpl* maskLayer = m_owningLayer->maskLayer();
+ if (maskLayer && (!maskLayer->drawsContent() || maskLayer->bounds().isEmpty()))
+ maskLayer = 0;
+
+ if (!maskLayer && forReplica) {
+ maskLayer = m_owningLayer->replicaLayer()->maskLayer();
+ if (maskLayer && (!maskLayer->drawsContent() || maskLayer->bounds().isEmpty()))
+ maskLayer = 0;
+ }
+
+ float maskTexCoordScaleX = 1;
+ float maskTexCoordScaleY = 1;
+ float maskTexCoordOffsetX = 1;
+ float maskTexCoordOffsetY = 1;
+ if (maskLayer) {
+ maskTexCoordScaleX = static_cast<float>(contentRect().width()) / maskLayer->contentBounds().width();
+ maskTexCoordScaleY = static_cast<float>(contentRect().height()) / maskLayer->contentBounds().height();
+ maskTexCoordOffsetX = static_cast<float>(contentRect().x()) / contentRect().width() * maskTexCoordScaleX;
+ maskTexCoordOffsetY = static_cast<float>(contentRect().y()) / contentRect().height() * maskTexCoordScaleY;
+ }
+
+ CCResourceProvider::ResourceId maskResourceId = maskLayer ? maskLayer->contentsResourceId() : 0;
+ IntRect contentsChangedSinceLastFrame = contentsChanged() ? m_contentRect : IntRect();
+
+ quadSink.append(CCRenderPassDrawQuad::create(sharedQuadState, contentRect(), renderPassId, forReplica, maskResourceId, contentsChangedSinceLastFrame,
+ maskTexCoordScaleX, maskTexCoordScaleY, maskTexCoordOffsetX, maskTexCoordOffsetY));
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCRenderSurface.h b/cc/CCRenderSurface.h
new file mode 100644
index 0000000..df62d34
--- /dev/null
+++ b/cc/CCRenderSurface.h
@@ -0,0 +1,124 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCRenderSurface_h
+#define CCRenderSurface_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCSharedQuadState.h"
+#include "FloatRect.h"
+#include "IntRect.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CCDamageTracker;
+class CCQuadSink;
+class CCRenderPass;
+class CCLayerImpl;
+class TextStream;
+
+class CCRenderSurface {
+ WTF_MAKE_NONCOPYABLE(CCRenderSurface);
+public:
+ explicit CCRenderSurface(CCLayerImpl*);
+ virtual ~CCRenderSurface();
+
+ String name() const;
+ void dumpSurface(TextStream&, int indent) const;
+
+ FloatPoint contentRectCenter() const { return FloatRect(m_contentRect).center(); }
+
+ // Returns the rect that encloses the RenderSurface including any reflection.
+ FloatRect drawableContentRect() const;
+
+ float drawOpacity() const { return m_drawOpacity; }
+ void setDrawOpacity(float opacity) { m_drawOpacity = opacity; }
+
+ void setNearestAncestorThatMovesPixels(CCRenderSurface* surface) { m_nearestAncestorThatMovesPixels = surface; }
+ const CCRenderSurface* nearestAncestorThatMovesPixels() const { return m_nearestAncestorThatMovesPixels; }
+
+ bool drawOpacityIsAnimating() const { return m_drawOpacityIsAnimating; }
+ void setDrawOpacityIsAnimating(bool drawOpacityIsAnimating) { m_drawOpacityIsAnimating = drawOpacityIsAnimating; }
+
+ void setDrawTransform(const WebKit::WebTransformationMatrix& drawTransform) { m_drawTransform = drawTransform; }
+ const WebKit::WebTransformationMatrix& drawTransform() const { return m_drawTransform; }
+
+ void setScreenSpaceTransform(const WebKit::WebTransformationMatrix& screenSpaceTransform) { m_screenSpaceTransform = screenSpaceTransform; }
+ const WebKit::WebTransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
+
+ void setReplicaDrawTransform(const WebKit::WebTransformationMatrix& replicaDrawTransform) { m_replicaDrawTransform = replicaDrawTransform; }
+ const WebKit::WebTransformationMatrix& replicaDrawTransform() const { return m_replicaDrawTransform; }
+
+ void setReplicaScreenSpaceTransform(const WebKit::WebTransformationMatrix& replicaScreenSpaceTransform) { m_replicaScreenSpaceTransform = replicaScreenSpaceTransform; }
+ const WebKit::WebTransformationMatrix& replicaScreenSpaceTransform() const { return m_replicaScreenSpaceTransform; }
+
+ bool targetSurfaceTransformsAreAnimating() const { return m_targetSurfaceTransformsAreAnimating; }
+ void setTargetSurfaceTransformsAreAnimating(bool animating) { m_targetSurfaceTransformsAreAnimating = animating; }
+ bool screenSpaceTransformsAreAnimating() const { return m_screenSpaceTransformsAreAnimating; }
+ void setScreenSpaceTransformsAreAnimating(bool animating) { m_screenSpaceTransformsAreAnimating = animating; }
+
+ void setClipRect(const IntRect&);
+ const IntRect& clipRect() const { return m_clipRect; }
+
+ bool contentsChanged() const;
+
+ void setContentRect(const IntRect&);
+ const IntRect& contentRect() const { return m_contentRect; }
+
+ void clearLayerList() { m_layerList.clear(); }
+ Vector<CCLayerImpl*>& layerList() { return m_layerList; }
+
+ int owningLayerId() const;
+
+ void resetPropertyChangedFlag() { m_surfacePropertyChanged = false; }
+ bool surfacePropertyChanged() const;
+ bool surfacePropertyChangedOnlyFromDescendant() const;
+
+ CCDamageTracker* damageTracker() const { return m_damageTracker.get(); }
+
+ void appendQuads(CCQuadSink&, bool forReplica, int renderPassId);
+
+private:
+ CCLayerImpl* m_owningLayer;
+
+ // Uses this surface's space.
+ IntRect m_contentRect;
+ bool m_surfacePropertyChanged;
+
+ float m_drawOpacity;
+ bool m_drawOpacityIsAnimating;
+ WebKit::WebTransformationMatrix m_drawTransform;
+ WebKit::WebTransformationMatrix m_screenSpaceTransform;
+ WebKit::WebTransformationMatrix m_replicaDrawTransform;
+ WebKit::WebTransformationMatrix m_replicaScreenSpaceTransform;
+ bool m_targetSurfaceTransformsAreAnimating;
+ bool m_screenSpaceTransformsAreAnimating;
+
+ // Uses the space of the surface's target surface.
+ IntRect m_clipRect;
+
+ Vector<CCLayerImpl*> m_layerList;
+
+ // The nearest ancestor target surface that will contain the contents of this surface, and that is going
+ // to move pixels within the surface (such as with a blur). This can point to itself.
+ CCRenderSurface* m_nearestAncestorThatMovesPixels;
+
+ OwnPtr<CCDamageTracker> m_damageTracker;
+
+ // For CCLayerIteratorActions
+ int m_targetRenderSurfaceLayerIndexHistory;
+ int m_currentLayerIndexHistory;
+
+ friend struct CCLayerIteratorActions;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCRenderSurfaceFilters.cpp b/cc/CCRenderSurfaceFilters.cpp
new file mode 100644
index 0000000..3ea246e
--- /dev/null
+++ b/cc/CCRenderSurfaceFilters.cpp
@@ -0,0 +1,443 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCRenderSurfaceFilters.h"
+
+#include "FloatSize.h"
+#include "SkBlurImageFilter.h"
+#include "SkCanvas.h"
+#include "SkColorMatrixFilter.h"
+#include "SkGpuDevice.h"
+#include "SkGrTexturePixelRef.h"
+#include "SkMagnifierImageFilter.h"
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+#include <public/WebGraphicsContext3D.h>
+#include <wtf/MathExtras.h>
+
+namespace {
+
+void getBrightnessMatrix(float amount, SkScalar matrix[20])
+{
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ // Old implementation, a la the draft spec, a straight-up scale,
+ // representing <feFunc[R|G|B] type="linear" slope="[amount]">
+ // (See http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
+ // matrix[0] = matrix[6] = matrix[12] = amount;
+ // matrix[18] = 1;
+ // New implementation, a translation in color space, representing
+ // <feFunc[R|G|B] type="linear" intercept="[amount]"/>
+ // (See https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647)
+ matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1;
+ matrix[4] = matrix[9] = matrix[14] = amount * 255;
+}
+
+void getContrastMatrix(float amount, SkScalar matrix[20])
+{
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = amount;
+ matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f) * 255;
+ matrix[18] = 1;
+}
+
+void getSaturateMatrix(float amount, SkScalar matrix[20])
+{
+ // Note, these values are computed to ensure matrixNeedsClamping is false
+ // for amount in [0..1]
+ matrix[0] = 0.213f + 0.787f * amount;
+ matrix[1] = 0.715f - 0.715f * amount;
+ matrix[2] = 1.f - (matrix[0] + matrix[1]);
+ matrix[3] = matrix[4] = 0;
+ matrix[5] = 0.213f - 0.213f * amount;
+ matrix[6] = 0.715f + 0.285f * amount;
+ matrix[7] = 1.f - (matrix[5] + matrix[6]);
+ matrix[8] = matrix[9] = 0;
+ matrix[10] = 0.213f - 0.213f * amount;
+ matrix[11] = 0.715f - 0.715f * amount;
+ matrix[12] = 1.f - (matrix[10] + matrix[11]);
+ matrix[13] = matrix[14] = 0;
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
+ matrix[18] = 1;
+}
+
+void getHueRotateMatrix(float hue, SkScalar matrix[20])
+{
+ float cosHue = cosf(hue * piFloat / 180);
+ float sinHue = sinf(hue * piFloat / 180);
+ matrix[0] = 0.213f + cosHue * 0.787f - sinHue * 0.213f;
+ matrix[1] = 0.715f - cosHue * 0.715f - sinHue * 0.715f;
+ matrix[2] = 0.072f - cosHue * 0.072f + sinHue * 0.928f;
+ matrix[3] = matrix[4] = 0;
+ matrix[5] = 0.213f - cosHue * 0.213f + sinHue * 0.143f;
+ matrix[6] = 0.715f + cosHue * 0.285f + sinHue * 0.140f;
+ matrix[7] = 0.072f - cosHue * 0.072f - sinHue * 0.283f;
+ matrix[8] = matrix[9] = 0;
+ matrix[10] = 0.213f - cosHue * 0.213f - sinHue * 0.787f;
+ matrix[11] = 0.715f - cosHue * 0.715f + sinHue * 0.715f;
+ matrix[12] = 0.072f + cosHue * 0.928f + sinHue * 0.072f;
+ matrix[13] = matrix[14] = 0;
+ matrix[15] = matrix[16] = matrix[17] = 0;
+ matrix[18] = 1;
+ matrix[19] = 0;
+}
+
+void getInvertMatrix(float amount, SkScalar matrix[20])
+{
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = 1 - 2 * amount;
+ matrix[4] = matrix[9] = matrix[14] = amount * 255;
+ matrix[18] = 1;
+}
+
+void getOpacityMatrix(float amount, SkScalar matrix[20])
+{
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = 1;
+ matrix[18] = amount;
+}
+
+void getGrayscaleMatrix(float amount, SkScalar matrix[20])
+{
+ // Note, these values are computed to ensure matrixNeedsClamping is false
+ // for amount in [0..1]
+ matrix[0] = 0.2126f + 0.7874f * amount;
+ matrix[1] = 0.7152f - 0.7152f * amount;
+ matrix[2] = 1.f - (matrix[0] + matrix[1]);
+ matrix[3] = matrix[4] = 0;
+
+ matrix[5] = 0.2126f - 0.2126f * amount;
+ matrix[6] = 0.7152f + 0.2848f * amount;
+ matrix[7] = 1.f - (matrix[5] + matrix[6]);
+ matrix[8] = matrix[9] = 0;
+
+ matrix[10] = 0.2126f - 0.2126f * amount;
+ matrix[11] = 0.7152f - 0.7152f * amount;
+ matrix[12] = 1.f - (matrix[10] + matrix[11]);
+ matrix[13] = matrix[14] = 0;
+
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
+ matrix[18] = 1;
+}
+
+void getSepiaMatrix(float amount, SkScalar matrix[20])
+{
+ matrix[0] = 0.393f + 0.607f * amount;
+ matrix[1] = 0.769f - 0.769f * amount;
+ matrix[2] = 0.189f - 0.189f * amount;
+ matrix[3] = matrix[4] = 0;
+
+ matrix[5] = 0.349f - 0.349f * amount;
+ matrix[6] = 0.686f + 0.314f * amount;
+ matrix[7] = 0.168f - 0.168f * amount;
+ matrix[8] = matrix[9] = 0;
+
+ matrix[10] = 0.272f - 0.272f * amount;
+ matrix[11] = 0.534f - 0.534f * amount;
+ matrix[12] = 0.131f + 0.869f * amount;
+ matrix[13] = matrix[14] = 0;
+
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
+ matrix[18] = 1;
+}
+
+// The 5x4 matrix is really a "compressed" version of a 5x5 matrix that'd have
+// (0 0 0 0 1) as a last row, and that would be applied to a 5-vector extended
+// from the 4-vector color with a 1.
+void multColorMatrix(SkScalar a[20], SkScalar b[20], SkScalar out[20])
+{
+ for (int j = 0; j < 4; ++j) {
+ for (int i = 0; i < 5; ++i) {
+ out[i+j*5] = i == 4 ? a[4+j*5] : 0;
+ for (int k = 0; k < 4; ++k)
+ out[i+j*5] += a[k+j*5] * b[i+k*5];
+ }
+ }
+}
+
+// To detect if we need to apply clamping after applying a matrix, we check if
+// any output component might go outside of [0, 255] for any combination of
+// input components in [0..255].
+// Each output component is an affine transformation of the input component, so
+// the minimum and maximum values are for any combination of minimum or maximum
+// values of input components (i.e. 0 or 255).
+// E.g. if R' = x*R + y*G + z*B + w*A + t
+// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
+// minimum value will be for R=0 if x>0 or R=255 if x<0.
+// Same goes for all components.
+bool componentNeedsClamping(SkScalar row[5])
+{
+ SkScalar maxValue = row[4] / 255;
+ SkScalar minValue = row[4] / 255;
+ for (int i = 0; i < 4; ++i) {
+ if (row[i] > 0)
+ maxValue += row[i];
+ else
+ minValue += row[i];
+ }
+ return (maxValue > 1) || (minValue < 0);
+}
+
+bool matrixNeedsClamping(SkScalar matrix[20])
+{
+ return componentNeedsClamping(matrix)
+ || componentNeedsClamping(matrix+5)
+ || componentNeedsClamping(matrix+10)
+ || componentNeedsClamping(matrix+15);
+}
+
+bool getColorMatrix(const WebKit::WebFilterOperation& op, SkScalar matrix[20])
+{
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeBrightness: {
+ getBrightnessMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeContrast: {
+ getContrastMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeGrayscale: {
+ getGrayscaleMatrix(1 - op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeSepia: {
+ getSepiaMatrix(1 - op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeSaturate: {
+ getSaturateMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeHueRotate: {
+ getHueRotateMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeInvert: {
+ getInvertMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeOpacity: {
+ getOpacityMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix: {
+ memcpy(matrix, op.matrix(), sizeof(SkScalar[20]));
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+class FilterBufferState {
+public:
+ FilterBufferState(GrContext* grContext, const WebCore::FloatSize& size, unsigned textureId)
+ : m_grContext(grContext)
+ , m_currentTexture(0)
+ {
+ // Wrap the source texture in a Ganesh platform texture.
+ GrPlatformTextureDesc platformTextureDescription;
+ platformTextureDescription.fWidth = size.width();
+ platformTextureDescription.fHeight = size.height();
+ platformTextureDescription.fConfig = kSkia8888_PM_GrPixelConfig;
+ platformTextureDescription.fTextureHandle = textureId;
+ SkAutoTUnref<GrTexture> texture(grContext->createPlatformTexture(platformTextureDescription));
+ // Place the platform texture inside an SkBitmap.
+ m_source.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ m_source.setPixelRef(new SkGrTexturePixelRef(texture.get()))->unref();
+ }
+
+ ~FilterBufferState() { }
+
+ bool init(int filterCount)
+ {
+ int scratchCount = std::min(2, filterCount);
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fSampleCnt = 0;
+ desc.fWidth = m_source.width();
+ desc.fHeight = m_source.height();
+ desc.fConfig = kSkia8888_PM_GrPixelConfig;
+ for (int i = 0; i < scratchCount; ++i) {
+ GrAutoScratchTexture scratchTexture(m_grContext, desc, GrContext::kExact_ScratchTexMatch);
+ m_scratchTextures[i].reset(scratchTexture.detach());
+ if (!m_scratchTextures[i].get())
+ return false;
+ }
+ return true;
+ }
+
+ SkCanvas* canvas()
+ {
+ if (!m_canvas.get())
+ createCanvas();
+ return m_canvas.get();
+ }
+
+ const SkBitmap& source() { return m_source; }
+
+ void swap()
+ {
+ m_canvas->flush();
+ m_canvas.reset(0);
+ m_device.reset(0);
+
+ m_source.setPixelRef(new SkGrTexturePixelRef(m_scratchTextures[m_currentTexture].get()))->unref();
+ m_currentTexture = 1 - m_currentTexture;
+ }
+
+private:
+ void createCanvas()
+ {
+ ASSERT(m_scratchTextures[m_currentTexture].get());
+ m_device.reset(new SkGpuDevice(m_grContext, m_scratchTextures[m_currentTexture].get()));
+ m_canvas.reset(new SkCanvas(m_device.get()));
+ m_canvas->clear(0x0);
+ }
+
+ GrContext* m_grContext;
+ SkBitmap m_source;
+ SkAutoTUnref<GrTexture> m_scratchTextures[2];
+ int m_currentTexture;
+ SkAutoTUnref<SkGpuDevice> m_device;
+ SkAutoTUnref<SkCanvas> m_canvas;
+};
+
+}
+
+namespace WebCore {
+
+WebKit::WebFilterOperations CCRenderSurfaceFilters::optimize(const WebKit::WebFilterOperations& filters)
+{
+ WebKit::WebFilterOperations newList;
+
+ SkScalar accumulatedColorMatrix[20];
+ bool haveAccumulatedColorMatrix = false;
+ for (unsigned i = 0; i < filters.size(); ++i) {
+ const WebKit::WebFilterOperation& op = filters.at(i);
+
+ // If the filter is a color matrix, we may be able to combine it with
+ // following filter(s) that also are color matrices.
+ SkScalar matrix[20];
+ if (getColorMatrix(op, matrix)) {
+ if (haveAccumulatedColorMatrix) {
+ SkScalar newMatrix[20];
+ multColorMatrix(matrix, accumulatedColorMatrix, newMatrix);
+ memcpy(accumulatedColorMatrix, newMatrix, sizeof(accumulatedColorMatrix));
+ } else {
+ memcpy(accumulatedColorMatrix, matrix, sizeof(accumulatedColorMatrix));
+ haveAccumulatedColorMatrix = true;
+ }
+
+ // We can only combine matrices if clamping of color components
+ // would have no effect.
+ if (!matrixNeedsClamping(accumulatedColorMatrix))
+ continue;
+ }
+
+ if (haveAccumulatedColorMatrix)
+ newList.append(WebKit::WebFilterOperation::createColorMatrixFilter(accumulatedColorMatrix));
+ haveAccumulatedColorMatrix = false;
+
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeBlur:
+ case WebKit::WebFilterOperation::FilterTypeDropShadow:
+ case WebKit::WebFilterOperation::FilterTypeZoom:
+ newList.append(op);
+ break;
+ case WebKit::WebFilterOperation::FilterTypeBrightness:
+ case WebKit::WebFilterOperation::FilterTypeContrast:
+ case WebKit::WebFilterOperation::FilterTypeGrayscale:
+ case WebKit::WebFilterOperation::FilterTypeSepia:
+ case WebKit::WebFilterOperation::FilterTypeSaturate:
+ case WebKit::WebFilterOperation::FilterTypeHueRotate:
+ case WebKit::WebFilterOperation::FilterTypeInvert:
+ case WebKit::WebFilterOperation::FilterTypeOpacity:
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix:
+ break;
+ }
+ }
+ if (haveAccumulatedColorMatrix)
+ newList.append(WebKit::WebFilterOperation::createColorMatrixFilter(accumulatedColorMatrix));
+ return newList;
+}
+
+SkBitmap CCRenderSurfaceFilters::apply(const WebKit::WebFilterOperations& filters, unsigned textureId, const FloatSize& size, WebKit::WebGraphicsContext3D* context3D, GrContext* grContext)
+{
+ if (!context3D || !grContext)
+ return SkBitmap();
+
+ WebKit::WebFilterOperations optimizedFilters = optimize(filters);
+ FilterBufferState state(grContext, size, textureId);
+ if (!state.init(optimizedFilters.size()))
+ return SkBitmap();
+
+ for (unsigned i = 0; i < optimizedFilters.size(); ++i) {
+ const WebKit::WebFilterOperation& op = optimizedFilters.at(i);
+ SkCanvas* canvas = state.canvas();
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix: {
+ SkPaint paint;
+ paint.setColorFilter(new SkColorMatrixFilter(op.matrix()))->unref();
+ canvas->drawBitmap(state.source(), 0, 0, &paint);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeBlur: {
+ float stdDeviation = op.amount();
+ SkAutoTUnref<SkImageFilter> filter(new SkBlurImageFilter(stdDeviation, stdDeviation));
+ SkPaint paint;
+ paint.setImageFilter(filter.get());
+ canvas->drawSprite(state.source(), 0, 0, &paint);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeDropShadow: {
+ SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(op.amount(), op.amount()));
+ SkAutoTUnref<SkColorFilter> colorFilter(SkColorFilter::CreateModeFilter(op.dropShadowColor(), SkXfermode::kSrcIn_Mode));
+ SkPaint paint;
+ paint.setImageFilter(blurFilter.get());
+ paint.setColorFilter(colorFilter.get());
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ canvas->saveLayer(0, &paint);
+ canvas->drawBitmap(state.source(), op.dropShadowOffset().x, -op.dropShadowOffset().y);
+ canvas->restore();
+ canvas->drawBitmap(state.source(), 0, 0);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeZoom: {
+ SkPaint paint;
+ SkAutoTUnref<SkImageFilter> zoomFilter(
+ new SkMagnifierImageFilter(
+ SkRect::MakeXYWH(op.zoomRect().x,
+ op.zoomRect().y,
+ op.zoomRect().width,
+ op.zoomRect().height),
+ op.amount()));
+ paint.setImageFilter(zoomFilter.get());
+ canvas->saveLayer(0, &paint);
+ canvas->drawBitmap(state.source(), 0, 0);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeBrightness:
+ case WebKit::WebFilterOperation::FilterTypeContrast:
+ case WebKit::WebFilterOperation::FilterTypeGrayscale:
+ case WebKit::WebFilterOperation::FilterTypeSepia:
+ case WebKit::WebFilterOperation::FilterTypeSaturate:
+ case WebKit::WebFilterOperation::FilterTypeHueRotate:
+ case WebKit::WebFilterOperation::FilterTypeInvert:
+ case WebKit::WebFilterOperation::FilterTypeOpacity:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ state.swap();
+ }
+ context3D->flush();
+ return state.source();
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCRenderSurfaceFilters.h b/cc/CCRenderSurfaceFilters.h
new file mode 100644
index 0000000..92cd716
--- /dev/null
+++ b/cc/CCRenderSurfaceFilters.h
@@ -0,0 +1,34 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCRenderSurfaceFilters_h
+#define CCRenderSurfaceFilters_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+class GrContext;
+class SkBitmap;
+
+namespace WebKit {
+class WebFilterOperations;
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+class FloatSize;
+
+class CCRenderSurfaceFilters {
+public:
+ static SkBitmap apply(const WebKit::WebFilterOperations& filters, unsigned textureId, const FloatSize&, WebKit::WebGraphicsContext3D*, GrContext*);
+ static WebKit::WebFilterOperations optimize(const WebKit::WebFilterOperations& filters);
+
+private:
+ CCRenderSurfaceFilters();
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCRenderSurfaceFiltersTest.cpp b/cc/CCRenderSurfaceFiltersTest.cpp
new file mode 100644
index 0000000..8ec4332
--- /dev/null
+++ b/cc/CCRenderSurfaceFiltersTest.cpp
@@ -0,0 +1,141 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCRenderSurfaceFilters.h"
+
+#include "CompositorFakeWebGraphicsContext3D.h"
+#include <gtest/gtest.h>
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+#include <wtf/RefPtr.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+namespace {
+
+// Checks whether op can be combined with a following color matrix.
+bool isCombined(const WebFilterOperation& op)
+{
+ WebFilterOperations filters;
+ filters.append(op);
+ filters.append(WebFilterOperation::createBrightnessFilter(0)); // brightness(0) is identity.
+ WebFilterOperations optimized = CCRenderSurfaceFilters::optimize(filters);
+ return optimized.size() == 1;
+}
+
+TEST(CCRenderSurfaceFiltersTest, testColorMatrixFiltersCombined)
+{
+ // Several filters should always combine for any amount between 0 and 1:
+ // grayscale, saturate, invert, contrast, opacity.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0)));
+ // Note that we use 0.3f to avoid "argument is truncated from 'double' to
+ // 'float'" warnings on Windows. 0.5 is exactly representable as a float, so
+ // there is no warning.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(1)));
+
+ // Several filters should never combine: brightness(amount > 0), blur, drop-shadow.
+ EXPECT_FALSE(isCombined(WebFilterOperation::createBrightnessFilter(0.5)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createBrightnessFilter(1)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createBlurFilter(3)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createDropShadowFilter(WebPoint(2, 2), 3, 0xffffffff)));
+
+ // sepia and hue may or may not combine depending on the value.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSepiaFilter(0)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createSepiaFilter(1)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createHueRotateFilter(0)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createHueRotateFilter(180)));
+
+ float matrix1[20] = {
+ 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix1)));
+
+ float matrix2[20] = {
+ 1, 1, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_FALSE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix2)));
+
+ float matrix3[20] = {
+ 0.25, 0, 0, 0, 255*0.75,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix3)));
+
+ float matrix4[20] = {
+ -0.25, 0.75, 0, 0, 255*0.25,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix4)));
+}
+
+TEST(CCRenderSurfaceFiltersTest, testOptimize)
+{
+ WebFilterOperation combines(WebFilterOperation::createBrightnessFilter(0));
+ WebFilterOperation doesntCombine(WebFilterOperation::createBrightnessFilter(1));
+
+ WebFilterOperations filters;
+ WebFilterOperations optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(0u, optimized.size());
+
+ filters.append(combines);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(combines);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(combines);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(2u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(2u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = CCRenderSurfaceFilters::optimize(filters);
+ EXPECT_EQ(3u, optimized.size());
+}
+
+} // namespace
diff --git a/cc/CCRenderSurfaceTest.cpp b/cc/CCRenderSurfaceTest.cpp
new file mode 100644
index 0000000..c044993
--- /dev/null
+++ b/cc/CCRenderSurfaceTest.cpp
@@ -0,0 +1,116 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCRenderSurface.h"
+
+#include "CCLayerImpl.h"
+#include "CCSharedQuadState.h"
+#include "CCSingleThreadProxy.h"
+#include "MockCCQuadCuller.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <public/WebTransformationMatrix.h>
+
+using namespace WebCore;
+using WebKit::WebTransformationMatrix;
+
+namespace {
+
+#define EXECUTE_AND_VERIFY_SURFACE_CHANGED(codeToTest) \
+ renderSurface->resetPropertyChangedFlag(); \
+ codeToTest; \
+ EXPECT_TRUE(renderSurface->surfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(codeToTest) \
+ renderSurface->resetPropertyChangedFlag(); \
+ codeToTest; \
+ EXPECT_FALSE(renderSurface->surfacePropertyChanged())
+
+TEST(CCRenderSurfaceTest, verifySurfaceChangesAreTrackedProperly)
+{
+ //
+ // This test checks that surfacePropertyChanged() has the correct behavior.
+ //
+
+ // This will fake that we are on the correct thread for testing purposes.
+ DebugScopedSetImplThread setImplThread;
+
+ OwnPtr<CCLayerImpl> owningLayer = CCLayerImpl::create(1);
+ owningLayer->createRenderSurface();
+ ASSERT_TRUE(owningLayer->renderSurface());
+ CCRenderSurface* renderSurface = owningLayer->renderSurface();
+ IntRect testRect = IntRect(IntPoint(3, 4), IntSize(5, 6));
+ owningLayer->resetAllChangeTrackingForSubtree();
+
+ // Currently, the contentRect, clipRect, and owningLayer->layerPropertyChanged() are
+ // the only sources of change.
+ EXECUTE_AND_VERIFY_SURFACE_CHANGED(renderSurface->setClipRect(testRect));
+ EXECUTE_AND_VERIFY_SURFACE_CHANGED(renderSurface->setContentRect(testRect));
+
+ owningLayer->setOpacity(0.5f);
+ EXPECT_TRUE(renderSurface->surfacePropertyChanged());
+ owningLayer->resetAllChangeTrackingForSubtree();
+
+ // Setting the surface properties to the same values again should not be considered "change".
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setClipRect(testRect));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setContentRect(testRect));
+
+ OwnPtr<CCLayerImpl> dummyMask = CCLayerImpl::create(1);
+ WebTransformationMatrix dummyMatrix;
+ dummyMatrix.translate(1.0, 2.0);
+
+ // The rest of the surface properties are either internal and should not cause change,
+ // or they are already accounted for by the owninglayer->layerPropertyChanged().
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setDrawOpacity(0.5));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setDrawTransform(dummyMatrix));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setReplicaDrawTransform(dummyMatrix));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->clearLayerList());
+}
+
+TEST(CCRenderSurfaceTest, sanityCheckSurfaceCreatesCorrectSharedQuadState)
+{
+ // This will fake that we are on the correct thread for testing purposes.
+ DebugScopedSetImplThread setImplThread;
+
+ OwnPtr<CCLayerImpl> rootLayer = CCLayerImpl::create(1);
+
+ OwnPtr<CCLayerImpl> owningLayer = CCLayerImpl::create(2);
+ owningLayer->createRenderSurface();
+ ASSERT_TRUE(owningLayer->renderSurface());
+ owningLayer->setRenderTarget(owningLayer.get());
+ CCRenderSurface* renderSurface = owningLayer->renderSurface();
+
+ rootLayer->addChild(owningLayer.release());
+
+ IntRect contentRect = IntRect(IntPoint::zero(), IntSize(50, 50));
+ IntRect clipRect = IntRect(IntPoint(5, 5), IntSize(40, 40));
+ WebTransformationMatrix origin;
+
+ origin.translate(30, 40);
+
+ renderSurface->setDrawTransform(origin);
+ renderSurface->setContentRect(contentRect);
+ renderSurface->setClipRect(clipRect);
+ renderSurface->setDrawOpacity(1);
+
+ CCQuadList quadList;
+ CCSharedQuadStateList sharedStateList;
+ MockCCQuadCuller mockQuadCuller(quadList, sharedStateList);
+
+ bool forReplica = false;
+ renderSurface->appendQuads(mockQuadCuller, forReplica, 1);
+
+ ASSERT_EQ(1u, sharedStateList.size());
+ CCSharedQuadState* sharedQuadState = sharedStateList[0].get();
+
+ EXPECT_EQ(30, sharedQuadState->quadTransform.m41());
+ EXPECT_EQ(40, sharedQuadState->quadTransform.m42());
+ EXPECT_EQ(contentRect, IntRect(sharedQuadState->visibleContentRect));
+ EXPECT_EQ(1, sharedQuadState->opacity);
+ EXPECT_FALSE(sharedQuadState->opaque);
+}
+
+} // namespace
diff --git a/cc/CCRenderer.h b/cc/CCRenderer.h
new file mode 100644
index 0000000..629f1d7
--- /dev/null
+++ b/cc/CCRenderer.h
@@ -0,0 +1,90 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCRenderer_h
+#define CCRenderer_h
+
+#include "CCLayerTreeHost.h"
+#include "CCRenderPass.h"
+#include "FloatQuad.h"
+#include "IntRect.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class CCScopedTexture;
+class TextureCopier;
+class TextureUploader;
+
+enum TextureUploaderOption { ThrottledUploader, UnthrottledUploader };
+
+class CCRendererClient {
+public:
+ virtual const IntSize& deviceViewportSize() const = 0;
+ virtual const CCLayerTreeSettings& settings() const = 0;
+ virtual void didLoseContext() = 0;
+ virtual void onSwapBuffersComplete() = 0;
+ virtual void releaseContentsTextures() = 0;
+ virtual void setFullRootLayerDamage() = 0;
+ virtual void setMemoryAllocationLimitBytes(size_t) = 0;
+protected:
+ virtual ~CCRendererClient() { }
+};
+
+class CCRenderer {
+ WTF_MAKE_NONCOPYABLE(CCRenderer);
+public:
+ // This enum defines the various resource pools for the CCResourceProvider
+ // where textures get allocated.
+ enum ResourcePool {
+ ImplPool = 1, // This pool is for textures that get allocated on the impl thread (e.g. RenderSurfaces).
+ ContentPool // This pool is for textures that get allocated on the main thread (e.g. tiles).
+ };
+
+ virtual ~CCRenderer() { }
+
+ virtual const RendererCapabilities& capabilities() const = 0;
+
+ const CCLayerTreeSettings& settings() const { return m_client->settings(); }
+
+ const IntSize& viewportSize() { return m_client->deviceViewportSize(); }
+ int viewportWidth() { return viewportSize().width(); }
+ int viewportHeight() { return viewportSize().height(); }
+
+ virtual void viewportChanged() { }
+
+ virtual void decideRenderPassAllocationsForFrame(const CCRenderPassList&) { }
+ virtual bool haveCachedResourcesForRenderPassId(int) const { return false; }
+
+ virtual void drawFrame(const CCRenderPassList&, const CCRenderPassIdHashMap&) = 0;
+
+ // waits for rendering to finish
+ virtual void finish() = 0;
+
+ virtual void doNoOp() { }
+ // puts backbuffer onscreen
+ virtual bool swapBuffers() = 0;
+
+ virtual void getFramebufferPixels(void *pixels, const IntRect&) = 0;
+
+ virtual TextureCopier* textureCopier() const = 0;
+ virtual TextureUploader* textureUploader() const = 0;
+
+ virtual bool isContextLost() { return false; }
+
+ virtual void setVisible(bool) = 0;
+
+protected:
+ explicit CCRenderer(CCRendererClient* client)
+ : m_client(client)
+ {
+ }
+
+ CCRendererClient* m_client;
+};
+
+}
+
+#endif // CCRenderer_h
diff --git a/cc/CCRendererGL.cpp b/cc/CCRendererGL.cpp
new file mode 100644
index 0000000..dc20571
--- /dev/null
+++ b/cc/CCRendererGL.cpp
@@ -0,0 +1,1509 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "CCRendererGL.h"
+
+#include "CCDamageTracker.h"
+#include "CCLayerQuad.h"
+#include "CCMathUtil.h"
+#include "CCProxy.h"
+#include "CCRenderPass.h"
+#include "CCRenderSurfaceFilters.h"
+#include "CCScopedTexture.h"
+#include "CCSettings.h"
+#include "CCSingleThreadProxy.h"
+#include "CCVideoLayerImpl.h"
+#include "Extensions3D.h"
+#include "FloatQuad.h"
+#include "GeometryBinding.h"
+#include "GrTexture.h"
+#include "NotImplemented.h"
+#include "PlatformColor.h"
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "ThrottledTextureUploader.h"
+#include "TraceEvent.h"
+#include "UnthrottledTextureUploader.h"
+#include <public/WebGraphicsContext3D.h>
+#include <public/WebSharedGraphicsContext3D.h>
+#include <public/WebVideoFrame.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/MainThread.h>
+#include <wtf/text/StringHash.h>
+
+using namespace std;
+using WebKit::WebGraphicsContext3D;
+using WebKit::WebGraphicsMemoryAllocation;
+using WebKit::WebSharedGraphicsContext3D;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+namespace {
+
+bool needsIOSurfaceReadbackWorkaround()
+{
+#if OS(DARWIN)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // anonymous namespace
+
+PassOwnPtr<CCRendererGL> CCRendererGL::create(CCRendererClient* client, CCResourceProvider* resourceProvider, TextureUploaderOption textureUploaderSetting)
+{
+ OwnPtr<CCRendererGL> renderer(adoptPtr(new CCRendererGL(client, resourceProvider, textureUploaderSetting)));
+ if (!renderer->initialize())
+ return nullptr;
+
+ return renderer.release();
+}
+
+CCRendererGL::CCRendererGL(CCRendererClient* client,
+ CCResourceProvider* resourceProvider,
+ TextureUploaderOption textureUploaderSetting)
+ : CCDirectRenderer(client, resourceProvider)
+ , m_offscreenFramebufferId(0)
+ , m_sharedGeometryQuad(FloatRect(-0.5f, -0.5f, 1.0f, 1.0f))
+ , m_context(resourceProvider->graphicsContext3D())
+ , m_isViewportChanged(false)
+ , m_isFramebufferDiscarded(false)
+ , m_isUsingBindUniform(false)
+ , m_visible(true)
+ , m_textureUploaderSetting(textureUploaderSetting)
+{
+ ASSERT(m_context);
+}
+
+bool CCRendererGL::initialize()
+{
+ if (!m_context->makeContextCurrent())
+ return false;
+
+ m_context->setContextLostCallback(this);
+ m_context->pushGroupMarkerEXT("CompositorContext");
+
+ WebKit::WebString extensionsWebString = m_context->getString(GraphicsContext3D::EXTENSIONS);
+ String extensionsString(extensionsWebString.data(), extensionsWebString.length());
+ Vector<String> extensionsList;
+ extensionsString.split(' ', extensionsList);
+ HashSet<String> extensions;
+ for (size_t i = 0; i < extensionsList.size(); ++i)
+ extensions.add(extensionsList[i]);
+
+ if (settings().acceleratePainting && extensions.contains("GL_EXT_texture_format_BGRA8888")
+ && extensions.contains("GL_EXT_read_format_bgra"))
+ m_capabilities.usingAcceleratedPainting = true;
+ else
+ m_capabilities.usingAcceleratedPainting = false;
+
+
+ m_capabilities.contextHasCachedFrontBuffer = extensions.contains("GL_CHROMIUM_front_buffer_cached");
+
+ m_capabilities.usingPartialSwap = CCSettings::partialSwapEnabled() && extensions.contains("GL_CHROMIUM_post_sub_buffer");
+
+ // Use the swapBuffers callback only with the threaded proxy.
+ if (CCProxy::hasImplThread())
+ m_capabilities.usingSwapCompleteCallback = extensions.contains("GL_CHROMIUM_swapbuffers_complete_callback");
+ if (m_capabilities.usingSwapCompleteCallback)
+ m_context->setSwapBuffersCompleteCallbackCHROMIUM(this);
+
+ m_capabilities.usingSetVisibility = extensions.contains("GL_CHROMIUM_set_visibility");
+
+ if (extensions.contains("GL_CHROMIUM_iosurface"))
+ ASSERT(extensions.contains("GL_ARB_texture_rectangle"));
+
+ m_capabilities.usingGpuMemoryManager = extensions.contains("GL_CHROMIUM_gpu_memory_manager");
+ if (m_capabilities.usingGpuMemoryManager)
+ m_context->setMemoryAllocationChangedCallbackCHROMIUM(this);
+
+ m_capabilities.usingDiscardFramebuffer = extensions.contains("GL_CHROMIUM_discard_framebuffer");
+
+ m_capabilities.usingEglImage = extensions.contains("GL_OES_EGL_image_external");
+
+ GLC(m_context, m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_capabilities.maxTextureSize));
+ m_capabilities.bestTextureFormat = PlatformColor::bestTextureFormat(m_context, extensions.contains("GL_EXT_texture_format_BGRA8888"));
+
+ m_isUsingBindUniform = extensions.contains("GL_CHROMIUM_bind_uniform_location");
+
+ if (!initializeSharedObjects())
+ return false;
+
+ // Make sure the viewport and context gets initialized, even if it is to zero.
+ viewportChanged();
+ return true;
+}
+
+CCRendererGL::~CCRendererGL()
+{
+ ASSERT(CCProxy::isImplThread());
+ m_context->setSwapBuffersCompleteCallbackCHROMIUM(0);
+ m_context->setMemoryAllocationChangedCallbackCHROMIUM(0);
+ m_context->setContextLostCallback(0);
+ cleanupSharedObjects();
+}
+
+WebGraphicsContext3D* CCRendererGL::context()
+{
+ return m_context;
+}
+
+void CCRendererGL::debugGLCall(WebGraphicsContext3D* context, const char* command, const char* file, int line)
+{
+ unsigned long error = context->getError();
+ if (error != GraphicsContext3D::NO_ERROR)
+ LOG_ERROR("GL command failed: File: %s\n\tLine %d\n\tcommand: %s, error %x\n", file, line, command, static_cast<int>(error));
+}
+
+void CCRendererGL::setVisible(bool visible)
+{
+ if (m_visible == visible)
+ return;
+ m_visible = visible;
+
+ // TODO: Replace setVisibilityCHROMIUM with an extension to explicitly manage front/backbuffers
+ // crbug.com/116049
+ if (m_capabilities.usingSetVisibility)
+ m_context->setVisibilityCHROMIUM(visible);
+}
+
+void CCRendererGL::releaseRenderPassTextures()
+{
+ m_renderPassTextures.clear();
+}
+
+void CCRendererGL::viewportChanged()
+{
+ m_isViewportChanged = true;
+}
+
+void CCRendererGL::clearFramebuffer(DrawingFrame& frame)
+{
+ // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
+ if (frame.currentRenderPass->hasTransparentBackground())
+ GLC(m_context, m_context->clearColor(0, 0, 0, 0));
+ else
+ GLC(m_context, m_context->clearColor(0, 0, 1, 1));
+
+#if defined(NDEBUG)
+ if (frame.currentRenderPass->hasTransparentBackground())
+#endif
+ m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
+}
+
+void CCRendererGL::beginDrawingFrame(DrawingFrame& frame)
+{
+ // FIXME: Remove this once framebuffer is automatically recreated on first use
+ ensureFramebuffer();
+
+ if (viewportSize().isEmpty())
+ return;
+
+ TRACE_EVENT0("cc", "CCRendererGL::drawLayers");
+ if (m_isViewportChanged) {
+ // Only reshape when we know we are going to draw. Otherwise, the reshape
+ // can leave the window at the wrong size if we never draw and the proper
+ // viewport size is never set.
+ m_isViewportChanged = false;
+ m_context->reshape(viewportWidth(), viewportHeight());
+ }
+
+ makeContextCurrent();
+ // Bind the common vertex attributes used for drawing all the layers.
+ m_sharedGeometry->prepareForDraw();
+
+ GLC(m_context, m_context->disable(GraphicsContext3D::DEPTH_TEST));
+ GLC(m_context, m_context->disable(GraphicsContext3D::CULL_FACE));
+ GLC(m_context, m_context->colorMask(true, true, true, true));
+ GLC(m_context, m_context->enable(GraphicsContext3D::BLEND));
+ GLC(m_context, m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA));
+}
+
+void CCRendererGL::doNoOp()
+{
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0));
+ GLC(m_context, m_context->flush());
+}
+
+void CCRendererGL::drawQuad(DrawingFrame& frame, const CCDrawQuad* quad)
+{
+ if (quad->needsBlending())
+ GLC(m_context, m_context->enable(GraphicsContext3D::BLEND));
+ else
+ GLC(m_context, m_context->disable(GraphicsContext3D::BLEND));
+
+ switch (quad->material()) {
+ case CCDrawQuad::Invalid:
+ ASSERT_NOT_REACHED();
+ break;
+ case CCDrawQuad::Checkerboard:
+ drawCheckerboardQuad(frame, CCCheckerboardDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::DebugBorder:
+ drawDebugBorderQuad(frame, CCDebugBorderDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::IOSurfaceContent:
+ drawIOSurfaceQuad(frame, CCIOSurfaceDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::RenderPass:
+ drawRenderPassQuad(frame, CCRenderPassDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::SolidColor:
+ drawSolidColorQuad(frame, CCSolidColorDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::StreamVideoContent:
+ drawStreamVideoQuad(frame, CCStreamVideoDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::TextureContent:
+ drawTextureQuad(frame, CCTextureDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::TiledContent:
+ drawTileQuad(frame, CCTileDrawQuad::materialCast(quad));
+ break;
+ case CCDrawQuad::YUVVideoContent:
+ drawYUVVideoQuad(frame, CCYUVVideoDrawQuad::materialCast(quad));
+ break;
+ }
+}
+
+void CCRendererGL::drawCheckerboardQuad(const DrawingFrame& frame, const CCCheckerboardDrawQuad* quad)
+{
+ const TileCheckerboardProgram* program = tileCheckerboardProgram();
+ ASSERT(program && program->initialized());
+ GLC(context(), context()->useProgram(program->program()));
+
+ IntRect tileRect = quad->quadRect();
+ float texOffsetX = tileRect.x();
+ float texOffsetY = tileRect.y();
+ float texScaleX = tileRect.width();
+ float texScaleY = tileRect.height();
+ GLC(context(), context()->uniform4f(program->fragmentShader().texTransformLocation(), texOffsetX, texOffsetY, texScaleX, texScaleY));
+
+ const int checkerboardWidth = 16;
+ float frequency = 1.0 / checkerboardWidth;
+
+ GLC(context(), context()->uniform1f(program->fragmentShader().frequencyLocation(), frequency));
+
+ setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
+}
+
+void CCRendererGL::drawDebugBorderQuad(const DrawingFrame& frame, const CCDebugBorderDrawQuad* quad)
+{
+ static float glMatrix[16];
+ const SolidColorProgram* program = solidColorProgram();
+ ASSERT(program && program->initialized());
+ GLC(context(), context()->useProgram(program->program()));
+
+ // Use the full quadRect for debug quads to not move the edges based on partial swaps.
+ const IntRect& layerRect = quad->quadRect();
+ WebTransformationMatrix renderMatrix = quad->quadTransform();
+ renderMatrix.translate(0.5 * layerRect.width() + layerRect.x(), 0.5 * layerRect.height() + layerRect.y());
+ renderMatrix.scaleNonUniform(layerRect.width(), layerRect.height());
+ CCRendererGL::toGLMatrix(&glMatrix[0], frame.projectionMatrix * renderMatrix);
+ GLC(context(), context()->uniformMatrix4fv(program->vertexShader().matrixLocation(), 1, false, &glMatrix[0]));
+
+ SkColor color = quad->color();
+ float alpha = SkColorGetA(color) / 255.0;
+
+ GLC(context(), context()->uniform4f(program->fragmentShader().colorLocation(), (SkColorGetR(color) / 255.0) * alpha, (SkColorGetG(color) / 255.0) * alpha, (SkColorGetB(color) / 255.0) * alpha, alpha));
+
+ GLC(context(), context()->lineWidth(quad->width()));
+
+ // The indices for the line are stored in the same array as the triangle indices.
+ GLC(context(), context()->drawElements(GraphicsContext3D::LINE_LOOP, 4, GraphicsContext3D::UNSIGNED_SHORT, 6 * sizeof(unsigned short)));
+}
+
+static inline SkBitmap applyFilters(CCRendererGL* renderer, const WebKit::WebFilterOperations& filters, CCScopedTexture* sourceTexture)
+{
+ if (filters.isEmpty())
+ return SkBitmap();
+
+ WebGraphicsContext3D* filterContext = CCProxy::hasImplThread() ? WebSharedGraphicsContext3D::compositorThreadContext() : WebSharedGraphicsContext3D::mainThreadContext();
+ GrContext* filterGrContext = CCProxy::hasImplThread() ? WebSharedGraphicsContext3D::compositorThreadGrContext() : WebSharedGraphicsContext3D::mainThreadGrContext();
+
+ if (!filterContext || !filterGrContext)
+ return SkBitmap();
+
+ renderer->context()->flush();
+
+ CCResourceProvider::ScopedWriteLockGL lock(renderer->resourceProvider(), sourceTexture->id());
+ SkBitmap source = CCRenderSurfaceFilters::apply(filters, lock.textureId(), sourceTexture->size(), filterContext, filterGrContext);
+ return source;
+}
+
+PassOwnPtr<CCScopedTexture> CCRendererGL::drawBackgroundFilters(DrawingFrame& frame, const CCRenderPassDrawQuad* quad, const WebKit::WebFilterOperations& filters, const WebTransformationMatrix& contentsDeviceTransform)
+{
+ // This method draws a background filter, which applies a filter to any pixels behind the quad and seen through its background.
+ // The algorithm works as follows:
+ // 1. Compute a bounding box around the pixels that will be visible through the quad.
+ // 2. Read the pixels in the bounding box into a buffer R.
+ // 3. Apply the background filter to R, so that it is applied in the pixels' coordinate space.
+ // 4. Apply the quad's inverse transform to map the pixels in R into the quad's content space. This implicitly
+ // clips R by the content bounds of the quad since the destination texture has bounds matching the quad's content.
+ // 5. Draw the background texture for the contents using the same transform as used to draw the contents itself. This is done
+ // without blending to replace the current background pixels with the new filtered background.
+ // 6. Draw the contents of the quad over drop of the new background with blending, as per usual. The filtered background
+ // pixels will show through any non-opaque pixels in this draws.
+ //
+ // Pixel copies in this algorithm occur at steps 2, 3, 4, and 5.
+
+ // FIXME: When this algorithm changes, update CCLayerTreeHost::prioritizeTextures() accordingly.
+
+ if (filters.isEmpty())
+ return nullptr;
+
+ // FIXME: We only allow background filters on an opaque render surface because other surfaces may contain
+ // translucent pixels, and the contents behind those translucent pixels wouldn't have the filter applied.
+ if (frame.currentRenderPass->hasTransparentBackground())
+ return nullptr;
+ ASSERT(!frame.currentTexture);
+
+ // FIXME: Do a single readback for both the surface and replica and cache the filtered results (once filter textures are not reused).
+ IntRect deviceRect = enclosingIntRect(CCMathUtil::mapClippedRect(contentsDeviceTransform, sharedGeometryQuad().boundingBox()));
+
+ int top, right, bottom, left;
+ filters.getOutsets(top, right, bottom, left);
+ deviceRect.move(-left, -top);
+ deviceRect.expand(left + right, top + bottom);
+
+ deviceRect.intersect(frame.currentRenderPass->outputRect());
+
+ OwnPtr<CCScopedTexture> deviceBackgroundTexture = CCScopedTexture::create(m_resourceProvider);
+ if (!getFramebufferTexture(deviceBackgroundTexture.get(), deviceRect))
+ return nullptr;
+
+ SkBitmap filteredDeviceBackground = applyFilters(this, filters, deviceBackgroundTexture.get());
+ if (!filteredDeviceBackground.getTexture())
+ return nullptr;
+
+ GrTexture* texture = reinterpret_cast<GrTexture*>(filteredDeviceBackground.getTexture());
+ int filteredDeviceBackgroundTextureId = texture->getTextureHandle();
+
+ OwnPtr<CCScopedTexture> backgroundTexture = CCScopedTexture::create(m_resourceProvider);
+ if (!backgroundTexture->allocate(CCRenderer::ImplPool, quad->quadRect().size(), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageFramebuffer))
+ return nullptr;
+
+ const CCRenderPass* targetRenderPass = frame.currentRenderPass;
+ bool usingBackgroundTexture = useScopedTexture(frame, backgroundTexture.get(), quad->quadRect());
+
+ if (usingBackgroundTexture) {
+ // Copy the readback pixels from device to the background texture for the surface.
+ WebTransformationMatrix deviceToFramebufferTransform;
+ deviceToFramebufferTransform.translate(quad->quadRect().width() / 2.0, quad->quadRect().height() / 2.0);
+ deviceToFramebufferTransform.scale3d(quad->quadRect().width(), quad->quadRect().height(), 1);
+ deviceToFramebufferTransform.multiply(contentsDeviceTransform.inverse());
+ copyTextureToFramebuffer(frame, filteredDeviceBackgroundTextureId, deviceRect, deviceToFramebufferTransform);
+ }
+
+ useRenderPass(frame, targetRenderPass);
+
+ if (!usingBackgroundTexture)
+ return nullptr;
+ return backgroundTexture.release();
+}
+
+void CCRendererGL::drawRenderPassQuad(DrawingFrame& frame, const CCRenderPassDrawQuad* quad)
+{
+ CachedTexture* contentsTexture = m_renderPassTextures.get(quad->renderPassId());
+ if (!contentsTexture || !contentsTexture->id())
+ return;
+
+ const CCRenderPass* renderPass = frame.renderPassesById->get(quad->renderPassId());
+ ASSERT(renderPass);
+ if (!renderPass)
+ return;
+
+ WebTransformationMatrix renderMatrix = quad->quadTransform();
+ renderMatrix.translate(0.5 * quad->quadRect().width() + quad->quadRect().x(), 0.5 * quad->quadRect().height() + quad->quadRect().y());
+ WebTransformationMatrix deviceMatrix = renderMatrix;
+ deviceMatrix.scaleNonUniform(quad->quadRect().width(), quad->quadRect().height());
+ WebTransformationMatrix contentsDeviceTransform = WebTransformationMatrix(frame.windowMatrix * frame.projectionMatrix * deviceMatrix).to2dTransform();
+
+ // Can only draw surface if device matrix is invertible.
+ if (!contentsDeviceTransform.isInvertible())
+ return;
+
+ OwnPtr<CCScopedTexture> backgroundTexture = drawBackgroundFilters(frame, quad, renderPass->backgroundFilters(), contentsDeviceTransform);
+
+ // FIXME: Cache this value so that we don't have to do it for both the surface and its replica.
+ // Apply filters to the contents texture.
+ SkBitmap filterBitmap = applyFilters(this, renderPass->filters(), contentsTexture);
+ OwnPtr<CCResourceProvider::ScopedReadLockGL> contentsResourceLock;
+ unsigned contentsTextureId = 0;
+ if (filterBitmap.getTexture()) {
+ GrTexture* texture = reinterpret_cast<GrTexture*>(filterBitmap.getTexture());
+ contentsTextureId = texture->getTextureHandle();
+ } else {
+ contentsResourceLock = adoptPtr(new CCResourceProvider::ScopedReadLockGL(m_resourceProvider, contentsTexture->id()));
+ contentsTextureId = contentsResourceLock->textureId();
+ }
+
+ // Draw the background texture if there is one.
+ if (backgroundTexture) {
+ ASSERT(backgroundTexture->size() == quad->quadRect().size());
+ CCResourceProvider::ScopedReadLockGL lock(m_resourceProvider, backgroundTexture->id());
+ copyTextureToFramebuffer(frame, lock.textureId(), quad->quadRect(), quad->quadTransform());
+ }
+
+ bool clipped = false;
+ FloatQuad deviceQuad = CCMathUtil::mapQuad(contentsDeviceTransform, sharedGeometryQuad(), clipped);
+ ASSERT(!clipped);
+ CCLayerQuad deviceLayerBounds = CCLayerQuad(FloatQuad(deviceQuad.boundingBox()));
+ CCLayerQuad deviceLayerEdges = CCLayerQuad(deviceQuad);
+
+ // Use anti-aliasing programs only when necessary.
+ bool useAA = (!deviceQuad.isRectilinear() || !deviceQuad.boundingBox().isExpressibleAsIntRect());
+ if (useAA) {
+ deviceLayerBounds.inflateAntiAliasingDistance();
+ deviceLayerEdges.inflateAntiAliasingDistance();
+ }
+
+ OwnPtr<CCResourceProvider::ScopedReadLockGL> maskResourceLock;
+ unsigned maskTextureId = 0;
+ if (quad->maskResourceId()) {
+ maskResourceLock = adoptPtr(new CCResourceProvider::ScopedReadLockGL(m_resourceProvider, quad->maskResourceId()));
+ maskTextureId = maskResourceLock->textureId();
+ }
+
+ // FIXME: use the backgroundTexture and blend the background in with this draw instead of having a separate copy of the background texture.
+
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ context()->bindTexture(GraphicsContext3D::TEXTURE_2D, contentsTextureId);
+
+ int shaderQuadLocation = -1;
+ int shaderEdgeLocation = -1;
+ int shaderMaskSamplerLocation = -1;
+ int shaderMaskTexCoordScaleLocation = -1;
+ int shaderMaskTexCoordOffsetLocation = -1;
+ int shaderMatrixLocation = -1;
+ int shaderAlphaLocation = -1;
+ if (useAA && maskTextureId) {
+ const RenderPassMaskProgramAA* program = renderPassMaskProgramAA();
+ GLC(context(), context()->useProgram(program->program()));
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+
+ shaderQuadLocation = program->vertexShader().pointLocation();
+ shaderEdgeLocation = program->fragmentShader().edgeLocation();
+ shaderMaskSamplerLocation = program->fragmentShader().maskSamplerLocation();
+ shaderMaskTexCoordScaleLocation = program->fragmentShader().maskTexCoordScaleLocation();
+ shaderMaskTexCoordOffsetLocation = program->fragmentShader().maskTexCoordOffsetLocation();
+ shaderMatrixLocation = program->vertexShader().matrixLocation();
+ shaderAlphaLocation = program->fragmentShader().alphaLocation();
+ } else if (!useAA && maskTextureId) {
+ const RenderPassMaskProgram* program = renderPassMaskProgram();
+ GLC(context(), context()->useProgram(program->program()));
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+
+ shaderMaskSamplerLocation = program->fragmentShader().maskSamplerLocation();
+ shaderMaskTexCoordScaleLocation = program->fragmentShader().maskTexCoordScaleLocation();
+ shaderMaskTexCoordOffsetLocation = program->fragmentShader().maskTexCoordOffsetLocation();
+ shaderMatrixLocation = program->vertexShader().matrixLocation();
+ shaderAlphaLocation = program->fragmentShader().alphaLocation();
+ } else if (useAA && !maskTextureId) {
+ const RenderPassProgramAA* program = renderPassProgramAA();
+ GLC(context(), context()->useProgram(program->program()));
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+
+ shaderQuadLocation = program->vertexShader().pointLocation();
+ shaderEdgeLocation = program->fragmentShader().edgeLocation();
+ shaderMatrixLocation = program->vertexShader().matrixLocation();
+ shaderAlphaLocation = program->fragmentShader().alphaLocation();
+ } else {
+ const RenderPassProgram* program = renderPassProgram();
+ GLC(context(), context()->useProgram(program->program()));
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+
+ shaderMatrixLocation = program->vertexShader().matrixLocation();
+ shaderAlphaLocation = program->fragmentShader().alphaLocation();
+ }
+
+ if (shaderMaskSamplerLocation != -1) {
+ ASSERT(shaderMaskTexCoordScaleLocation != 1);
+ ASSERT(shaderMaskTexCoordOffsetLocation != 1);
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE1));
+ GLC(context(), context()->uniform1i(shaderMaskSamplerLocation, 1));
+ GLC(context(), context()->uniform2f(shaderMaskTexCoordScaleLocation, quad->maskTexCoordScaleX(), quad->maskTexCoordScaleY()));
+ GLC(context(), context()->uniform2f(shaderMaskTexCoordOffsetLocation, quad->maskTexCoordOffsetX(), quad->maskTexCoordOffsetY()));
+ context()->bindTexture(GraphicsContext3D::TEXTURE_2D, maskTextureId);
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ }
+
+ if (shaderEdgeLocation != -1) {
+ float edge[24];
+ deviceLayerEdges.toFloatArray(edge);
+ deviceLayerBounds.toFloatArray(&edge[12]);
+ GLC(context(), context()->uniform3fv(shaderEdgeLocation, 8, edge));
+ }
+
+ // Map device space quad to surface space. contentsDeviceTransform has no 3d component since it was generated with to2dTransform() so we don't need to project.
+ FloatQuad surfaceQuad = CCMathUtil::mapQuad(contentsDeviceTransform.inverse(), deviceLayerEdges.floatQuad(), clipped);
+ ASSERT(!clipped);
+
+ setShaderOpacity(quad->opacity(), shaderAlphaLocation);
+ setShaderFloatQuad(surfaceQuad, shaderQuadLocation);
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), shaderMatrixLocation);
+}
+
+void CCRendererGL::drawSolidColorQuad(const DrawingFrame& frame, const CCSolidColorDrawQuad* quad)
+{
+ const SolidColorProgram* program = solidColorProgram();
+ GLC(context(), context()->useProgram(program->program()));
+
+ SkColor color = quad->color();
+ float opacity = quad->opacity();
+ float alpha = (SkColorGetA(color) / 255.0) * opacity;
+
+ GLC(context(), context()->uniform4f(program->fragmentShader().colorLocation(), (SkColorGetR(color) / 255.0) * alpha, (SkColorGetG(color) / 255.0) * alpha, (SkColorGetB(color) / 255.0) * alpha, alpha));
+
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
+}
+
+struct TileProgramUniforms {
+ unsigned program;
+ unsigned samplerLocation;
+ unsigned vertexTexTransformLocation;
+ unsigned fragmentTexTransformLocation;
+ unsigned edgeLocation;
+ unsigned matrixLocation;
+ unsigned alphaLocation;
+ unsigned pointLocation;
+};
+
+template<class T>
+static void tileUniformLocation(T program, TileProgramUniforms& uniforms)
+{
+ uniforms.program = program->program();
+ uniforms.vertexTexTransformLocation = program->vertexShader().vertexTexTransformLocation();
+ uniforms.matrixLocation = program->vertexShader().matrixLocation();
+ uniforms.pointLocation = program->vertexShader().pointLocation();
+
+ uniforms.samplerLocation = program->fragmentShader().samplerLocation();
+ uniforms.alphaLocation = program->fragmentShader().alphaLocation();
+ uniforms.fragmentTexTransformLocation = program->fragmentShader().fragmentTexTransformLocation();
+ uniforms.edgeLocation = program->fragmentShader().edgeLocation();
+}
+
+void CCRendererGL::drawTileQuad(const DrawingFrame& frame, const CCTileDrawQuad* quad)
+{
+ IntRect tileRect = quad->quadVisibleRect();
+
+ FloatRect clampRect(tileRect);
+ // Clamp texture coordinates to avoid sampling outside the layer
+ // by deflating the tile region half a texel or half a texel
+ // minus epsilon for one pixel layers. The resulting clamp region
+ // is mapped to the unit square by the vertex shader and mapped
+ // back to normalized texture coordinates by the fragment shader
+ // after being clamped to 0-1 range.
+ const float epsilon = 1 / 1024.0f;
+ float clampX = min(0.5, clampRect.width() / 2.0 - epsilon);
+ float clampY = min(0.5, clampRect.height() / 2.0 - epsilon);
+ clampRect.inflateX(-clampX);
+ clampRect.inflateY(-clampY);
+ FloatSize clampOffset = clampRect.minXMinYCorner() - FloatRect(tileRect).minXMinYCorner();
+
+ FloatPoint textureOffset = quad->textureOffset() + clampOffset +
+ IntPoint(tileRect.location() - quad->quadRect().location());
+
+ // Map clamping rectangle to unit square.
+ float vertexTexTranslateX = -clampRect.x() / clampRect.width();
+ float vertexTexTranslateY = -clampRect.y() / clampRect.height();
+ float vertexTexScaleX = tileRect.width() / clampRect.width();
+ float vertexTexScaleY = tileRect.height() / clampRect.height();
+
+ // Map to normalized texture coordinates.
+ const IntSize& textureSize = quad->textureSize();
+ float fragmentTexTranslateX = textureOffset.x() / textureSize.width();
+ float fragmentTexTranslateY = textureOffset.y() / textureSize.height();
+ float fragmentTexScaleX = clampRect.width() / textureSize.width();
+ float fragmentTexScaleY = clampRect.height() / textureSize.height();
+
+
+ FloatQuad localQuad;
+ WebTransformationMatrix deviceTransform = WebTransformationMatrix(frame.windowMatrix * frame.projectionMatrix * quad->quadTransform()).to2dTransform();
+ if (!deviceTransform.isInvertible())
+ return;
+
+ bool clipped = false;
+ FloatQuad deviceLayerQuad = CCMathUtil::mapQuad(deviceTransform, FloatQuad(quad->visibleContentRect()), clipped);
+ ASSERT(!clipped);
+
+ TileProgramUniforms uniforms;
+ // For now, we simply skip anti-aliasing with the quad is clipped. This only happens
+ // on perspective transformed layers that go partially behind the camera.
+ if (quad->isAntialiased() && !clipped) {
+ if (quad->swizzleContents())
+ tileUniformLocation(tileProgramSwizzleAA(), uniforms);
+ else
+ tileUniformLocation(tileProgramAA(), uniforms);
+ } else {
+ if (quad->needsBlending()) {
+ if (quad->swizzleContents())
+ tileUniformLocation(tileProgramSwizzle(), uniforms);
+ else
+ tileUniformLocation(tileProgram(), uniforms);
+ } else {
+ if (quad->swizzleContents())
+ tileUniformLocation(tileProgramSwizzleOpaque(), uniforms);
+ else
+ tileUniformLocation(tileProgramOpaque(), uniforms);
+ }
+ }
+
+ GLC(context(), context()->useProgram(uniforms.program));
+ GLC(context(), context()->uniform1i(uniforms.samplerLocation, 0));
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ CCResourceProvider::ScopedReadLockGL quadResourceLock(m_resourceProvider, quad->resourceId());
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, quadResourceLock.textureId()));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, quad->textureFilter()));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, quad->textureFilter()));
+
+ bool useAA = !clipped && quad->isAntialiased();
+ if (useAA) {
+ CCLayerQuad deviceLayerBounds = CCLayerQuad(FloatQuad(deviceLayerQuad.boundingBox()));
+ deviceLayerBounds.inflateAntiAliasingDistance();
+
+ CCLayerQuad deviceLayerEdges = CCLayerQuad(deviceLayerQuad);
+ deviceLayerEdges.inflateAntiAliasingDistance();
+
+ float edge[24];
+ deviceLayerEdges.toFloatArray(edge);
+ deviceLayerBounds.toFloatArray(&edge[12]);
+ GLC(context(), context()->uniform3fv(uniforms.edgeLocation, 8, edge));
+
+ GLC(context(), context()->uniform4f(uniforms.vertexTexTransformLocation, vertexTexTranslateX, vertexTexTranslateY, vertexTexScaleX, vertexTexScaleY));
+ GLC(context(), context()->uniform4f(uniforms.fragmentTexTransformLocation, fragmentTexTranslateX, fragmentTexTranslateY, fragmentTexScaleX, fragmentTexScaleY));
+
+ FloatPoint bottomRight(tileRect.maxX(), tileRect.maxY());
+ FloatPoint bottomLeft(tileRect.x(), tileRect.maxY());
+ FloatPoint topLeft(tileRect.x(), tileRect.y());
+ FloatPoint topRight(tileRect.maxX(), tileRect.y());
+
+ // Map points to device space.
+ bottomRight = CCMathUtil::mapPoint(deviceTransform, bottomRight, clipped);
+ ASSERT(!clipped);
+ bottomLeft = CCMathUtil::mapPoint(deviceTransform, bottomLeft, clipped);
+ ASSERT(!clipped);
+ topLeft = CCMathUtil::mapPoint(deviceTransform, topLeft, clipped);
+ ASSERT(!clipped);
+ topRight = CCMathUtil::mapPoint(deviceTransform, topRight, clipped);
+ ASSERT(!clipped);
+
+ CCLayerQuad::Edge bottomEdge(bottomRight, bottomLeft);
+ CCLayerQuad::Edge leftEdge(bottomLeft, topLeft);
+ CCLayerQuad::Edge topEdge(topLeft, topRight);
+ CCLayerQuad::Edge rightEdge(topRight, bottomRight);
+
+ // Only apply anti-aliasing to edges not clipped by culling or scissoring.
+ if (quad->topEdgeAA() && tileRect.y() == quad->quadRect().y())
+ topEdge = deviceLayerEdges.top();
+ if (quad->leftEdgeAA() && tileRect.x() == quad->quadRect().x())
+ leftEdge = deviceLayerEdges.left();
+ if (quad->rightEdgeAA() && tileRect.maxX() == quad->quadRect().maxX())
+ rightEdge = deviceLayerEdges.right();
+ if (quad->bottomEdgeAA() && tileRect.maxY() == quad->quadRect().maxY())
+ bottomEdge = deviceLayerEdges.bottom();
+
+ float sign = FloatQuad(tileRect).isCounterclockwise() ? -1 : 1;
+ bottomEdge.scale(sign);
+ leftEdge.scale(sign);
+ topEdge.scale(sign);
+ rightEdge.scale(sign);
+
+ // Create device space quad.
+ CCLayerQuad deviceQuad(leftEdge, topEdge, rightEdge, bottomEdge);
+
+ // Map device space quad to local space. contentsDeviceTransform has no 3d component since it was generated with to2dTransform() so we don't need to project.
+ WebTransformationMatrix inverseDeviceTransform = deviceTransform.inverse();
+ localQuad = CCMathUtil::mapQuad(inverseDeviceTransform, deviceQuad.floatQuad(), clipped);
+
+ // We should not ASSERT(!clipped) here, because anti-aliasing inflation may cause deviceQuad to become
+ // clipped. To our knowledge this scenario does not need to be handled differently than the unclipped case.
+ } else {
+ // Move fragment shader transform to vertex shader. We can do this while
+ // still producing correct results as fragmentTexTransformLocation
+ // should always be non-negative when tiles are transformed in a way
+ // that could result in sampling outside the layer.
+ vertexTexScaleX *= fragmentTexScaleX;
+ vertexTexScaleY *= fragmentTexScaleY;
+ vertexTexTranslateX *= fragmentTexScaleX;
+ vertexTexTranslateY *= fragmentTexScaleY;
+ vertexTexTranslateX += fragmentTexTranslateX;
+ vertexTexTranslateY += fragmentTexTranslateY;
+
+ GLC(context(), context()->uniform4f(uniforms.vertexTexTransformLocation, vertexTexTranslateX, vertexTexTranslateY, vertexTexScaleX, vertexTexScaleY));
+
+ localQuad = FloatRect(tileRect);
+ }
+
+ // Normalize to tileRect.
+ localQuad.scale(1.0f / tileRect.width(), 1.0f / tileRect.height());
+
+ setShaderOpacity(quad->opacity(), uniforms.alphaLocation);
+ setShaderFloatQuad(localQuad, uniforms.pointLocation);
+
+ // The tile quad shader behaves differently compared to all other shaders.
+ // The transform and vertex data are used to figure out the extents that the
+ // un-antialiased quad should have and which vertex this is and the float
+ // quad passed in via uniform is the actual geometry that gets used to draw
+ // it. This is why this centered rect is used and not the original quadRect.
+ FloatRect centeredRect(FloatPoint(-0.5 * tileRect.width(), -0.5 * tileRect.height()), tileRect.size());
+ drawQuadGeometry(frame, quad->quadTransform(), centeredRect, uniforms.matrixLocation);
+}
+
+void CCRendererGL::drawYUVVideoQuad(const DrawingFrame& frame, const CCYUVVideoDrawQuad* quad)
+{
+ const VideoYUVProgram* program = videoYUVProgram();
+ ASSERT(program && program->initialized());
+
+ const CCVideoLayerImpl::FramePlane& yPlane = quad->yPlane();
+ const CCVideoLayerImpl::FramePlane& uPlane = quad->uPlane();
+ const CCVideoLayerImpl::FramePlane& vPlane = quad->vPlane();
+
+ CCResourceProvider::ScopedReadLockGL yPlaneLock(m_resourceProvider, yPlane.resourceId);
+ CCResourceProvider::ScopedReadLockGL uPlaneLock(m_resourceProvider, uPlane.resourceId);
+ CCResourceProvider::ScopedReadLockGL vPlaneLock(m_resourceProvider, vPlane.resourceId);
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE1));
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, yPlaneLock.textureId()));
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE2));
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, uPlaneLock.textureId()));
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE3));
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, vPlaneLock.textureId()));
+
+ GLC(context(), context()->useProgram(program->program()));
+
+ float yWidthScaleFactor = static_cast<float>(yPlane.visibleSize.width()) / yPlane.size.width();
+ // Arbitrarily take the u sizes because u and v dimensions are identical.
+ float uvWidthScaleFactor = static_cast<float>(uPlane.visibleSize.width()) / uPlane.size.width();
+ GLC(context(), context()->uniform1f(program->vertexShader().yWidthScaleFactorLocation(), yWidthScaleFactor));
+ GLC(context(), context()->uniform1f(program->vertexShader().uvWidthScaleFactorLocation(), uvWidthScaleFactor));
+
+ GLC(context(), context()->uniform1i(program->fragmentShader().yTextureLocation(), 1));
+ GLC(context(), context()->uniform1i(program->fragmentShader().uTextureLocation(), 2));
+ GLC(context(), context()->uniform1i(program->fragmentShader().vTextureLocation(), 3));
+
+ // These values are magic numbers that are used in the transformation from YUV to RGB color values.
+ // They are taken from the following webpage: http://www.fourcc.org/fccyvrgb.php
+ float yuv2RGB[9] = {
+ 1.164f, 1.164f, 1.164f,
+ 0.f, -.391f, 2.018f,
+ 1.596f, -.813f, 0.f,
+ };
+ GLC(context(), context()->uniformMatrix3fv(program->fragmentShader().ccMatrixLocation(), 1, 0, yuv2RGB));
+
+ // These values map to 16, 128, and 128 respectively, and are computed
+ // as a fraction over 256 (e.g. 16 / 256 = 0.0625).
+ // They are used in the YUV to RGBA conversion formula:
+ // Y - 16 : Gives 16 values of head and footroom for overshooting
+ // U - 128 : Turns unsigned U into signed U [-128,127]
+ // V - 128 : Turns unsigned V into signed V [-128,127]
+ float yuvAdjust[3] = {
+ -0.0625f,
+ -0.5f,
+ -0.5f,
+ };
+ GLC(context(), context()->uniform3fv(program->fragmentShader().yuvAdjLocation(), 1, yuvAdjust));
+
+ setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
+
+ // Reset active texture back to texture 0.
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+}
+
+void CCRendererGL::drawStreamVideoQuad(const DrawingFrame& frame, const CCStreamVideoDrawQuad* quad)
+{
+ static float glMatrix[16];
+
+ ASSERT(m_capabilities.usingEglImage);
+
+ const VideoStreamTextureProgram* program = videoStreamTextureProgram();
+ GLC(context(), context()->useProgram(program->program()));
+
+ toGLMatrix(&glMatrix[0], quad->matrix());
+ GLC(context(), context()->uniformMatrix4fv(program->vertexShader().texMatrixLocation(), 1, false, glMatrix));
+
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ GLC(context(), context()->bindTexture(Extensions3DChromium::GL_TEXTURE_EXTERNAL_OES, quad->textureId()));
+
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+
+ setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
+}
+
+struct TextureProgramBinding {
+ template<class Program> void set(Program* program)
+ {
+ ASSERT(program && program->initialized());
+ programId = program->program();
+ samplerLocation = program->fragmentShader().samplerLocation();
+ matrixLocation = program->vertexShader().matrixLocation();
+ alphaLocation = program->fragmentShader().alphaLocation();
+ }
+ int programId;
+ int samplerLocation;
+ int matrixLocation;
+ int alphaLocation;
+};
+
+struct TexTransformTextureProgramBinding : TextureProgramBinding {
+ template<class Program> void set(Program* program)
+ {
+ TextureProgramBinding::set(program);
+ texTransformLocation = program->vertexShader().texTransformLocation();
+ }
+ int texTransformLocation;
+};
+
+void CCRendererGL::drawTextureQuad(const DrawingFrame& frame, const CCTextureDrawQuad* quad)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ TexTransformTextureProgramBinding binding;
+ if (quad->flipped())
+ binding.set(textureProgramFlip());
+ else
+ binding.set(textureProgram());
+ GLC(context(), context()->useProgram(binding.programId));
+ GLC(context(), context()->uniform1i(binding.samplerLocation, 0));
+ const FloatRect& uvRect = quad->uvRect();
+ GLC(context(), context()->uniform4f(binding.texTransformLocation, uvRect.x(), uvRect.y(), uvRect.width(), uvRect.height()));
+
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ CCResourceProvider::ScopedReadLockGL quadResourceLock(m_resourceProvider, quad->resourceId());
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, quadResourceLock.textureId()));
+
+ // FIXME: setting the texture parameters every time is redundant. Move this code somewhere
+ // where it will only happen once per texture.
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
+
+ if (!quad->premultipliedAlpha()) {
+ // As it turns out, the premultiplied alpha blending function (ONE, ONE_MINUS_SRC_ALPHA)
+ // will never cause the alpha channel to be set to anything less than 1.0 if it is
+ // initialized to that value! Therefore, premultipliedAlpha being false is the first
+ // situation we can generally see an alpha channel less than 1.0 coming out of the
+ // compositor. This is causing platform differences in some layout tests (see
+ // https://bugs.webkit.org/show_bug.cgi?id=82412), so in this situation, use a separate
+ // blend function for the alpha channel to avoid modifying it. Don't use colorMask for this
+ // as it has performance implications on some platforms.
+ GLC(context(), context()->blendFuncSeparate(GraphicsContext3D::SRC_ALPHA, GraphicsContext3D::ONE_MINUS_SRC_ALPHA, GraphicsContext3D::ZERO, GraphicsContext3D::ONE));
+ }
+
+ setShaderOpacity(quad->opacity(), binding.alphaLocation);
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), binding.matrixLocation);
+
+ if (!quad->premultipliedAlpha())
+ GLC(m_context, m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA));
+}
+
+void CCRendererGL::drawIOSurfaceQuad(const DrawingFrame& frame, const CCIOSurfaceDrawQuad* quad)
+{
+ ASSERT(CCProxy::isImplThread());
+ TexTransformTextureProgramBinding binding;
+ binding.set(textureIOSurfaceProgram());
+
+ GLC(context(), context()->useProgram(binding.programId));
+ GLC(context(), context()->uniform1i(binding.samplerLocation, 0));
+ if (quad->orientation() == CCIOSurfaceDrawQuad::Flipped)
+ GLC(context(), context()->uniform4f(binding.texTransformLocation, 0, quad->ioSurfaceSize().height(), quad->ioSurfaceSize().width(), quad->ioSurfaceSize().height() * -1.0));
+ else
+ GLC(context(), context()->uniform4f(binding.texTransformLocation, 0, 0, quad->ioSurfaceSize().width(), quad->ioSurfaceSize().height()));
+
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ GLC(context(), context()->bindTexture(Extensions3D::TEXTURE_RECTANGLE_ARB, quad->ioSurfaceTextureId()));
+
+ setShaderOpacity(quad->opacity(), binding.alphaLocation);
+ drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), binding.matrixLocation);
+
+ GLC(context(), context()->bindTexture(Extensions3D::TEXTURE_RECTANGLE_ARB, 0));
+}
+
+void CCRendererGL::finishDrawingFrame(DrawingFrame& frame)
+{
+ m_currentFramebufferLock.clear();
+ m_swapBufferRect.unite(enclosingIntRect(frame.rootDamageRect));
+
+ GLC(m_context, m_context->disable(GraphicsContext3D::SCISSOR_TEST));
+ GLC(m_context, m_context->disable(GraphicsContext3D::BLEND));
+}
+
+void CCRendererGL::toGLMatrix(float* flattened, const WebTransformationMatrix& m)
+{
+ flattened[0] = m.m11();
+ flattened[1] = m.m12();
+ flattened[2] = m.m13();
+ flattened[3] = m.m14();
+ flattened[4] = m.m21();
+ flattened[5] = m.m22();
+ flattened[6] = m.m23();
+ flattened[7] = m.m24();
+ flattened[8] = m.m31();
+ flattened[9] = m.m32();
+ flattened[10] = m.m33();
+ flattened[11] = m.m34();
+ flattened[12] = m.m41();
+ flattened[13] = m.m42();
+ flattened[14] = m.m43();
+ flattened[15] = m.m44();
+}
+
+void CCRendererGL::setShaderFloatQuad(const FloatQuad& quad, int quadLocation)
+{
+ if (quadLocation == -1)
+ return;
+
+ float point[8];
+ point[0] = quad.p1().x();
+ point[1] = quad.p1().y();
+ point[2] = quad.p2().x();
+ point[3] = quad.p2().y();
+ point[4] = quad.p3().x();
+ point[5] = quad.p3().y();
+ point[6] = quad.p4().x();
+ point[7] = quad.p4().y();
+ GLC(m_context, m_context->uniform2fv(quadLocation, 4, point));
+}
+
+void CCRendererGL::setShaderOpacity(float opacity, int alphaLocation)
+{
+ if (alphaLocation != -1)
+ GLC(m_context, m_context->uniform1f(alphaLocation, opacity));
+}
+
+void CCRendererGL::drawQuadGeometry(const DrawingFrame& frame, const WebKit::WebTransformationMatrix& drawTransform, const FloatRect& quadRect, int matrixLocation)
+{
+ WebTransformationMatrix quadRectMatrix;
+ quadRectTransform(&quadRectMatrix, drawTransform, quadRect);
+ static float glMatrix[16];
+ toGLMatrix(&glMatrix[0], frame.projectionMatrix * quadRectMatrix);
+ GLC(m_context, m_context->uniformMatrix4fv(matrixLocation, 1, false, &glMatrix[0]));
+
+ GLC(m_context, m_context->drawElements(GraphicsContext3D::TRIANGLES, 6, GraphicsContext3D::UNSIGNED_SHORT, 0));
+}
+
+void CCRendererGL::copyTextureToFramebuffer(const DrawingFrame& frame, int textureId, const IntRect& rect, const WebTransformationMatrix& drawMatrix)
+{
+ const RenderPassProgram* program = renderPassProgram();
+
+ GLC(context(), context()->activeTexture(GraphicsContext3D::TEXTURE0));
+ GLC(context(), context()->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
+ GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
+
+ GLC(context(), context()->useProgram(program->program()));
+ GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
+ setShaderOpacity(1, program->fragmentShader().alphaLocation());
+ drawQuadGeometry(frame, drawMatrix, rect, program->vertexShader().matrixLocation());
+}
+
+void CCRendererGL::finish()
+{
+ TRACE_EVENT0("cc", "CCRendererGL::finish");
+ m_context->finish();
+}
+
+bool CCRendererGL::swapBuffers()
+{
+ ASSERT(m_visible);
+ ASSERT(!m_isFramebufferDiscarded);
+
+ TRACE_EVENT0("cc", "CCRendererGL::swapBuffers");
+ // We're done! Time to swapbuffers!
+
+ if (m_capabilities.usingPartialSwap) {
+ // If supported, we can save significant bandwidth by only swapping the damaged/scissored region (clamped to the viewport)
+ m_swapBufferRect.intersect(IntRect(IntPoint(), viewportSize()));
+ int flippedYPosOfRectBottom = viewportHeight() - m_swapBufferRect.y() - m_swapBufferRect.height();
+ m_context->postSubBufferCHROMIUM(m_swapBufferRect.x(), flippedYPosOfRectBottom, m_swapBufferRect.width(), m_swapBufferRect.height());
+ } else {
+ // Note that currently this has the same effect as swapBuffers; we should
+ // consider exposing a different entry point on WebGraphicsContext3D.
+ m_context->prepareTexture();
+ }
+
+ m_swapBufferRect = IntRect();
+
+ return true;
+}
+
+void CCRendererGL::onSwapBuffersComplete()
+{
+ m_client->onSwapBuffersComplete();
+}
+
+void CCRendererGL::onMemoryAllocationChanged(WebGraphicsMemoryAllocation allocation)
+{
+ // FIXME: This is called on the main thread in single threaded mode, but we expect it on the impl thread.
+ if (!CCProxy::hasImplThread()) {
+ ASSERT(CCProxy::isMainThread());
+ DebugScopedSetImplThread impl;
+ onMemoryAllocationChangedOnImplThread(allocation);
+ } else {
+ ASSERT(CCProxy::isImplThread());
+ onMemoryAllocationChangedOnImplThread(allocation);
+ }
+}
+
+void CCRendererGL::onMemoryAllocationChangedOnImplThread(WebKit::WebGraphicsMemoryAllocation allocation)
+{
+ if (m_visible && !allocation.gpuResourceSizeInBytes)
+ return;
+
+ if (!allocation.suggestHaveBackbuffer && !m_visible)
+ discardFramebuffer();
+
+ if (!allocation.gpuResourceSizeInBytes) {
+ releaseRenderPassTextures();
+ m_client->releaseContentsTextures();
+ GLC(m_context, m_context->flush());
+ } else
+ m_client->setMemoryAllocationLimitBytes(allocation.gpuResourceSizeInBytes);
+}
+
+void CCRendererGL::discardFramebuffer()
+{
+ if (m_isFramebufferDiscarded)
+ return;
+
+ if (!m_capabilities.usingDiscardFramebuffer)
+ return;
+
+ // FIXME: Update attachments argument to appropriate values once they are no longer ignored.
+ m_context->discardFramebufferEXT(GraphicsContext3D::TEXTURE_2D, 0, 0);
+ m_isFramebufferDiscarded = true;
+
+ // Damage tracker needs a full reset every time framebuffer is discarded.
+ m_client->setFullRootLayerDamage();
+}
+
+void CCRendererGL::ensureFramebuffer()
+{
+ if (!m_isFramebufferDiscarded)
+ return;
+
+ if (!m_capabilities.usingDiscardFramebuffer)
+ return;
+
+ m_context->ensureFramebufferCHROMIUM();
+ m_isFramebufferDiscarded = false;
+}
+
+void CCRendererGL::onContextLost()
+{
+ m_client->didLoseContext();
+}
+
+
+void CCRendererGL::getFramebufferPixels(void *pixels, const IntRect& rect)
+{
+ ASSERT(rect.maxX() <= viewportWidth() && rect.maxY() <= viewportHeight());
+
+ if (!pixels)
+ return;
+
+ makeContextCurrent();
+
+ bool doWorkaround = needsIOSurfaceReadbackWorkaround();
+
+ Platform3DObject temporaryTexture = 0;
+ Platform3DObject temporaryFBO = 0;
+
+ if (doWorkaround) {
+ // On Mac OS X, calling glReadPixels against an FBO whose color attachment is an
+ // IOSurface-backed texture causes corruption of future glReadPixels calls, even those on
+ // different OpenGL contexts. It is believed that this is the root cause of top crasher
+ // http://crbug.com/99393. <rdar://problem/10949687>
+
+ temporaryTexture = m_context->createTexture();
+ GLC(m_context, m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, temporaryTexture));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
+ // Copy the contents of the current (IOSurface-backed) framebuffer into a temporary texture.
+ GLC(m_context, m_context->copyTexImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, 0, 0, rect.maxX(), rect.maxY(), 0));
+ temporaryFBO = m_context->createFramebuffer();
+ // Attach this texture to an FBO, and perform the readback from that FBO.
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, temporaryFBO));
+ GLC(m_context, m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, temporaryTexture, 0));
+
+ ASSERT(m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) == GraphicsContext3D::FRAMEBUFFER_COMPLETE);
+ }
+
+ GLC(m_context, m_context->readPixels(rect.x(), rect.y(), rect.width(), rect.height(),
+ GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels));
+
+ if (doWorkaround) {
+ // Clean up.
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0));
+ GLC(m_context, m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0));
+ GLC(m_context, m_context->deleteFramebuffer(temporaryFBO));
+ GLC(m_context, m_context->deleteTexture(temporaryTexture));
+ }
+
+ if (!m_visible) {
+ TRACE_EVENT0("cc", "CCRendererGL::getFramebufferPixels dropping resources after readback");
+ discardFramebuffer();
+ releaseRenderPassTextures();
+ m_client->releaseContentsTextures();
+ GLC(m_context, m_context->flush());
+ }
+}
+
+bool CCRendererGL::getFramebufferTexture(CCScopedTexture* texture, const IntRect& deviceRect)
+{
+ ASSERT(!texture->id() || (texture->size() == deviceRect.size() && texture->format() == GraphicsContext3D::RGB));
+
+ if (!texture->id() && !texture->allocate(CCRenderer::ImplPool, deviceRect.size(), GraphicsContext3D::RGB, CCResourceProvider::TextureUsageAny))
+ return false;
+
+ CCResourceProvider::ScopedWriteLockGL lock(m_resourceProvider, texture->id());
+ GLC(m_context, m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, lock.textureId()));
+ GLC(m_context, m_context->copyTexImage2D(GraphicsContext3D::TEXTURE_2D, 0, texture->format(),
+ deviceRect.x(), deviceRect.y(), deviceRect.width(), deviceRect.height(), 0));
+ return true;
+}
+
+bool CCRendererGL::useScopedTexture(DrawingFrame& frame, const CCScopedTexture* texture, const IntRect& viewportRect)
+{
+ ASSERT(texture->id());
+ frame.currentRenderPass = 0;
+ frame.currentTexture = texture;
+
+ return bindFramebufferToTexture(frame, texture, viewportRect);
+}
+
+void CCRendererGL::bindFramebufferToOutputSurface(DrawingFrame& frame)
+{
+ m_currentFramebufferLock.clear();
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0));
+}
+
+bool CCRendererGL::bindFramebufferToTexture(DrawingFrame& frame, const CCScopedTexture* texture, const IntRect& framebufferRect)
+{
+ ASSERT(texture->id());
+
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_offscreenFramebufferId));
+ m_currentFramebufferLock = adoptPtr(new CCResourceProvider::ScopedWriteLockGL(m_resourceProvider, texture->id()));
+ unsigned textureId = m_currentFramebufferLock->textureId();
+ GLC(m_context, m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, textureId, 0));
+
+#if !defined ( NDEBUG )
+ if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+#endif
+
+ initializeMatrices(frame, framebufferRect, false);
+ setDrawViewportSize(framebufferRect.size());
+
+ return true;
+}
+
+void CCRendererGL::enableScissorTestRect(const IntRect& scissorRect)
+{
+ GLC(m_context, m_context->enable(GraphicsContext3D::SCISSOR_TEST));
+ GLC(m_context, m_context->scissor(scissorRect.x(), scissorRect.y(), scissorRect.width(), scissorRect.height()));
+}
+
+void CCRendererGL::disableScissorTest()
+{
+ GLC(m_context, m_context->disable(GraphicsContext3D::SCISSOR_TEST));
+}
+
+void CCRendererGL::setDrawViewportSize(const IntSize& viewportSize)
+{
+ GLC(m_context, m_context->viewport(0, 0, viewportSize.width(), viewportSize.height()));
+}
+
+bool CCRendererGL::makeContextCurrent()
+{
+ return m_context->makeContextCurrent();
+}
+
+bool CCRendererGL::initializeSharedObjects()
+{
+ TRACE_EVENT0("cc", "CCRendererGL::initializeSharedObjects");
+ makeContextCurrent();
+
+ // Create an FBO for doing offscreen rendering.
+ GLC(m_context, m_offscreenFramebufferId = m_context->createFramebuffer());
+
+ // We will always need these programs to render, so create the programs eagerly so that the shader compilation can
+ // start while we do other work. Other programs are created lazily on first access.
+ m_sharedGeometry = adoptPtr(new GeometryBinding(m_context, quadVertexRect()));
+ m_renderPassProgram = adoptPtr(new RenderPassProgram(m_context));
+ m_tileProgram = adoptPtr(new TileProgram(m_context));
+ m_tileProgramOpaque = adoptPtr(new TileProgramOpaque(m_context));
+
+ GLC(m_context, m_context->flush());
+
+ m_textureCopier = AcceleratedTextureCopier::create(m_context, m_isUsingBindUniform);
+ if (m_textureUploaderSetting == ThrottledUploader)
+ m_textureUploader = ThrottledTextureUploader::create(m_context);
+ else
+ m_textureUploader = UnthrottledTextureUploader::create();
+
+ return true;
+}
+
+const CCRendererGL::TileCheckerboardProgram* CCRendererGL::tileCheckerboardProgram()
+{
+ if (!m_tileCheckerboardProgram)
+ m_tileCheckerboardProgram = adoptPtr(new TileCheckerboardProgram(m_context));
+ if (!m_tileCheckerboardProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::checkerboardProgram::initalize");
+ m_tileCheckerboardProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileCheckerboardProgram.get();
+}
+
+const CCRendererGL::SolidColorProgram* CCRendererGL::solidColorProgram()
+{
+ if (!m_solidColorProgram)
+ m_solidColorProgram = adoptPtr(new SolidColorProgram(m_context));
+ if (!m_solidColorProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::solidColorProgram::initialize");
+ m_solidColorProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_solidColorProgram.get();
+}
+
+const CCRendererGL::RenderPassProgram* CCRendererGL::renderPassProgram()
+{
+ ASSERT(m_renderPassProgram);
+ if (!m_renderPassProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::renderPassProgram::initialize");
+ m_renderPassProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_renderPassProgram.get();
+}
+
+const CCRendererGL::RenderPassProgramAA* CCRendererGL::renderPassProgramAA()
+{
+ if (!m_renderPassProgramAA)
+ m_renderPassProgramAA = adoptPtr(new RenderPassProgramAA(m_context));
+ if (!m_renderPassProgramAA->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::renderPassProgramAA::initialize");
+ m_renderPassProgramAA->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_renderPassProgramAA.get();
+}
+
+const CCRendererGL::RenderPassMaskProgram* CCRendererGL::renderPassMaskProgram()
+{
+ if (!m_renderPassMaskProgram)
+ m_renderPassMaskProgram = adoptPtr(new RenderPassMaskProgram(m_context));
+ if (!m_renderPassMaskProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::renderPassMaskProgram::initialize");
+ m_renderPassMaskProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_renderPassMaskProgram.get();
+}
+
+const CCRendererGL::RenderPassMaskProgramAA* CCRendererGL::renderPassMaskProgramAA()
+{
+ if (!m_renderPassMaskProgramAA)
+ m_renderPassMaskProgramAA = adoptPtr(new RenderPassMaskProgramAA(m_context));
+ if (!m_renderPassMaskProgramAA->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::renderPassMaskProgramAA::initialize");
+ m_renderPassMaskProgramAA->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_renderPassMaskProgramAA.get();
+}
+
+const CCRendererGL::TileProgram* CCRendererGL::tileProgram()
+{
+ ASSERT(m_tileProgram);
+ if (!m_tileProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgram::initialize");
+ m_tileProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgram.get();
+}
+
+const CCRendererGL::TileProgramOpaque* CCRendererGL::tileProgramOpaque()
+{
+ ASSERT(m_tileProgramOpaque);
+ if (!m_tileProgramOpaque->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgramOpaque::initialize");
+ m_tileProgramOpaque->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgramOpaque.get();
+}
+
+const CCRendererGL::TileProgramAA* CCRendererGL::tileProgramAA()
+{
+ if (!m_tileProgramAA)
+ m_tileProgramAA = adoptPtr(new TileProgramAA(m_context));
+ if (!m_tileProgramAA->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgramAA::initialize");
+ m_tileProgramAA->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgramAA.get();
+}
+
+const CCRendererGL::TileProgramSwizzle* CCRendererGL::tileProgramSwizzle()
+{
+ if (!m_tileProgramSwizzle)
+ m_tileProgramSwizzle = adoptPtr(new TileProgramSwizzle(m_context));
+ if (!m_tileProgramSwizzle->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgramSwizzle::initialize");
+ m_tileProgramSwizzle->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgramSwizzle.get();
+}
+
+const CCRendererGL::TileProgramSwizzleOpaque* CCRendererGL::tileProgramSwizzleOpaque()
+{
+ if (!m_tileProgramSwizzleOpaque)
+ m_tileProgramSwizzleOpaque = adoptPtr(new TileProgramSwizzleOpaque(m_context));
+ if (!m_tileProgramSwizzleOpaque->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgramSwizzleOpaque::initialize");
+ m_tileProgramSwizzleOpaque->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgramSwizzleOpaque.get();
+}
+
+const CCRendererGL::TileProgramSwizzleAA* CCRendererGL::tileProgramSwizzleAA()
+{
+ if (!m_tileProgramSwizzleAA)
+ m_tileProgramSwizzleAA = adoptPtr(new TileProgramSwizzleAA(m_context));
+ if (!m_tileProgramSwizzleAA->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::tileProgramSwizzleAA::initialize");
+ m_tileProgramSwizzleAA->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_tileProgramSwizzleAA.get();
+}
+
+const CCRendererGL::TextureProgram* CCRendererGL::textureProgram()
+{
+ if (!m_textureProgram)
+ m_textureProgram = adoptPtr(new TextureProgram(m_context));
+ if (!m_textureProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::textureProgram::initialize");
+ m_textureProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_textureProgram.get();
+}
+
+const CCRendererGL::TextureProgramFlip* CCRendererGL::textureProgramFlip()
+{
+ if (!m_textureProgramFlip)
+ m_textureProgramFlip = adoptPtr(new TextureProgramFlip(m_context));
+ if (!m_textureProgramFlip->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::textureProgramFlip::initialize");
+ m_textureProgramFlip->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_textureProgramFlip.get();
+}
+
+const CCRendererGL::TextureIOSurfaceProgram* CCRendererGL::textureIOSurfaceProgram()
+{
+ if (!m_textureIOSurfaceProgram)
+ m_textureIOSurfaceProgram = adoptPtr(new TextureIOSurfaceProgram(m_context));
+ if (!m_textureIOSurfaceProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::textureIOSurfaceProgram::initialize");
+ m_textureIOSurfaceProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_textureIOSurfaceProgram.get();
+}
+
+const CCRendererGL::VideoYUVProgram* CCRendererGL::videoYUVProgram()
+{
+ if (!m_videoYUVProgram)
+ m_videoYUVProgram = adoptPtr(new VideoYUVProgram(m_context));
+ if (!m_videoYUVProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::videoYUVProgram::initialize");
+ m_videoYUVProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_videoYUVProgram.get();
+}
+
+const CCRendererGL::VideoStreamTextureProgram* CCRendererGL::videoStreamTextureProgram()
+{
+ if (!m_videoStreamTextureProgram)
+ m_videoStreamTextureProgram = adoptPtr(new VideoStreamTextureProgram(m_context));
+ if (!m_videoStreamTextureProgram->initialized()) {
+ TRACE_EVENT0("cc", "CCRendererGL::streamTextureProgram::initialize");
+ m_videoStreamTextureProgram->initialize(m_context, m_isUsingBindUniform);
+ }
+ return m_videoStreamTextureProgram.get();
+}
+
+void CCRendererGL::cleanupSharedObjects()
+{
+ makeContextCurrent();
+
+ m_sharedGeometry.clear();
+
+ if (m_tileProgram)
+ m_tileProgram->cleanup(m_context);
+ if (m_tileProgramOpaque)
+ m_tileProgramOpaque->cleanup(m_context);
+ if (m_tileProgramSwizzle)
+ m_tileProgramSwizzle->cleanup(m_context);
+ if (m_tileProgramSwizzleOpaque)
+ m_tileProgramSwizzleOpaque->cleanup(m_context);
+ if (m_tileProgramAA)
+ m_tileProgramAA->cleanup(m_context);
+ if (m_tileProgramSwizzleAA)
+ m_tileProgramSwizzleAA->cleanup(m_context);
+ if (m_tileCheckerboardProgram)
+ m_tileCheckerboardProgram->cleanup(m_context);
+
+ if (m_renderPassMaskProgram)
+ m_renderPassMaskProgram->cleanup(m_context);
+ if (m_renderPassProgram)
+ m_renderPassProgram->cleanup(m_context);
+ if (m_renderPassMaskProgramAA)
+ m_renderPassMaskProgramAA->cleanup(m_context);
+ if (m_renderPassProgramAA)
+ m_renderPassProgramAA->cleanup(m_context);
+
+ if (m_textureProgram)
+ m_textureProgram->cleanup(m_context);
+ if (m_textureProgramFlip)
+ m_textureProgramFlip->cleanup(m_context);
+ if (m_textureIOSurfaceProgram)
+ m_textureIOSurfaceProgram->cleanup(m_context);
+
+ if (m_videoYUVProgram)
+ m_videoYUVProgram->cleanup(m_context);
+ if (m_videoStreamTextureProgram)
+ m_videoStreamTextureProgram->cleanup(m_context);
+
+ if (m_solidColorProgram)
+ m_solidColorProgram->cleanup(m_context);
+
+ if (m_offscreenFramebufferId)
+ GLC(m_context, m_context->deleteFramebuffer(m_offscreenFramebufferId));
+
+ m_textureCopier.clear();
+ m_textureUploader.clear();
+
+ releaseRenderPassTextures();
+}
+
+bool CCRendererGL::isContextLost()
+{
+ return (m_context->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR);
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCRendererGL.h b/cc/CCRendererGL.h
new file mode 100644
index 0000000..202d412
--- /dev/null
+++ b/cc/CCRendererGL.h
@@ -0,0 +1,245 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCRendererGL_h
+#define CCRendererGL_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCCheckerboardDrawQuad.h"
+#include "CCDebugBorderDrawQuad.h"
+#include "CCDirectRenderer.h"
+#include "CCIOSurfaceDrawQuad.h"
+#include "CCRenderPassDrawQuad.h"
+#include "CCRenderer.h"
+#include "CCSolidColorDrawQuad.h"
+#include "CCStreamVideoDrawQuad.h"
+#include "CCTextureDrawQuad.h"
+#include "CCTileDrawQuad.h"
+#include "CCYUVVideoDrawQuad.h"
+#include "Extensions3DChromium.h"
+#include "TextureCopier.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class CCScopedTexture;
+class GeometryBinding;
+class ScopedEnsureFramebufferAllocation;
+
+// Class that handles drawing of composited render layers using GL.
+class CCRendererGL : public CCDirectRenderer,
+ public WebKit::WebGraphicsContext3D::WebGraphicsSwapBuffersCompleteCallbackCHROMIUM,
+ public WebKit::WebGraphicsContext3D::WebGraphicsMemoryAllocationChangedCallbackCHROMIUM ,
+ public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback {
+ WTF_MAKE_NONCOPYABLE(CCRendererGL);
+public:
+ static PassOwnPtr<CCRendererGL> create(CCRendererClient*, CCResourceProvider*, TextureUploaderOption);
+
+ virtual ~CCRendererGL();
+
+ virtual const RendererCapabilities& capabilities() const OVERRIDE { return m_capabilities; }
+
+ WebKit::WebGraphicsContext3D* context();
+
+ virtual void viewportChanged() OVERRIDE;
+
+ const FloatQuad& sharedGeometryQuad() const { return m_sharedGeometryQuad; }
+
+ // waits for rendering to finish
+ virtual void finish() OVERRIDE;
+
+ virtual void doNoOp() OVERRIDE;
+ // puts backbuffer onscreen
+ virtual bool swapBuffers() OVERRIDE;
+
+ static void debugGLCall(WebKit::WebGraphicsContext3D*, const char* command, const char* file, int line);
+
+ const GeometryBinding* sharedGeometry() const { return m_sharedGeometry.get(); }
+
+ virtual void getFramebufferPixels(void *pixels, const IntRect&) OVERRIDE;
+ bool getFramebufferTexture(CCScopedTexture*, const IntRect& deviceRect);
+
+ virtual TextureCopier* textureCopier() const OVERRIDE { return m_textureCopier.get(); }
+ virtual TextureUploader* textureUploader() const OVERRIDE { return m_textureUploader.get(); }
+
+ virtual bool isContextLost() OVERRIDE;
+
+ virtual void setVisible(bool) OVERRIDE;
+
+protected:
+ CCRendererGL(CCRendererClient*, CCResourceProvider*, TextureUploaderOption);
+
+ bool isFramebufferDiscarded() const { return m_isFramebufferDiscarded; }
+ bool initialize();
+
+ void releaseRenderPassTextures();
+
+ virtual void bindFramebufferToOutputSurface(DrawingFrame&) OVERRIDE;
+ virtual bool bindFramebufferToTexture(DrawingFrame&, const CCScopedTexture*, const IntRect& framebufferRect) OVERRIDE;
+ virtual void setDrawViewportSize(const IntSize&) OVERRIDE;
+ virtual void enableScissorTestRect(const IntRect& scissorRect) OVERRIDE;
+ virtual void disableScissorTest() OVERRIDE;
+ virtual void clearFramebuffer(DrawingFrame&) OVERRIDE;
+ virtual void drawQuad(DrawingFrame&, const CCDrawQuad*) OVERRIDE;
+ virtual void beginDrawingFrame(DrawingFrame&) OVERRIDE;
+ virtual void finishDrawingFrame(DrawingFrame&) OVERRIDE;
+
+private:
+ static void toGLMatrix(float*, const WebKit::WebTransformationMatrix&);
+
+ void drawCheckerboardQuad(const DrawingFrame&, const CCCheckerboardDrawQuad*);
+ void drawDebugBorderQuad(const DrawingFrame&, const CCDebugBorderDrawQuad*);
+ PassOwnPtr<CCScopedTexture> drawBackgroundFilters(DrawingFrame&, const CCRenderPassDrawQuad*, const WebKit::WebFilterOperations&, const WebKit::WebTransformationMatrix& deviceTransform);
+ void drawRenderPassQuad(DrawingFrame&, const CCRenderPassDrawQuad*);
+ void drawSolidColorQuad(const DrawingFrame&, const CCSolidColorDrawQuad*);
+ void drawStreamVideoQuad(const DrawingFrame&, const CCStreamVideoDrawQuad*);
+ void drawTextureQuad(const DrawingFrame&, const CCTextureDrawQuad*);
+ void drawIOSurfaceQuad(const DrawingFrame&, const CCIOSurfaceDrawQuad*);
+ void drawTileQuad(const DrawingFrame&, const CCTileDrawQuad*);
+ void drawYUVVideoQuad(const DrawingFrame&, const CCYUVVideoDrawQuad*);
+
+ void setShaderOpacity(float opacity, int alphaLocation);
+ void setShaderFloatQuad(const FloatQuad&, int quadLocation);
+ void drawQuadGeometry(const DrawingFrame&, const WebKit::WebTransformationMatrix& drawTransform, const FloatRect& quadRect, int matrixLocation);
+
+ void copyTextureToFramebuffer(const DrawingFrame&, int textureId, const IntRect&, const WebKit::WebTransformationMatrix& drawMatrix);
+
+ bool useScopedTexture(DrawingFrame&, const CCScopedTexture*, const IntRect& viewportRect);
+
+ bool makeContextCurrent();
+
+ bool initializeSharedObjects();
+ void cleanupSharedObjects();
+
+ // WebKit::WebGraphicsContext3D::WebGraphicsSwapBuffersCompleteCallbackCHROMIUM implementation.
+ virtual void onSwapBuffersComplete() OVERRIDE;
+
+ // WebKit::WebGraphicsContext3D::WebGraphicsMemoryAllocationChangedCallbackCHROMIUM implementation.
+ virtual void onMemoryAllocationChanged(WebKit::WebGraphicsMemoryAllocation) OVERRIDE;
+ void onMemoryAllocationChangedOnImplThread(WebKit::WebGraphicsMemoryAllocation);
+ void discardFramebuffer();
+ void ensureFramebuffer();
+
+ // WebGraphicsContext3D::WebGraphicsContextLostCallback implementation.
+ virtual void onContextLost() OVERRIDE;
+
+ RendererCapabilities m_capabilities;
+
+ unsigned m_offscreenFramebufferId;
+
+ OwnPtr<GeometryBinding> m_sharedGeometry;
+ FloatQuad m_sharedGeometryQuad;
+
+ // This block of bindings defines all of the programs used by the compositor itself.
+
+ // Tiled layer shaders.
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexAlpha> TileProgram;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexClampAlphaAA> TileProgramAA;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexClampSwizzleAlphaAA> TileProgramSwizzleAA;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexOpaque> TileProgramOpaque;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexSwizzleAlpha> TileProgramSwizzle;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexSwizzleOpaque> TileProgramSwizzleOpaque;
+ typedef ProgramBinding<VertexShaderPosTex, FragmentShaderCheckerboard> TileCheckerboardProgram;
+
+ // Render surface shaders.
+ typedef ProgramBinding<VertexShaderPosTex, FragmentShaderRGBATexAlpha> RenderPassProgram;
+ typedef ProgramBinding<VertexShaderPosTex, FragmentShaderRGBATexAlphaMask> RenderPassMaskProgram;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderRGBATexAlphaAA> RenderPassProgramAA;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderRGBATexAlphaMaskAA> RenderPassMaskProgramAA;
+
+ // Texture shaders.
+ typedef ProgramBinding<VertexShaderPosTexTransform, FragmentShaderRGBATexAlpha> TextureProgram;
+ typedef ProgramBinding<VertexShaderPosTexTransform, FragmentShaderRGBATexFlipAlpha> TextureProgramFlip;
+ typedef ProgramBinding<VertexShaderPosTexTransform, FragmentShaderRGBATexRectAlpha> TextureIOSurfaceProgram;
+
+ // Video shaders.
+ typedef ProgramBinding<VertexShaderVideoTransform, FragmentShaderOESImageExternal> VideoStreamTextureProgram;
+ typedef ProgramBinding<VertexShaderPosTexYUVStretch, FragmentShaderYUVVideo> VideoYUVProgram;
+
+ // Special purpose / effects shaders.
+ typedef ProgramBinding<VertexShaderPos, FragmentShaderColor> SolidColorProgram;
+
+ const TileProgram* tileProgram();
+ const TileProgramOpaque* tileProgramOpaque();
+ const TileProgramAA* tileProgramAA();
+ const TileProgramSwizzle* tileProgramSwizzle();
+ const TileProgramSwizzleOpaque* tileProgramSwizzleOpaque();
+ const TileProgramSwizzleAA* tileProgramSwizzleAA();
+ const TileCheckerboardProgram* tileCheckerboardProgram();
+
+ const RenderPassProgram* renderPassProgram();
+ const RenderPassProgramAA* renderPassProgramAA();
+ const RenderPassMaskProgram* renderPassMaskProgram();
+ const RenderPassMaskProgramAA* renderPassMaskProgramAA();
+
+ const TextureProgram* textureProgram();
+ const TextureProgramFlip* textureProgramFlip();
+ const TextureIOSurfaceProgram* textureIOSurfaceProgram();
+
+ const VideoYUVProgram* videoYUVProgram();
+ const VideoStreamTextureProgram* videoStreamTextureProgram();
+
+ const SolidColorProgram* solidColorProgram();
+
+ OwnPtr<TileProgram> m_tileProgram;
+ OwnPtr<TileProgramOpaque> m_tileProgramOpaque;
+ OwnPtr<TileProgramAA> m_tileProgramAA;
+ OwnPtr<TileProgramSwizzle> m_tileProgramSwizzle;
+ OwnPtr<TileProgramSwizzleOpaque> m_tileProgramSwizzleOpaque;
+ OwnPtr<TileProgramSwizzleAA> m_tileProgramSwizzleAA;
+ OwnPtr<TileCheckerboardProgram> m_tileCheckerboardProgram;
+
+ OwnPtr<RenderPassProgram> m_renderPassProgram;
+ OwnPtr<RenderPassProgramAA> m_renderPassProgramAA;
+ OwnPtr<RenderPassMaskProgram> m_renderPassMaskProgram;
+ OwnPtr<RenderPassMaskProgramAA> m_renderPassMaskProgramAA;
+
+ OwnPtr<TextureProgram> m_textureProgram;
+ OwnPtr<TextureProgramFlip> m_textureProgramFlip;
+ OwnPtr<TextureIOSurfaceProgram> m_textureIOSurfaceProgram;
+
+ OwnPtr<VideoYUVProgram> m_videoYUVProgram;
+ OwnPtr<VideoStreamTextureProgram> m_videoStreamTextureProgram;
+
+ OwnPtr<SolidColorProgram> m_solidColorProgram;
+
+ OwnPtr<AcceleratedTextureCopier> m_textureCopier;
+ OwnPtr<TextureUploader> m_textureUploader;
+
+ WebKit::WebGraphicsContext3D* m_context;
+
+ IntRect m_swapBufferRect;
+ bool m_isViewportChanged;
+ bool m_isFramebufferDiscarded;
+ bool m_isUsingBindUniform;
+ bool m_visible;
+ TextureUploaderOption m_textureUploaderSetting;
+
+ OwnPtr<CCResourceProvider::ScopedWriteLockGL> m_currentFramebufferLock;
+};
+
+
+// Setting DEBUG_GL_CALLS to 1 will call glGetError() after almost every GL
+// call made by the compositor. Useful for debugging rendering issues but
+// will significantly degrade performance.
+#define DEBUG_GL_CALLS 0
+
+#if DEBUG_GL_CALLS && !defined ( NDEBUG )
+#define GLC(context, x) (x, CCRendererGL::debugGLCall(&*context, #x, __FILE__, __LINE__))
+#else
+#define GLC(context, x) (x)
+#endif
+
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCRenderingStats.h b/cc/CCRenderingStats.h
new file mode 100644
index 0000000..5926f6c
--- /dev/null
+++ b/cc/CCRenderingStats.h
@@ -0,0 +1,30 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCRenderingStats_h
+#define CCRenderingStats_h
+
+namespace WebCore {
+
+struct CCRenderingStats {
+ // FIXME: Rename these to animationFrameCount and screenFrameCount, crbug.com/138641.
+ int numAnimationFrames;
+ int numFramesSentToScreen;
+ int droppedFrameCount;
+ double totalPaintTimeInSeconds;
+ double totalRasterizeTimeInSeconds;
+
+ CCRenderingStats()
+ : numAnimationFrames(0)
+ , numFramesSentToScreen(0)
+ , droppedFrameCount(0)
+ , totalPaintTimeInSeconds(0)
+ , totalRasterizeTimeInSeconds(0)
+ {
+ }
+};
+
+}
+
+#endif
diff --git a/cc/CCResourceProvider.cpp b/cc/CCResourceProvider.cpp
new file mode 100644
index 0000000..2f575ed
--- /dev/null
+++ b/cc/CCResourceProvider.cpp
@@ -0,0 +1,529 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCResourceProvider.h"
+
+#include "CCProxy.h"
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "Extensions3DChromium.h"
+#include "IntRect.h"
+#include "LayerTextureSubImage.h"
+#include <limits.h>
+#include <public/WebGraphicsContext3D.h>
+#include <wtf/HashSet.h>
+
+using WebKit::WebGraphicsContext3D;
+
+namespace WebCore {
+
+static GC3Denum textureToStorageFormat(GC3Denum textureFormat)
+{
+ GC3Denum storageFormat = Extensions3D::RGBA8_OES;
+ switch (textureFormat) {
+ case GraphicsContext3D::RGBA:
+ break;
+ case Extensions3D::BGRA_EXT:
+ storageFormat = Extensions3DChromium::BGRA8_EXT;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ return storageFormat;
+}
+
+static bool isTextureFormatSupportedForStorage(GC3Denum format)
+{
+ return (format == GraphicsContext3D::RGBA || format == Extensions3D::BGRA_EXT);
+}
+
+PassOwnPtr<CCResourceProvider> CCResourceProvider::create(CCGraphicsContext* context)
+{
+ OwnPtr<CCResourceProvider> resourceProvider(adoptPtr(new CCResourceProvider(context)));
+ if (!resourceProvider->initialize())
+ return nullptr;
+ return resourceProvider.release();
+}
+
+CCResourceProvider::~CCResourceProvider()
+{
+}
+
+WebGraphicsContext3D* CCResourceProvider::graphicsContext3D()
+{
+ ASSERT(CCProxy::isImplThread());
+ return m_context->context3D();
+}
+
+bool CCResourceProvider::inUseByConsumer(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end());
+ return !!it->second.lockForReadCount || it->second.exported;
+}
+
+CCResourceProvider::ResourceId CCResourceProvider::createResource(int pool, const IntSize& size, GC3Denum format, TextureUsageHint hint)
+{
+ switch (m_defaultResourceType) {
+ case GLTexture:
+ return createGLTexture(pool, size, format, hint);
+ case Bitmap:
+ ASSERT(format == GraphicsContext3D::RGBA);
+ return createBitmap(pool, size);
+ }
+
+ CRASH();
+ return 0;
+}
+
+CCResourceProvider::ResourceId CCResourceProvider::createGLTexture(int pool, const IntSize& size, GC3Denum format, TextureUsageHint hint)
+{
+ ASSERT(CCProxy::isImplThread());
+ unsigned textureId = 0;
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ ASSERT(context3d);
+ GLC(context3d, textureId = context3d->createTexture());
+ GLC(context3d, context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
+ GLC(context3d, context3d->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context3d, context3d->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(context3d, context3d->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
+ GLC(context3d, context3d->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
+
+ if (m_useTextureUsageHint && hint == TextureUsageFramebuffer)
+ GLC(context3d, context3d->texParameteri(GraphicsContext3D::TEXTURE_2D, Extensions3DChromium::GL_TEXTURE_USAGE_ANGLE, Extensions3DChromium::GL_FRAMEBUFFER_ATTACHMENT_ANGLE));
+ if (m_useTextureStorageExt && isTextureFormatSupportedForStorage(format)) {
+ GC3Denum storageFormat = textureToStorageFormat(format);
+ GLC(context3d, context3d->texStorage2DEXT(GraphicsContext3D::TEXTURE_2D, 1, storageFormat, size.width(), size.height()));
+ } else
+ GLC(context3d, context3d->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, GraphicsContext3D::UNSIGNED_BYTE, 0));
+ ResourceId id = m_nextId++;
+ Resource resource(textureId, pool, size, format);
+ m_resources.add(id, resource);
+ return id;
+}
+
+CCResourceProvider::ResourceId CCResourceProvider::createBitmap(int pool, const IntSize& size)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ uint8_t* pixels = new uint8_t[size.width() * size.height() * 4];
+
+ ResourceId id = m_nextId++;
+ Resource resource(pixels, pool, size, GraphicsContext3D::RGBA);
+ m_resources.add(id, resource);
+ return id;
+}
+
+CCResourceProvider::ResourceId CCResourceProvider::createResourceFromExternalTexture(unsigned textureId)
+{
+ ASSERT(CCProxy::isImplThread());
+ ASSERT(m_context->context3D());
+ ResourceId id = m_nextId++;
+ Resource resource(textureId, 0, IntSize(), 0);
+ resource.external = true;
+ m_resources.add(id, resource);
+ return id;
+}
+
+void CCResourceProvider::deleteResource(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && !it->second.lockedForWrite && !it->second.lockForReadCount);
+
+ if (it->second.glId && !it->second.external) {
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ ASSERT(context3d);
+ GLC(context3d, context3d->deleteTexture(it->second.glId));
+ }
+ if (it->second.pixels)
+ delete it->second.pixels;
+
+ m_resources.remove(it);
+}
+
+void CCResourceProvider::deleteOwnedResources(int pool)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceIdArray toDelete;
+ for (ResourceMap::iterator it = m_resources.begin(); it != m_resources.end(); ++it) {
+ if (it->second.pool == pool && !it->second.external)
+ toDelete.append(it->first);
+ }
+ for (ResourceIdArray::iterator it = toDelete.begin(); it != toDelete.end(); ++it)
+ deleteResource(*it);
+}
+
+CCResourceProvider::ResourceType CCResourceProvider::resourceType(ResourceId id)
+{
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end());
+ return it->second.type;
+}
+
+void CCResourceProvider::upload(ResourceId id, const uint8_t* image, const IntRect& imageRect, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && !it->second.lockedForWrite && !it->second.lockForReadCount && !it->second.external);
+
+ if (it->second.glId) {
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ ASSERT(context3d);
+ ASSERT(m_texSubImage.get());
+ context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, it->second.glId);
+ m_texSubImage->upload(image, imageRect, sourceRect, destOffset, it->second.format, context3d);
+ }
+
+ if (it->second.pixels) {
+ SkBitmap srcFull;
+ srcFull.setConfig(SkBitmap::kARGB_8888_Config, imageRect.width(), imageRect.height());
+ srcFull.setPixels(const_cast<uint8_t*>(image));
+ SkBitmap srcSubset;
+ SkIRect skSourceRect = SkIRect::MakeXYWH(sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height());
+ skSourceRect.offset(-imageRect.x(), -imageRect.y());
+ srcFull.extractSubset(&srcSubset, skSourceRect);
+
+ ScopedWriteLockSoftware lock(this, id);
+ SkCanvas* dest = lock.skCanvas();
+ dest->writePixels(srcSubset, destOffset.width(), destOffset.height());
+ }
+}
+
+void CCResourceProvider::flush()
+{
+ ASSERT(CCProxy::isImplThread());
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (context3d)
+ context3d->flush();
+}
+
+bool CCResourceProvider::shallowFlushIfSupported()
+{
+ ASSERT(CCProxy::isImplThread());
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d || !m_useShallowFlush)
+ return false;
+
+ context3d->shallowFlushCHROMIUM();
+ return true;
+}
+
+const CCResourceProvider::Resource* CCResourceProvider::lockForRead(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && !it->second.lockedForWrite);
+ it->second.lockForReadCount++;
+ return &it->second;
+}
+
+void CCResourceProvider::unlockForRead(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && it->second.lockForReadCount > 0);
+ it->second.lockForReadCount--;
+}
+
+const CCResourceProvider::Resource* CCResourceProvider::lockForWrite(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && !it->second.lockedForWrite && !it->second.lockForReadCount && !it->second.external);
+ it->second.lockedForWrite = true;
+ return &it->second;
+}
+
+void CCResourceProvider::unlockForWrite(ResourceId id)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && it->second.lockedForWrite && !it->second.external);
+ it->second.lockedForWrite = false;
+}
+
+CCResourceProvider::ScopedReadLockGL::ScopedReadLockGL(CCResourceProvider* resourceProvider, CCResourceProvider::ResourceId resourceId)
+ : m_resourceProvider(resourceProvider)
+ , m_resourceId(resourceId)
+ , m_textureId(resourceProvider->lockForRead(resourceId)->glId)
+{
+ ASSERT(m_textureId);
+}
+
+CCResourceProvider::ScopedReadLockGL::~ScopedReadLockGL()
+{
+ m_resourceProvider->unlockForRead(m_resourceId);
+}
+
+CCResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(CCResourceProvider* resourceProvider, CCResourceProvider::ResourceId resourceId)
+ : m_resourceProvider(resourceProvider)
+ , m_resourceId(resourceId)
+ , m_textureId(resourceProvider->lockForWrite(resourceId)->glId)
+{
+ ASSERT(m_textureId);
+}
+
+CCResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL()
+{
+ m_resourceProvider->unlockForWrite(m_resourceId);
+}
+
+void CCResourceProvider::populateSkBitmapWithResource(SkBitmap* skBitmap, const Resource* resource)
+{
+ ASSERT(resource->pixels);
+ ASSERT(resource->format == GraphicsContext3D::RGBA);
+ skBitmap->setConfig(SkBitmap::kARGB_8888_Config, resource->size.width(), resource->size.height());
+ skBitmap->setPixels(resource->pixels);
+}
+
+CCResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware(CCResourceProvider* resourceProvider, CCResourceProvider::ResourceId resourceId)
+ : m_resourceProvider(resourceProvider)
+ , m_resourceId(resourceId)
+{
+ CCResourceProvider::populateSkBitmapWithResource(&m_skBitmap, resourceProvider->lockForRead(resourceId));
+}
+
+CCResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware()
+{
+ m_resourceProvider->unlockForRead(m_resourceId);
+}
+
+CCResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware(CCResourceProvider* resourceProvider, CCResourceProvider::ResourceId resourceId)
+ : m_resourceProvider(resourceProvider)
+ , m_resourceId(resourceId)
+{
+ CCResourceProvider::populateSkBitmapWithResource(&m_skBitmap, resourceProvider->lockForWrite(resourceId));
+ m_skCanvas.setBitmapDevice(m_skBitmap);
+}
+
+CCResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware()
+{
+ m_resourceProvider->unlockForWrite(m_resourceId);
+}
+
+CCResourceProvider::CCResourceProvider(CCGraphicsContext* context)
+ : m_context(context)
+ , m_nextId(1)
+ , m_nextChild(1)
+ , m_defaultResourceType(GLTexture)
+ , m_useTextureStorageExt(false)
+ , m_useTextureUsageHint(false)
+ , m_useShallowFlush(false)
+ , m_maxTextureSize(0)
+{
+}
+
+bool CCResourceProvider::initialize()
+{
+ ASSERT(CCProxy::isImplThread());
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d) {
+ m_maxTextureSize = INT_MAX;
+
+ // FIXME: Implement this path for software compositing.
+ return false;
+ }
+ if (!context3d->makeContextCurrent())
+ return false;
+
+ WebKit::WebString extensionsWebString = context3d->getString(GraphicsContext3D::EXTENSIONS);
+ String extensionsString(extensionsWebString.data(), extensionsWebString.length());
+ Vector<String> extensions;
+ extensionsString.split(' ', extensions);
+ bool useMapSub = false;
+ for (size_t i = 0; i < extensions.size(); ++i) {
+ if (extensions[i] == "GL_EXT_texture_storage")
+ m_useTextureStorageExt = true;
+ else if (extensions[i] == "GL_ANGLE_texture_usage")
+ m_useTextureUsageHint = true;
+ else if (extensions[i] == "GL_CHROMIUM_map_sub")
+ useMapSub = true;
+ else if (extensions[i] == "GL_CHROMIUM_shallow_flush")
+ m_useShallowFlush = true;
+ }
+
+ m_texSubImage = adoptPtr(new LayerTextureSubImage(useMapSub));
+ GLC(context3d, context3d->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize));
+ return true;
+}
+
+int CCResourceProvider::createChild(int pool)
+{
+ ASSERT(CCProxy::isImplThread());
+ Child childInfo;
+ childInfo.pool = pool;
+ int child = m_nextChild++;
+ m_children.add(child, childInfo);
+ return child;
+}
+
+void CCResourceProvider::destroyChild(int child)
+{
+ ASSERT(CCProxy::isImplThread());
+ ChildMap::iterator it = m_children.find(child);
+ ASSERT(it != m_children.end());
+ deleteOwnedResources(it->second.pool);
+ m_children.remove(it);
+ trimMailboxDeque();
+}
+
+const CCResourceProvider::ResourceIdMap& CCResourceProvider::getChildToParentMap(int child) const
+{
+ ASSERT(CCProxy::isImplThread());
+ ChildMap::const_iterator it = m_children.find(child);
+ ASSERT(it != m_children.end());
+ return it->second.childToParentMap;
+}
+
+CCResourceProvider::TransferableResourceList CCResourceProvider::prepareSendToParent(const ResourceIdArray& resources)
+{
+ ASSERT(CCProxy::isImplThread());
+ TransferableResourceList list;
+ list.syncPoint = 0;
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d || !context3d->makeContextCurrent()) {
+ // FIXME: Implement this path for software compositing.
+ return list;
+ }
+ for (ResourceIdArray::const_iterator it = resources.begin(); it != resources.end(); ++it) {
+ TransferableResource resource;
+ if (transferResource(context3d, *it, &resource)) {
+ m_resources.find(*it)->second.exported = true;
+ list.resources.append(resource);
+ }
+ }
+ if (list.resources.size())
+ list.syncPoint = context3d->insertSyncPoint();
+ return list;
+}
+
+CCResourceProvider::TransferableResourceList CCResourceProvider::prepareSendToChild(int child, const ResourceIdArray& resources)
+{
+ ASSERT(CCProxy::isImplThread());
+ TransferableResourceList list;
+ list.syncPoint = 0;
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d || !context3d->makeContextCurrent()) {
+ // FIXME: Implement this path for software compositing.
+ return list;
+ }
+ Child& childInfo = m_children.find(child)->second;
+ for (ResourceIdArray::const_iterator it = resources.begin(); it != resources.end(); ++it) {
+ TransferableResource resource;
+ if (!transferResource(context3d, *it, &resource))
+ ASSERT_NOT_REACHED();
+ resource.id = childInfo.parentToChildMap.get(*it);
+ childInfo.parentToChildMap.remove(*it);
+ childInfo.childToParentMap.remove(resource.id);
+ list.resources.append(resource);
+ deleteResource(*it);
+ }
+ if (list.resources.size())
+ list.syncPoint = context3d->insertSyncPoint();
+ return list;
+}
+
+void CCResourceProvider::receiveFromChild(int child, const TransferableResourceList& resources)
+{
+ ASSERT(CCProxy::isImplThread());
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d || !context3d->makeContextCurrent()) {
+ // FIXME: Implement this path for software compositing.
+ return;
+ }
+ if (resources.syncPoint) {
+ // NOTE: If the parent is a browser and the child a renderer, the parent
+ // is not supposed to have its context wait, because that could induce
+ // deadlocks and/or security issues. The caller is responsible for
+ // waiting asynchronously, and resetting syncPoint before calling this.
+ // However if the parent is a renderer (e.g. browser tag), it may be ok
+ // (and is simpler) to wait.
+ GLC(context3d, context3d->waitSyncPoint(resources.syncPoint));
+ }
+ Child& childInfo = m_children.find(child)->second;
+ for (Vector<TransferableResource>::const_iterator it = resources.resources.begin(); it != resources.resources.end(); ++it) {
+ unsigned textureId;
+ GLC(context3d, textureId = context3d->createTexture());
+ GLC(context3d, context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
+ GLC(context3d, context3d->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, it->mailbox.name));
+ ResourceId id = m_nextId++;
+ Resource resource(textureId, childInfo.pool, it->size, it->format);
+ m_resources.add(id, resource);
+ m_mailboxes.append(it->mailbox);
+ childInfo.parentToChildMap.add(id, it->id);
+ childInfo.childToParentMap.add(it->id, id);
+ }
+}
+
+void CCResourceProvider::receiveFromParent(const TransferableResourceList& resources)
+{
+ ASSERT(CCProxy::isImplThread());
+ WebGraphicsContext3D* context3d = m_context->context3D();
+ if (!context3d || !context3d->makeContextCurrent()) {
+ // FIXME: Implement this path for software compositing.
+ return;
+ }
+ if (resources.syncPoint)
+ GLC(context3d, context3d->waitSyncPoint(resources.syncPoint));
+ for (Vector<TransferableResource>::const_iterator it = resources.resources.begin(); it != resources.resources.end(); ++it) {
+ Resource& resource = m_resources.find(it->id)->second;
+ ASSERT(resource.exported);
+ resource.exported = false;
+ GLC(context3d, context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, resource.glId));
+ GLC(context3d, context3d->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, it->mailbox.name));
+ m_mailboxes.append(it->mailbox);
+ }
+}
+
+bool CCResourceProvider::transferResource(WebGraphicsContext3D* context, ResourceId id, TransferableResource* resource)
+{
+ ASSERT(CCProxy::isImplThread());
+ ResourceMap::const_iterator it = m_resources.find(id);
+ ASSERT(it != m_resources.end() && !it->second.lockedForWrite && !it->second.lockForReadCount && !it->second.external);
+ if (it->second.exported)
+ return false;
+ resource->id = id;
+ resource->format = it->second.format;
+ resource->size = it->second.size;
+ if (!m_mailboxes.isEmpty())
+ resource->mailbox = m_mailboxes.takeFirst();
+ else
+ GLC(context, context->genMailboxCHROMIUM(resource->mailbox.name));
+ GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, it->second.glId));
+ GLC(context, context->produceTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, resource->mailbox.name));
+ return true;
+}
+
+void CCResourceProvider::trimMailboxDeque()
+{
+ // Trim the mailbox deque to the maximum number of resources we may need to
+ // send.
+ // If we have a parent, any non-external resource not already transfered is
+ // eligible to be sent to the parent. Otherwise, all resources belonging to
+ // a child might need to be sent back to the child.
+ size_t maxMailboxCount = 0;
+ if (m_context->capabilities().hasParentCompositor) {
+ for (ResourceMap::iterator it = m_resources.begin(); it != m_resources.end(); ++it) {
+ if (!it->second.exported && !it->second.external)
+ ++maxMailboxCount;
+ }
+ } else {
+ HashSet<int> childPoolSet;
+ for (ChildMap::iterator it = m_children.begin(); it != m_children.end(); ++it)
+ childPoolSet.add(it->second.pool);
+ for (ResourceMap::iterator it = m_resources.begin(); it != m_resources.end(); ++it) {
+ if (childPoolSet.contains(it->second.pool))
+ ++maxMailboxCount;
+ }
+ }
+ while (m_mailboxes.size() > maxMailboxCount)
+ m_mailboxes.removeFirst();
+}
+
+}
diff --git a/cc/CCResourceProvider.h b/cc/CCResourceProvider.h
new file mode 100644
index 0000000..8368683
--- /dev/null
+++ b/cc/CCResourceProvider.h
@@ -0,0 +1,289 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CCResourceProvider_h
+#define CCResourceProvider_h
+
+#include "CCGraphicsContext.h"
+#include "GraphicsContext3D.h"
+#include "IntSize.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class IntRect;
+class LayerTextureSubImage;
+
+// Thread-safety notes: this class is not thread-safe and can only be called
+// from the thread it was created on (in practice, the compositor thread).
+class CCResourceProvider {
+ WTF_MAKE_NONCOPYABLE(CCResourceProvider);
+public:
+ typedef unsigned ResourceId;
+ typedef Vector<ResourceId> ResourceIdArray;
+ typedef HashMap<ResourceId, ResourceId> ResourceIdMap;
+ enum TextureUsageHint { TextureUsageAny, TextureUsageFramebuffer };
+ enum ResourceType {
+ GLTexture = 1,
+ Bitmap,
+ };
+ struct Mailbox {
+ GC3Dbyte name[64];
+ };
+ struct TransferableResource {
+ unsigned id;
+ GC3Denum format;
+ IntSize size;
+ Mailbox mailbox;
+ };
+ typedef Vector<TransferableResource> TransferableResourceArray;
+ struct TransferableResourceList {
+ TransferableResourceArray resources;
+ unsigned syncPoint;
+ };
+
+ static PassOwnPtr<CCResourceProvider> create(CCGraphicsContext*);
+
+ virtual ~CCResourceProvider();
+
+ WebKit::WebGraphicsContext3D* graphicsContext3D();
+ int maxTextureSize() const { return m_maxTextureSize; }
+ unsigned numResources() const { return m_resources.size(); }
+
+ // Checks whether a resource is in use by a consumer.
+ bool inUseByConsumer(ResourceId);
+
+
+ // Producer interface.
+
+ void setDefaultResourceType(ResourceType type) { m_defaultResourceType = type; }
+ ResourceType defaultResourceType() const { return m_defaultResourceType; }
+ ResourceType resourceType(ResourceId);
+
+ // Creates a resource of the default resource type.
+ ResourceId createResource(int pool, const IntSize&, GC3Denum format, TextureUsageHint);
+
+ // You can also explicitly create a specific resource type.
+ ResourceId createGLTexture(int pool, const IntSize&, GC3Denum format, TextureUsageHint);
+ ResourceId createBitmap(int pool, const IntSize&);
+ // Wraps an external texture into a GL resource.
+ ResourceId createResourceFromExternalTexture(unsigned textureId);
+
+ void deleteResource(ResourceId);
+
+ // Deletes all resources owned by a given pool.
+ void deleteOwnedResources(int pool);
+
+ // Upload data from image, copying sourceRect (in image) into destRect (in the resource).
+ void upload(ResourceId, const uint8_t* image, const IntRect& imageRect, const IntRect& sourceRect, const IntSize& destOffset);
+
+ // Flush all context operations, kicking uploads and ensuring ordering with
+ // respect to other contexts.
+ void flush();
+
+ // Only flush the command buffer if supported.
+ // Returns true if the shallow flush occurred, false otherwise.
+ bool shallowFlushIfSupported();
+
+ // Creates accounting for a child, and associate it with a pool. Resources
+ // transfered from that child will go to that pool. Returns a child ID.
+ int createChild(int pool);
+
+ // Destroys accounting for the child, deleting all resources from that pool.
+ void destroyChild(int child);
+
+ // Gets the child->parent resource ID map.
+ const ResourceIdMap& getChildToParentMap(int child) const;
+
+ // Prepares resources to be transfered to the parent, moving them to
+ // mailboxes and serializing meta-data into TransferableResources.
+ // Resources are not removed from the CCResourceProvider, but are markes as
+ // "in use".
+ TransferableResourceList prepareSendToParent(const ResourceIdArray&);
+
+ // Prepares resources to be transfered back to the child, moving them to
+ // mailboxes and serializing meta-data into TransferableResources.
+ // Resources are removed from the CCResourceProvider. Note: the resource IDs
+ // passed are in the parent namespace and will be translated to the child
+ // namespace when returned.
+ TransferableResourceList prepareSendToChild(int child, const ResourceIdArray&);
+
+ // Receives resources from a child, moving them from mailboxes. Resource IDs
+ // passed are in the child namespace, and will be translated to the parent
+ // namespace, added to the child->parent map.
+ // NOTE: if the syncPoint filed in TransferableResourceList is set, this
+ // will wait on it.
+ void receiveFromChild(int child, const TransferableResourceList&);
+
+ // Receives resources from the parent, moving them from mailboxes. Resource IDs
+ // passed are in the child namespace.
+ // NOTE: if the syncPoint filed in TransferableResourceList is set, this
+ // will wait on it.
+ void receiveFromParent(const TransferableResourceList&);
+
+ // Only for testing
+ size_t mailboxCount() const { return m_mailboxes.size(); }
+
+ // The following lock classes are part of the CCResourceProvider API and are
+ // needed to read and write the resource contents. The user must ensure
+ // that they only use GL locks on GL resources, etc, and this is enforced
+ // by assertions.
+ class ScopedReadLockGL {
+ WTF_MAKE_NONCOPYABLE(ScopedReadLockGL);
+ public:
+ ScopedReadLockGL(CCResourceProvider*, CCResourceProvider::ResourceId);
+ ~ScopedReadLockGL();
+
+ unsigned textureId() const { return m_textureId; }
+
+ private:
+ CCResourceProvider* m_resourceProvider;
+ CCResourceProvider::ResourceId m_resourceId;
+ unsigned m_textureId;
+ };
+
+ class ScopedWriteLockGL {
+ WTF_MAKE_NONCOPYABLE(ScopedWriteLockGL);
+ public:
+ ScopedWriteLockGL(CCResourceProvider*, CCResourceProvider::ResourceId);
+ ~ScopedWriteLockGL();
+
+ unsigned textureId() const { return m_textureId; }
+
+ private:
+ CCResourceProvider* m_resourceProvider;
+ CCResourceProvider::ResourceId m_resourceId;
+ unsigned m_textureId;
+ };
+
+ class ScopedReadLockSoftware {
+ WTF_MAKE_NONCOPYABLE(ScopedReadLockSoftware);
+ public:
+ ScopedReadLockSoftware(CCResourceProvider*, CCResourceProvider::ResourceId);
+ ~ScopedReadLockSoftware();
+
+ const SkBitmap* skBitmap() const { return &m_skBitmap; }
+
+ private:
+ CCResourceProvider* m_resourceProvider;
+ CCResourceProvider::ResourceId m_resourceId;
+ SkBitmap m_skBitmap;
+ };
+
+ class ScopedWriteLockSoftware {
+ WTF_MAKE_NONCOPYABLE(ScopedWriteLockSoftware);
+ public:
+ ScopedWriteLockSoftware(CCResourceProvider*, CCResourceProvider::ResourceId);
+ ~ScopedWriteLockSoftware();
+
+ SkCanvas* skCanvas() { return &m_skCanvas; }
+
+ private:
+ CCResourceProvider* m_resourceProvider;
+ CCResourceProvider::ResourceId m_resourceId;
+ SkBitmap m_skBitmap;
+ SkCanvas m_skCanvas;
+ };
+
+private:
+ struct Resource {
+ Resource()
+ : glId(0)
+ , pixels(0)
+ , pool(0)
+ , lockForReadCount(0)
+ , lockedForWrite(false)
+ , external(false)
+ , exported(false)
+ , size()
+ , format(0)
+ , type(static_cast<ResourceType>(0))
+ { }
+ Resource(unsigned textureId, int pool, const IntSize& size, GC3Denum format)
+ : glId(textureId)
+ , pixels(0)
+ , pool(pool)
+ , lockForReadCount(0)
+ , lockedForWrite(false)
+ , external(false)
+ , exported(false)
+ , size(size)
+ , format(format)
+ , type(GLTexture)
+ { }
+ Resource(uint8_t* pixels, int pool, const IntSize& size, GC3Denum format)
+ : glId(0)
+ , pixels(pixels)
+ , pool(pool)
+ , lockForReadCount(0)
+ , lockedForWrite(false)
+ , external(false)
+ , exported(false)
+ , size(size)
+ , format(format)
+ , type(Bitmap)
+ { }
+ unsigned glId;
+ uint8_t* pixels;
+ int pool;
+ int lockForReadCount;
+ bool lockedForWrite;
+ bool external;
+ bool exported;
+ IntSize size;
+ GC3Denum format;
+ ResourceType type;
+ };
+ typedef HashMap<ResourceId, Resource> ResourceMap;
+ struct Child {
+ int pool;
+ ResourceIdMap childToParentMap;
+ ResourceIdMap parentToChildMap;
+ };
+ typedef HashMap<int, Child> ChildMap;
+
+ explicit CCResourceProvider(CCGraphicsContext*);
+ bool initialize();
+
+ const Resource* lockForRead(ResourceId);
+ void unlockForRead(ResourceId);
+ const Resource* lockForWrite(ResourceId);
+ void unlockForWrite(ResourceId);
+ static void populateSkBitmapWithResource(SkBitmap*, const Resource*);
+
+ bool transferResource(WebKit::WebGraphicsContext3D*, ResourceId, TransferableResource*);
+ void trimMailboxDeque();
+
+ CCGraphicsContext* m_context;
+ ResourceId m_nextId;
+ ResourceMap m_resources;
+ int m_nextChild;
+ ChildMap m_children;
+
+ Deque<Mailbox> m_mailboxes;
+
+ ResourceType m_defaultResourceType;
+ bool m_useTextureStorageExt;
+ bool m_useTextureUsageHint;
+ bool m_useShallowFlush;
+ OwnPtr<LayerTextureSubImage> m_texSubImage;
+ int m_maxTextureSize;
+};
+
+}
+
+#endif
diff --git a/cc/CCResourceProviderTest.cpp b/cc/CCResourceProviderTest.cpp
new file mode 100644
index 0000000..b8fe586
--- /dev/null
+++ b/cc/CCResourceProviderTest.cpp
@@ -0,0 +1,536 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCResourceProvider.h"
+
+#include "CCGraphicsContext.h"
+#include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
+#include "CompositorFakeWebGraphicsContext3D.h"
+#include "Extensions3DChromium.h"
+#include "FakeWebCompositorOutputSurface.h"
+#include <gtest/gtest.h>
+#include <public/WebGraphicsContext3D.h>
+#include <wtf/HashMap.h>
+#include <wtf/OwnArrayPtr.h>
+#include <wtf/OwnPtr.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+namespace {
+
+size_t textureSize(const IntSize& size, WGC3Denum format)
+{
+ unsigned int componentsPerPixel = 4;
+ unsigned int bytesPerComponent = 1;
+ GraphicsContext3D::computeFormatAndTypeParameters(format, GraphicsContext3D::UNSIGNED_BYTE, &componentsPerPixel, &bytesPerComponent);
+ return size.width() * size.height() * componentsPerPixel * bytesPerComponent;
+}
+
+struct Texture {
+ Texture(const IntSize& size, WGC3Denum format)
+ : size(size)
+ , format(format)
+ , data(adoptArrayPtr(new uint8_t[textureSize(size, format)]))
+ {
+ }
+
+ IntSize size;
+ WGC3Denum format;
+ OwnArrayPtr<uint8_t> data;
+};
+
+// Shared data between multiple ResourceProviderContext. This contains mailbox
+// contents as well as information about sync points.
+class ContextSharedData {
+public:
+ static PassOwnPtr<ContextSharedData> create() { return adoptPtr(new ContextSharedData()); }
+
+ unsigned insertSyncPoint() { return m_nextSyncPoint++; }
+
+ void genMailbox(WGC3Dbyte* mailbox)
+ {
+ memset(mailbox, 0, sizeof(WGC3Dbyte[64]));
+ memcpy(mailbox, &m_nextMailBox, sizeof(m_nextMailBox));
+ ++m_nextMailBox;
+ }
+
+ void produceTexture(const WGC3Dbyte* mailboxName, unsigned syncPoint, PassOwnPtr<Texture> texture)
+ {
+ unsigned mailbox = 0;
+ memcpy(&mailbox, mailboxName, sizeof(mailbox));
+ ASSERT(mailbox && mailbox < m_nextMailBox);
+ m_textures.set(mailbox, texture);
+ ASSERT(m_syncPointForMailbox.get(mailbox) < syncPoint);
+ m_syncPointForMailbox.set(mailbox, syncPoint);
+ }
+
+ PassOwnPtr<Texture> consumeTexture(const WGC3Dbyte* mailboxName, unsigned syncPoint)
+ {
+ unsigned mailbox = 0;
+ memcpy(&mailbox, mailboxName, sizeof(mailbox));
+ ASSERT(mailbox && mailbox < m_nextMailBox);
+
+ // If the latest sync point the context has waited on is before the sync
+ // point for when the mailbox was set, pretend we never saw that
+ // produceTexture.
+ if (m_syncPointForMailbox.get(mailbox) < syncPoint)
+ return nullptr;
+ return m_textures.take(mailbox);
+ }
+
+private:
+ ContextSharedData()
+ : m_nextSyncPoint(1)
+ , m_nextMailBox(1)
+ { }
+
+ unsigned m_nextSyncPoint;
+ unsigned m_nextMailBox;
+ typedef HashMap<unsigned, OwnPtr<Texture> > TextureMap;
+ TextureMap m_textures;
+ HashMap<unsigned, unsigned> m_syncPointForMailbox;
+};
+
+class ResourceProviderContext : public CompositorFakeWebGraphicsContext3D {
+public:
+ static PassOwnPtr<ResourceProviderContext> create(ContextSharedData* sharedData) { return adoptPtr(new ResourceProviderContext(Attributes(), sharedData)); }
+
+ virtual unsigned insertSyncPoint()
+ {
+ unsigned syncPoint = m_sharedData->insertSyncPoint();
+ // Commit the produceTextureCHROMIUM calls at this point, so that
+ // they're associated with the sync point.
+ for (PendingProduceTextureList::iterator it = m_pendingProduceTextures.begin(); it != m_pendingProduceTextures.end(); ++it)
+ m_sharedData->produceTexture((*it)->mailbox, syncPoint, (*it)->texture.release());
+ m_pendingProduceTextures.clear();
+ return syncPoint;
+ }
+
+ virtual void waitSyncPoint(unsigned syncPoint)
+ {
+ m_lastWaitedSyncPoint = std::max(syncPoint, m_lastWaitedSyncPoint);
+ }
+
+ virtual void bindTexture(WGC3Denum target, WebGLId texture)
+ {
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+ ASSERT(!texture || m_textures.find(texture) != m_textures.end());
+ m_currentTexture = texture;
+ }
+
+ virtual WebGLId createTexture()
+ {
+ WebGLId id = CompositorFakeWebGraphicsContext3D::createTexture();
+ m_textures.add(id, nullptr);
+ return id;
+ }
+
+ virtual void deleteTexture(WebGLId id)
+ {
+ TextureMap::iterator it = m_textures.find(id);
+ ASSERT(it != m_textures.end());
+ m_textures.remove(it);
+ if (m_currentTexture == id)
+ m_currentTexture = 0;
+ }
+
+ virtual void texStorage2DEXT(WGC3Denum target, WGC3Dint levels, WGC3Duint internalformat,
+ WGC3Dint width, WGC3Dint height)
+ {
+ ASSERT(m_currentTexture);
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+ ASSERT(levels == 1);
+ WGC3Denum format = GraphicsContext3D::RGBA;
+ switch (internalformat) {
+ case Extensions3D::RGBA8_OES:
+ break;
+ case Extensions3DChromium::BGRA8_EXT:
+ format = Extensions3D::BGRA_EXT;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ allocateTexture(IntSize(width, height), format);
+ }
+
+ virtual void texImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border, WGC3Denum format, WGC3Denum type, const void* pixels)
+ {
+ ASSERT(m_currentTexture);
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+ ASSERT(!level);
+ ASSERT(internalformat == format);
+ ASSERT(!border);
+ ASSERT(type == GraphicsContext3D::UNSIGNED_BYTE);
+ allocateTexture(IntSize(width, height), format);
+ if (pixels)
+ setPixels(0, 0, width, height, pixels);
+ }
+
+ virtual void texSubImage2D(WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset, WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Denum type, const void* pixels)
+ {
+ ASSERT(m_currentTexture);
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+ ASSERT(!level);
+ ASSERT(m_textures.get(m_currentTexture));
+ ASSERT(m_textures.get(m_currentTexture)->format == format);
+ ASSERT(type == GraphicsContext3D::UNSIGNED_BYTE);
+ ASSERT(pixels);
+ setPixels(xoffset, yoffset, width, height, pixels);
+ }
+
+ virtual void genMailboxCHROMIUM(WGC3Dbyte* mailbox) { return m_sharedData->genMailbox(mailbox); }
+ virtual void produceTextureCHROMIUM(WGC3Denum target, const WGC3Dbyte* mailbox)
+ {
+ ASSERT(m_currentTexture);
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+
+ // Delay movind the texture into the mailbox until the next
+ // insertSyncPoint, so that it is not visible to other contexts that
+ // haven't waited on that sync point.
+ OwnPtr<PendingProduceTexture> pending(adoptPtr(new PendingProduceTexture));
+ memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox));
+ pending->texture = m_textures.take(m_currentTexture);
+ m_textures.set(m_currentTexture, nullptr);
+ m_pendingProduceTextures.append(pending.release());
+ }
+
+ virtual void consumeTextureCHROMIUM(WGC3Denum target, const WGC3Dbyte* mailbox)
+ {
+ ASSERT(m_currentTexture);
+ ASSERT(target == GraphicsContext3D::TEXTURE_2D);
+ m_textures.set(m_currentTexture, m_sharedData->consumeTexture(mailbox, m_lastWaitedSyncPoint));
+ }
+
+ void getPixels(const IntSize& size, WGC3Denum format, uint8_t* pixels)
+ {
+ ASSERT(m_currentTexture);
+ Texture* texture = m_textures.get(m_currentTexture);
+ ASSERT(texture);
+ ASSERT(texture->size == size);
+ ASSERT(texture->format == format);
+ memcpy(pixels, texture->data.get(), textureSize(size, format));
+ }
+
+ int textureCount()
+ {
+ return m_textures.size();
+ }
+
+protected:
+ ResourceProviderContext(const Attributes& attrs, ContextSharedData* sharedData)
+ : CompositorFakeWebGraphicsContext3D(attrs)
+ , m_sharedData(sharedData)
+ , m_currentTexture(0)
+ , m_lastWaitedSyncPoint(0)
+ { }
+
+private:
+ void allocateTexture(const IntSize& size, WGC3Denum format)
+ {
+ ASSERT(m_currentTexture);
+ m_textures.set(m_currentTexture, adoptPtr(new Texture(size, format)));
+ }
+
+ void setPixels(int xoffset, int yoffset, int width, int height, const void* pixels)
+ {
+ ASSERT(m_currentTexture);
+ Texture* texture = m_textures.get(m_currentTexture);
+ ASSERT(texture);
+ ASSERT(xoffset >= 0 && xoffset+width <= texture->size.width());
+ ASSERT(yoffset >= 0 && yoffset+height <= texture->size.height());
+ ASSERT(pixels);
+ size_t inPitch = textureSize(IntSize(width, 1), texture->format);
+ size_t outPitch = textureSize(IntSize(texture->size.width(), 1), texture->format);
+ uint8_t* dest = texture->data.get() + yoffset * outPitch + textureSize(IntSize(xoffset, 1), texture->format);
+ const uint8_t* src = static_cast<const uint8_t*>(pixels);
+ for (int i = 0; i < height; ++i) {
+ memcpy(dest, src, inPitch);
+ dest += outPitch;
+ src += inPitch;
+ }
+ }
+
+ typedef HashMap<WebGLId, OwnPtr<Texture> > TextureMap;
+ struct PendingProduceTexture {
+ WGC3Dbyte mailbox[64];
+ OwnPtr<Texture> texture;
+ };
+ typedef Deque<OwnPtr<PendingProduceTexture> > PendingProduceTextureList;
+ ContextSharedData* m_sharedData;
+ WebGLId m_currentTexture;
+ TextureMap m_textures;
+ unsigned m_lastWaitedSyncPoint;
+ PendingProduceTextureList m_pendingProduceTextures;
+};
+
+class CCResourceProviderTest : public testing::TestWithParam<CCResourceProvider::ResourceType> {
+public:
+ CCResourceProviderTest()
+ : m_sharedData(ContextSharedData::create())
+ , m_context(FakeWebCompositorOutputSurface::create(ResourceProviderContext::create(m_sharedData.get())))
+ , m_resourceProvider(CCResourceProvider::create(m_context.get()))
+ {
+ m_resourceProvider->setDefaultResourceType(GetParam());
+ }
+
+ ResourceProviderContext* context() { return static_cast<ResourceProviderContext*>(m_context->context3D()); }
+
+ void getResourcePixels(CCResourceProvider::ResourceId id, const IntSize& size, WGC3Denum format, uint8_t* pixels)
+ {
+ if (GetParam() == CCResourceProvider::GLTexture) {
+ CCResourceProvider::ScopedReadLockGL lockGL(m_resourceProvider.get(), id);
+ ASSERT_NE(0U, lockGL.textureId());
+ context()->bindTexture(GraphicsContext3D::TEXTURE_2D, lockGL.textureId());
+ context()->getPixels(size, format, pixels);
+ } else if (GetParam() == CCResourceProvider::Bitmap) {
+ CCResourceProvider::ScopedReadLockSoftware lockSoftware(m_resourceProvider.get(), id);
+ memcpy(pixels, lockSoftware.skBitmap()->getPixels(), lockSoftware.skBitmap()->getSize());
+ }
+ }
+
+ void expectNumResources(int count)
+ {
+ EXPECT_EQ(count, static_cast<int>(m_resourceProvider->numResources()));
+ if (GetParam() == CCResourceProvider::GLTexture)
+ EXPECT_EQ(count, context()->textureCount());
+ }
+
+protected:
+ DebugScopedSetImplThread implThread;
+ OwnPtr<ContextSharedData> m_sharedData;
+ OwnPtr<CCGraphicsContext> m_context;
+ OwnPtr<CCResourceProvider> m_resourceProvider;
+};
+
+TEST_P(CCResourceProviderTest, Basic)
+{
+ IntSize size(1, 1);
+ WGC3Denum format = GraphicsContext3D::RGBA;
+ int pool = 1;
+ size_t pixelSize = textureSize(size, format);
+ ASSERT_EQ(4U, pixelSize);
+
+ CCResourceProvider::ResourceId id = m_resourceProvider->createResource(pool, size, format, CCResourceProvider::TextureUsageAny);
+ expectNumResources(1);
+
+ uint8_t data[4] = {1, 2, 3, 4};
+ IntRect rect(IntPoint(), size);
+ m_resourceProvider->upload(id, data, rect, rect, IntSize());
+
+ uint8_t result[4] = {0};
+ getResourcePixels(id, size, format, result);
+ EXPECT_EQ(0, memcmp(data, result, pixelSize));
+
+ m_resourceProvider->deleteResource(id);
+ expectNumResources(0);
+}
+
+TEST_P(CCResourceProviderTest, DeleteOwnedResources)
+{
+ IntSize size(1, 1);
+ WGC3Denum format = GraphicsContext3D::RGBA;
+ int pool = 1;
+
+ const int count = 3;
+ for (int i = 0; i < count; ++i)
+ m_resourceProvider->createResource(pool, size, format, CCResourceProvider::TextureUsageAny);
+ expectNumResources(3);
+
+ m_resourceProvider->deleteOwnedResources(pool+1);
+ expectNumResources(3);
+
+ m_resourceProvider->deleteOwnedResources(pool);
+ expectNumResources(0);
+}
+
+TEST_P(CCResourceProviderTest, Upload)
+{
+ IntSize size(2, 2);
+ WGC3Denum format = GraphicsContext3D::RGBA;
+ int pool = 1;
+ size_t pixelSize = textureSize(size, format);
+ ASSERT_EQ(16U, pixelSize);
+
+ CCResourceProvider::ResourceId id = m_resourceProvider->createResource(pool, size, format, CCResourceProvider::TextureUsageAny);
+
+ uint8_t image[16] = {0};
+ IntRect imageRect(IntPoint(), size);
+ m_resourceProvider->upload(id, image, imageRect, imageRect, IntSize());
+
+ for (uint8_t i = 0 ; i < pixelSize; ++i)
+ image[i] = i;
+
+ uint8_t result[16] = {0};
+ {
+ IntRect sourceRect(0, 0, 1, 1);
+ IntSize destOffset(0, 0);
+ m_resourceProvider->upload(id, image, imageRect, sourceRect, destOffset);
+
+ uint8_t expected[16] = {0, 1, 2, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ getResourcePixels(id, size, format, result);
+ EXPECT_EQ(0, memcmp(expected, result, pixelSize));
+ }
+ {
+ IntRect sourceRect(0, 0, 1, 1);
+ IntSize destOffset(1, 1);
+ m_resourceProvider->upload(id, image, imageRect, sourceRect, destOffset);
+
+ uint8_t expected[16] = {0, 1, 2, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3};
+ getResourcePixels(id, size, format, result);
+ EXPECT_EQ(0, memcmp(expected, result, pixelSize));
+ }
+ {
+ IntRect sourceRect(1, 0, 1, 1);
+ IntSize destOffset(0, 1);
+ m_resourceProvider->upload(id, image, imageRect, sourceRect, destOffset);
+
+ uint8_t expected[16] = {0, 1, 2, 3, 0, 0, 0, 0,
+ 4, 5, 6, 7, 0, 1, 2, 3};
+ getResourcePixels(id, size, format, result);
+ EXPECT_EQ(0, memcmp(expected, result, pixelSize));
+ }
+ {
+ IntRect offsetImageRect(IntPoint(100, 100), size);
+ IntRect sourceRect(100, 100, 1, 1);
+ IntSize destOffset(1, 0);
+ m_resourceProvider->upload(id, image, offsetImageRect, sourceRect, destOffset);
+
+ uint8_t expected[16] = {0, 1, 2, 3, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3};
+ getResourcePixels(id, size, format, result);
+ EXPECT_EQ(0, memcmp(expected, result, pixelSize));
+ }
+
+
+ m_resourceProvider->deleteResource(id);
+}
+
+TEST_P(CCResourceProviderTest, TransferResources)
+{
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != CCResourceProvider::GLTexture)
+ return;
+
+ OwnPtr<CCGraphicsContext> childContext(FakeWebCompositorOutputSurface::create(ResourceProviderContext::create(m_sharedData.get())));
+ OwnPtr<CCResourceProvider> childResourceProvider(CCResourceProvider::create(childContext.get()));
+
+ IntSize size(1, 1);
+ WGC3Denum format = GraphicsContext3D::RGBA;
+ int pool = 1;
+ size_t pixelSize = textureSize(size, format);
+ ASSERT_EQ(4U, pixelSize);
+
+ CCResourceProvider::ResourceId id1 = childResourceProvider->createResource(pool, size, format, CCResourceProvider::TextureUsageAny);
+ uint8_t data1[4] = {1, 2, 3, 4};
+ IntRect rect(IntPoint(), size);
+ childResourceProvider->upload(id1, data1, rect, rect, IntSize());
+
+ CCResourceProvider::ResourceId id2 = childResourceProvider->createResource(pool, size, format, CCResourceProvider::TextureUsageAny);
+ uint8_t data2[4] = {5, 5, 5, 5};
+ childResourceProvider->upload(id2, data2, rect, rect, IntSize());
+
+ int childPool = 2;
+ int childId = m_resourceProvider->createChild(childPool);
+
+ {
+ // Transfer some resources to the parent.
+ CCResourceProvider::ResourceIdArray resourceIdsToTransfer;
+ resourceIdsToTransfer.append(id1);
+ resourceIdsToTransfer.append(id2);
+ CCResourceProvider::TransferableResourceList list = childResourceProvider->prepareSendToParent(resourceIdsToTransfer);
+ EXPECT_NE(0u, list.syncPoint);
+ EXPECT_EQ(2u, list.resources.size());
+ EXPECT_TRUE(childResourceProvider->inUseByConsumer(id1));
+ EXPECT_TRUE(childResourceProvider->inUseByConsumer(id2));
+ m_resourceProvider->receiveFromChild(childId, list);
+ }
+
+ EXPECT_EQ(2u, m_resourceProvider->numResources());
+ EXPECT_EQ(2u, m_resourceProvider->mailboxCount());
+ CCResourceProvider::ResourceIdMap resourceMap = m_resourceProvider->getChildToParentMap(childId);
+ CCResourceProvider::ResourceId mappedId1 = resourceMap.get(id1);
+ CCResourceProvider::ResourceId mappedId2 = resourceMap.get(id2);
+ EXPECT_NE(0u, mappedId1);
+ EXPECT_NE(0u, mappedId2);
+ EXPECT_FALSE(m_resourceProvider->inUseByConsumer(id1));
+ EXPECT_FALSE(m_resourceProvider->inUseByConsumer(id2));
+
+ uint8_t result[4] = {0};
+ getResourcePixels(mappedId1, size, format, result);
+ EXPECT_EQ(0, memcmp(data1, result, pixelSize));
+
+ getResourcePixels(mappedId2, size, format, result);
+ EXPECT_EQ(0, memcmp(data2, result, pixelSize));
+
+ {
+ // Check that transfering again the same resource from the child to the
+ // parent is a noop.
+ CCResourceProvider::ResourceIdArray resourceIdsToTransfer;
+ resourceIdsToTransfer.append(id1);
+ CCResourceProvider::TransferableResourceList list = childResourceProvider->prepareSendToParent(resourceIdsToTransfer);
+ EXPECT_EQ(0u, list.syncPoint);
+ EXPECT_EQ(0u, list.resources.size());
+ }
+
+ {
+ // Transfer resources back from the parent to the child.
+ CCResourceProvider::ResourceIdArray resourceIdsToTransfer;
+ resourceIdsToTransfer.append(mappedId1);
+ resourceIdsToTransfer.append(mappedId2);
+ CCResourceProvider::TransferableResourceList list = m_resourceProvider->prepareSendToChild(childId, resourceIdsToTransfer);
+ EXPECT_NE(0u, list.syncPoint);
+ EXPECT_EQ(2u, list.resources.size());
+ childResourceProvider->receiveFromParent(list);
+ }
+ EXPECT_EQ(0u, m_resourceProvider->mailboxCount());
+ EXPECT_EQ(2u, childResourceProvider->mailboxCount());
+ EXPECT_FALSE(childResourceProvider->inUseByConsumer(id1));
+ EXPECT_FALSE(childResourceProvider->inUseByConsumer(id2));
+
+ ResourceProviderContext* childContext3D = static_cast<ResourceProviderContext*>(childContext->context3D());
+ {
+ CCResourceProvider::ScopedReadLockGL lock(childResourceProvider.get(), id1);
+ ASSERT_NE(0U, lock.textureId());
+ childContext3D->bindTexture(GraphicsContext3D::TEXTURE_2D, lock.textureId());
+ childContext3D->getPixels(size, format, result);
+ EXPECT_EQ(0, memcmp(data1, result, pixelSize));
+ }
+ {
+ CCResourceProvider::ScopedReadLockGL lock(childResourceProvider.get(), id2);
+ ASSERT_NE(0U, lock.textureId());
+ childContext3D->bindTexture(GraphicsContext3D::TEXTURE_2D, lock.textureId());
+ childContext3D->getPixels(size, format, result);
+ EXPECT_EQ(0, memcmp(data2, result, pixelSize));
+ }
+
+ {
+ // Transfer resources to the parent again.
+ CCResourceProvider::ResourceIdArray resourceIdsToTransfer;
+ resourceIdsToTransfer.append(id1);
+ resourceIdsToTransfer.append(id2);
+ CCResourceProvider::TransferableResourceList list = childResourceProvider->prepareSendToParent(resourceIdsToTransfer);
+ EXPECT_NE(0u, list.syncPoint);
+ EXPECT_EQ(2u, list.resources.size());
+ EXPECT_TRUE(childResourceProvider->inUseByConsumer(id1));
+ EXPECT_TRUE(childResourceProvider->inUseByConsumer(id2));
+ m_resourceProvider->receiveFromChild(childId, list);
+ }
+
+ EXPECT_EQ(2u, m_resourceProvider->numResources());
+ m_resourceProvider->destroyChild(childId);
+ EXPECT_EQ(0u, m_resourceProvider->numResources());
+ EXPECT_EQ(0u, m_resourceProvider->mailboxCount());
+}
+
+INSTANTIATE_TEST_CASE_P(CCResourceProviderTests,
+ CCResourceProviderTest,
+ ::testing::Values(CCResourceProvider::GLTexture,
+ CCResourceProvider::Bitmap));
+
+} // namespace
diff --git a/cc/CCScheduler.cpp b/cc/CCScheduler.cpp
new file mode 100644
index 0000000..1840b4a
--- /dev/null
+++ b/cc/CCScheduler.cpp
@@ -0,0 +1,191 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScheduler.h"
+
+#include "TraceEvent.h"
+
+namespace WebCore {
+
+CCScheduler::CCScheduler(CCSchedulerClient* client, PassOwnPtr<CCFrameRateController> frameRateController)
+ : m_client(client)
+ , m_frameRateController(frameRateController)
+ , m_updateMoreResourcesPending(false)
+{
+ ASSERT(m_client);
+ m_frameRateController->setClient(this);
+ m_frameRateController->setActive(m_stateMachine.vsyncCallbackNeeded());
+}
+
+CCScheduler::~CCScheduler()
+{
+ m_frameRateController->setActive(false);
+}
+
+void CCScheduler::setCanBeginFrame(bool can)
+{
+ m_stateMachine.setCanBeginFrame(can);
+ processScheduledActions();
+}
+
+void CCScheduler::setVisible(bool visible)
+{
+ m_stateMachine.setVisible(visible);
+ processScheduledActions();
+}
+
+void CCScheduler::setNeedsCommit()
+{
+ m_stateMachine.setNeedsCommit();
+ processScheduledActions();
+}
+
+void CCScheduler::setNeedsForcedCommit()
+{
+ m_stateMachine.setNeedsForcedCommit();
+ processScheduledActions();
+}
+
+void CCScheduler::setNeedsRedraw()
+{
+ m_stateMachine.setNeedsRedraw();
+ processScheduledActions();
+}
+
+void CCScheduler::setNeedsForcedRedraw()
+{
+ m_stateMachine.setNeedsForcedRedraw();
+ processScheduledActions();
+}
+
+void CCScheduler::setMainThreadNeedsLayerTextures()
+{
+ m_stateMachine.setMainThreadNeedsLayerTextures();
+ processScheduledActions();
+}
+
+void CCScheduler::beginFrameComplete()
+{
+ TRACE_EVENT0("cc", "CCScheduler::beginFrameComplete");
+ m_stateMachine.beginFrameComplete();
+ processScheduledActions();
+}
+
+void CCScheduler::beginFrameAborted()
+{
+ TRACE_EVENT0("cc", "CCScheduler::beginFrameAborted");
+ m_stateMachine.beginFrameAborted();
+ processScheduledActions();
+}
+
+void CCScheduler::setMaxFramesPending(int maxFramesPending)
+{
+ m_frameRateController->setMaxFramesPending(maxFramesPending);
+}
+
+void CCScheduler::didSwapBuffersComplete()
+{
+ TRACE_EVENT0("cc", "CCScheduler::didSwapBuffersComplete");
+ m_frameRateController->didFinishFrame();
+}
+
+void CCScheduler::didLoseContext()
+{
+ TRACE_EVENT0("cc", "CCScheduler::didLoseContext");
+ m_frameRateController->didAbortAllPendingFrames();
+ m_stateMachine.didLoseContext();
+ processScheduledActions();
+}
+
+void CCScheduler::didRecreateContext()
+{
+ TRACE_EVENT0("cc", "CCScheduler::didRecreateContext");
+ m_stateMachine.didRecreateContext();
+ processScheduledActions();
+}
+
+void CCScheduler::setTimebaseAndInterval(double timebase, double intervalSeconds)
+{
+ m_frameRateController->setTimebaseAndInterval(timebase, intervalSeconds);
+}
+
+void CCScheduler::vsyncTick()
+{
+ if (m_updateMoreResourcesPending) {
+ m_updateMoreResourcesPending = false;
+ m_stateMachine.beginUpdateMoreResourcesComplete(m_client->hasMoreResourceUpdates());
+ }
+ TRACE_EVENT0("cc", "CCScheduler::vsyncTick");
+
+ m_stateMachine.didEnterVSync();
+ processScheduledActions();
+ m_stateMachine.didLeaveVSync();
+}
+
+CCSchedulerStateMachine::Action CCScheduler::nextAction()
+{
+ m_stateMachine.setCanDraw(m_client->canDraw());
+ return m_stateMachine.nextAction();
+}
+
+void CCScheduler::processScheduledActions()
+{
+ // Early out so we don't spam TRACE_EVENTS with useless processScheduledActions.
+ if (nextAction() == CCSchedulerStateMachine::ACTION_NONE) {
+ m_frameRateController->setActive(m_stateMachine.vsyncCallbackNeeded());
+ return;
+ }
+
+ // This function can re-enter itself. For example, draw may call
+ // setNeedsCommit. Proceeed with caution.
+ CCSchedulerStateMachine::Action action;
+ do {
+ action = nextAction();
+ m_stateMachine.updateState(action);
+ TRACE_EVENT1("cc", "CCScheduler::processScheduledActions()", "action", action);
+
+ switch (action) {
+ case CCSchedulerStateMachine::ACTION_NONE:
+ break;
+ case CCSchedulerStateMachine::ACTION_BEGIN_FRAME:
+ m_client->scheduledActionBeginFrame();
+ break;
+ case CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES:
+ if (m_client->hasMoreResourceUpdates()) {
+ m_client->scheduledActionUpdateMoreResources(m_frameRateController->nextTickTimeIfActivated());
+ m_updateMoreResourcesPending = true;
+ } else
+ m_stateMachine.beginUpdateMoreResourcesComplete(false);
+ break;
+ case CCSchedulerStateMachine::ACTION_COMMIT:
+ m_client->scheduledActionCommit();
+ break;
+ case CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: {
+ CCScheduledActionDrawAndSwapResult result = m_client->scheduledActionDrawAndSwapIfPossible();
+ m_stateMachine.didDrawIfPossibleCompleted(result.didDraw);
+ if (result.didSwap)
+ m_frameRateController->didBeginFrame();
+ break;
+ }
+ case CCSchedulerStateMachine::ACTION_DRAW_FORCED: {
+ CCScheduledActionDrawAndSwapResult result = m_client->scheduledActionDrawAndSwapForced();
+ if (result.didSwap)
+ m_frameRateController->didBeginFrame();
+ break;
+ } case CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION:
+ m_client->scheduledActionBeginContextRecreation();
+ break;
+ case CCSchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
+ m_client->scheduledActionAcquireLayerTexturesForMainThread();
+ break;
+ }
+ } while (action != CCSchedulerStateMachine::ACTION_NONE);
+
+ // Activate or deactivate the frame rate controller.
+ m_frameRateController->setActive(m_stateMachine.vsyncCallbackNeeded());
+}
+
+}
diff --git a/cc/CCScheduler.h b/cc/CCScheduler.h
new file mode 100644
index 0000000..10fce73
--- /dev/null
+++ b/cc/CCScheduler.h
@@ -0,0 +1,107 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScheduler_h
+#define CCScheduler_h
+
+#include "CCFrameRateController.h"
+#include "CCSchedulerStateMachine.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCThread;
+
+struct CCScheduledActionDrawAndSwapResult {
+ CCScheduledActionDrawAndSwapResult()
+ : didDraw(false)
+ , didSwap(false)
+ {
+ }
+ CCScheduledActionDrawAndSwapResult(bool didDraw, bool didSwap)
+ : didDraw(didDraw)
+ , didSwap(didSwap)
+ {
+ }
+ bool didDraw;
+ bool didSwap;
+};
+
+class CCSchedulerClient {
+public:
+ virtual bool canDraw() = 0;
+ virtual bool hasMoreResourceUpdates() const = 0;
+
+ virtual void scheduledActionBeginFrame() = 0;
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() = 0;
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() = 0;
+ virtual void scheduledActionUpdateMoreResources(double monotonicTimeLimit) = 0;
+ virtual void scheduledActionCommit() = 0;
+ virtual void scheduledActionBeginContextRecreation() = 0;
+ virtual void scheduledActionAcquireLayerTexturesForMainThread() = 0;
+
+protected:
+ virtual ~CCSchedulerClient() { }
+};
+
+class CCScheduler : CCFrameRateControllerClient {
+ WTF_MAKE_NONCOPYABLE(CCScheduler);
+public:
+ static PassOwnPtr<CCScheduler> create(CCSchedulerClient* client, PassOwnPtr<CCFrameRateController> frameRateController)
+ {
+ return adoptPtr(new CCScheduler(client, frameRateController));
+ }
+
+ virtual ~CCScheduler();
+
+ void setCanBeginFrame(bool);
+
+ void setVisible(bool);
+
+ void setNeedsCommit();
+
+ // Like setNeedsCommit(), but ensures a commit will definitely happen even if we are not visible.
+ void setNeedsForcedCommit();
+
+ void setNeedsRedraw();
+
+ void setMainThreadNeedsLayerTextures();
+
+ // Like setNeedsRedraw(), but ensures the draw will definitely happen even if we are not visible.
+ void setNeedsForcedRedraw();
+
+ void beginFrameComplete();
+ void beginFrameAborted();
+
+ void setMaxFramesPending(int);
+ void didSwapBuffersComplete();
+
+ void didLoseContext();
+ void didRecreateContext();
+
+ bool commitPending() const { return m_stateMachine.commitPending(); }
+ bool redrawPending() const { return m_stateMachine.redrawPending(); }
+
+ void setTimebaseAndInterval(double timebase, double intervalSeconds);
+
+ // CCFrameRateControllerClient implementation
+ virtual void vsyncTick() OVERRIDE;
+
+private:
+ CCScheduler(CCSchedulerClient*, PassOwnPtr<CCFrameRateController>);
+
+ CCSchedulerStateMachine::Action nextAction();
+ void processScheduledActions();
+
+ CCSchedulerClient* m_client;
+ OwnPtr<CCFrameRateController> m_frameRateController;
+ CCSchedulerStateMachine m_stateMachine;
+ bool m_updateMoreResourcesPending;
+};
+
+}
+
+#endif // CCScheduler_h
diff --git a/cc/CCSchedulerStateMachine.cpp b/cc/CCSchedulerStateMachine.cpp
new file mode 100644
index 0000000..eda92ae
--- /dev/null
+++ b/cc/CCSchedulerStateMachine.cpp
@@ -0,0 +1,316 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSchedulerStateMachine.h"
+
+namespace WebCore {
+
+CCSchedulerStateMachine::CCSchedulerStateMachine()
+ : m_commitState(COMMIT_STATE_IDLE)
+ , m_currentFrameNumber(0)
+ , m_lastFrameNumberWhereDrawWasCalled(-1)
+ , m_consecutiveFailedDraws(0)
+ , m_maximumNumberOfFailedDrawsBeforeDrawIsForced(3)
+ , m_needsRedraw(false)
+ , m_needsForcedRedraw(false)
+ , m_needsForcedRedrawAfterNextCommit(false)
+ , m_needsCommit(false)
+ , m_needsForcedCommit(false)
+ , m_mainThreadNeedsLayerTextures(false)
+ , m_updateMoreResourcesPending(false)
+ , m_insideVSync(false)
+ , m_visible(false)
+ , m_canBeginFrame(false)
+ , m_canDraw(true)
+ , m_drawIfPossibleFailed(false)
+ , m_textureState(LAYER_TEXTURE_STATE_UNLOCKED)
+ , m_contextState(CONTEXT_ACTIVE)
+{
+}
+
+bool CCSchedulerStateMachine::hasDrawnThisFrame() const
+{
+ return m_currentFrameNumber == m_lastFrameNumberWhereDrawWasCalled;
+}
+
+bool CCSchedulerStateMachine::drawSuspendedUntilCommit() const
+{
+ if (!m_canDraw)
+ return true;
+ if (!m_visible)
+ return true;
+ if (m_textureState == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
+ return true;
+ return false;
+}
+
+bool CCSchedulerStateMachine::scheduledToDraw() const
+{
+ if (!m_needsRedraw)
+ return false;
+ if (drawSuspendedUntilCommit())
+ return false;
+ return true;
+}
+
+bool CCSchedulerStateMachine::shouldDraw() const
+{
+ if (m_needsForcedRedraw)
+ return true;
+
+ if (!scheduledToDraw())
+ return false;
+ if (!m_insideVSync)
+ return false;
+ if (hasDrawnThisFrame())
+ return false;
+ if (m_contextState != CONTEXT_ACTIVE)
+ return false;
+ return true;
+}
+
+bool CCSchedulerStateMachine::shouldAcquireLayerTexturesForMainThread() const
+{
+ if (!m_mainThreadNeedsLayerTextures)
+ return false;
+ if (m_textureState == LAYER_TEXTURE_STATE_UNLOCKED)
+ return true;
+ ASSERT(m_textureState == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
+ // Transfer the lock from impl thread to main thread immediately if the
+ // impl thread is not even scheduled to draw. Guards against deadlocking.
+ if (!scheduledToDraw())
+ return true;
+ if (!vsyncCallbackNeeded())
+ return true;
+ return false;
+}
+
+CCSchedulerStateMachine::Action CCSchedulerStateMachine::nextAction() const
+{
+ if (shouldAcquireLayerTexturesForMainThread())
+ return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
+ switch (m_commitState) {
+ case COMMIT_STATE_IDLE:
+ if (m_contextState != CONTEXT_ACTIVE && m_needsForcedRedraw)
+ return ACTION_DRAW_FORCED;
+ if (m_contextState != CONTEXT_ACTIVE && m_needsForcedCommit)
+ return ACTION_BEGIN_FRAME;
+ if (m_contextState == CONTEXT_LOST)
+ return ACTION_BEGIN_CONTEXT_RECREATION;
+ if (m_contextState == CONTEXT_RECREATING)
+ return ACTION_NONE;
+ if (shouldDraw())
+ return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
+ if (m_needsCommit && ((m_visible && m_canBeginFrame) || m_needsForcedCommit))
+ return ACTION_BEGIN_FRAME;
+ return ACTION_NONE;
+
+ case COMMIT_STATE_FRAME_IN_PROGRESS:
+ if (shouldDraw())
+ return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
+ return ACTION_NONE;
+
+ case COMMIT_STATE_UPDATING_RESOURCES:
+ if (shouldDraw())
+ return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
+ if (!m_updateMoreResourcesPending)
+ return ACTION_BEGIN_UPDATE_MORE_RESOURCES;
+ return ACTION_NONE;
+
+ case COMMIT_STATE_READY_TO_COMMIT:
+ return ACTION_COMMIT;
+
+ case COMMIT_STATE_WAITING_FOR_FIRST_DRAW:
+ if (shouldDraw() || m_contextState == CONTEXT_LOST)
+ return m_needsForcedRedraw ? ACTION_DRAW_FORCED : ACTION_DRAW_IF_POSSIBLE;
+ // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If m_canDraw is false
+ // or textures are not available, proceed to the next step (similar as in COMMIT_STATE_IDLE).
+ bool canCommit = m_visible || m_needsForcedCommit;
+ if (m_needsCommit && canCommit && drawSuspendedUntilCommit())
+ return ACTION_BEGIN_FRAME;
+ return ACTION_NONE;
+ }
+ ASSERT_NOT_REACHED();
+ return ACTION_NONE;
+}
+
+void CCSchedulerStateMachine::updateState(Action action)
+{
+ switch (action) {
+ case ACTION_NONE:
+ return;
+
+ case ACTION_BEGIN_FRAME:
+ ASSERT(m_visible || m_needsForcedCommit);
+ m_commitState = COMMIT_STATE_FRAME_IN_PROGRESS;
+ m_needsCommit = false;
+ m_needsForcedCommit = false;
+ return;
+
+ case ACTION_BEGIN_UPDATE_MORE_RESOURCES:
+ ASSERT(m_commitState == COMMIT_STATE_UPDATING_RESOURCES);
+ m_updateMoreResourcesPending = true;
+ return;
+
+ case ACTION_COMMIT:
+ if ((m_needsCommit || !m_visible) && !m_needsForcedCommit)
+ m_commitState = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ else
+ m_commitState = COMMIT_STATE_IDLE;
+
+ m_needsRedraw = true;
+ if (m_drawIfPossibleFailed)
+ m_lastFrameNumberWhereDrawWasCalled = -1;
+
+ if (m_needsForcedRedrawAfterNextCommit) {
+ m_needsForcedRedrawAfterNextCommit = false;
+ m_needsForcedRedraw = true;
+ }
+
+ m_textureState = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
+ return;
+
+ case ACTION_DRAW_FORCED:
+ case ACTION_DRAW_IF_POSSIBLE:
+ m_needsRedraw = false;
+ m_needsForcedRedraw = false;
+ m_drawIfPossibleFailed = false;
+ if (m_insideVSync)
+ m_lastFrameNumberWhereDrawWasCalled = m_currentFrameNumber;
+ if (m_commitState == COMMIT_STATE_WAITING_FOR_FIRST_DRAW)
+ m_commitState = COMMIT_STATE_IDLE;
+ if (m_textureState == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
+ m_textureState = LAYER_TEXTURE_STATE_UNLOCKED;
+ return;
+
+ case ACTION_BEGIN_CONTEXT_RECREATION:
+ ASSERT(m_commitState == COMMIT_STATE_IDLE);
+ ASSERT(m_contextState == CONTEXT_LOST);
+ m_contextState = CONTEXT_RECREATING;
+ return;
+
+ case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
+ m_textureState = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD;
+ m_mainThreadNeedsLayerTextures = false;
+ if (m_commitState != COMMIT_STATE_FRAME_IN_PROGRESS)
+ m_needsCommit = true;
+ return;
+ }
+}
+
+void CCSchedulerStateMachine::setMainThreadNeedsLayerTextures()
+{
+ ASSERT(!m_mainThreadNeedsLayerTextures);
+ ASSERT(m_textureState != LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD);
+ m_mainThreadNeedsLayerTextures = true;
+}
+
+bool CCSchedulerStateMachine::vsyncCallbackNeeded() const
+{
+ if (!m_visible || m_contextState != CONTEXT_ACTIVE) {
+ if (m_needsForcedRedraw || m_commitState == COMMIT_STATE_UPDATING_RESOURCES)
+ return true;
+
+ return false;
+ }
+
+ return m_needsRedraw || m_needsForcedRedraw || m_commitState == COMMIT_STATE_UPDATING_RESOURCES;
+}
+
+void CCSchedulerStateMachine::didEnterVSync()
+{
+ m_insideVSync = true;
+}
+
+void CCSchedulerStateMachine::didLeaveVSync()
+{
+ m_currentFrameNumber++;
+ m_insideVSync = false;
+}
+
+void CCSchedulerStateMachine::setVisible(bool visible)
+{
+ m_visible = visible;
+}
+
+void CCSchedulerStateMachine::setNeedsRedraw()
+{
+ m_needsRedraw = true;
+}
+
+void CCSchedulerStateMachine::setNeedsForcedRedraw()
+{
+ m_needsForcedRedraw = true;
+}
+
+void CCSchedulerStateMachine::didDrawIfPossibleCompleted(bool success)
+{
+ m_drawIfPossibleFailed = !success;
+ if (m_drawIfPossibleFailed) {
+ m_needsRedraw = true;
+ m_needsCommit = true;
+ m_consecutiveFailedDraws++;
+ if (m_consecutiveFailedDraws >= m_maximumNumberOfFailedDrawsBeforeDrawIsForced) {
+ m_consecutiveFailedDraws = 0;
+ // We need to force a draw, but it doesn't make sense to do this until
+ // we've committed and have new textures.
+ m_needsForcedRedrawAfterNextCommit = true;
+ }
+ } else
+ m_consecutiveFailedDraws = 0;
+}
+
+void CCSchedulerStateMachine::setNeedsCommit()
+{
+ m_needsCommit = true;
+}
+
+void CCSchedulerStateMachine::setNeedsForcedCommit()
+{
+ m_needsForcedCommit = true;
+}
+
+void CCSchedulerStateMachine::beginFrameComplete()
+{
+ ASSERT(m_commitState == COMMIT_STATE_FRAME_IN_PROGRESS);
+ m_commitState = COMMIT_STATE_UPDATING_RESOURCES;
+}
+
+void CCSchedulerStateMachine::beginFrameAborted()
+{
+ ASSERT(m_commitState == COMMIT_STATE_FRAME_IN_PROGRESS);
+ m_commitState = COMMIT_STATE_IDLE;
+}
+
+void CCSchedulerStateMachine::beginUpdateMoreResourcesComplete(bool morePending)
+{
+ ASSERT(m_commitState == COMMIT_STATE_UPDATING_RESOURCES);
+ ASSERT(m_updateMoreResourcesPending);
+ m_updateMoreResourcesPending = false;
+ if (!morePending)
+ m_commitState = COMMIT_STATE_READY_TO_COMMIT;
+}
+
+void CCSchedulerStateMachine::didLoseContext()
+{
+ if (m_contextState == CONTEXT_LOST || m_contextState == CONTEXT_RECREATING)
+ return;
+ m_contextState = CONTEXT_LOST;
+}
+
+void CCSchedulerStateMachine::didRecreateContext()
+{
+ ASSERT(m_contextState == CONTEXT_RECREATING);
+ m_contextState = CONTEXT_ACTIVE;
+ setNeedsCommit();
+}
+
+void CCSchedulerStateMachine::setMaximumNumberOfFailedDrawsBeforeDrawIsForced(int numDraws)
+{
+ m_maximumNumberOfFailedDrawsBeforeDrawIsForced = numDraws;
+}
+
+}
diff --git a/cc/CCSchedulerStateMachine.h b/cc/CCSchedulerStateMachine.h
new file mode 100644
index 0000000..279b716
--- /dev/null
+++ b/cc/CCSchedulerStateMachine.h
@@ -0,0 +1,163 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSchedulerStateMachine_h
+#define CCSchedulerStateMachine_h
+
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+// The CCSchedulerStateMachine decides how to coordinate main thread activites
+// like painting/running javascript with rendering and input activities on the
+// impl thread.
+//
+// The state machine tracks internal state but is also influenced by external state.
+// Internal state includes things like whether a frame has been requested, while
+// external state includes things like the current time being near to the vblank time.
+//
+// The scheduler seperates "what to do next" from the updating of its internal state to
+// make testing cleaner.
+class CCSchedulerStateMachine {
+public:
+ CCSchedulerStateMachine();
+
+ enum CommitState {
+ COMMIT_STATE_IDLE,
+ COMMIT_STATE_FRAME_IN_PROGRESS,
+ COMMIT_STATE_UPDATING_RESOURCES,
+ COMMIT_STATE_READY_TO_COMMIT,
+ COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
+ };
+
+ enum TextureState {
+ LAYER_TEXTURE_STATE_UNLOCKED,
+ LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD,
+ LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD,
+ };
+
+ enum ContextState {
+ CONTEXT_ACTIVE,
+ CONTEXT_LOST,
+ CONTEXT_RECREATING,
+ };
+
+ bool commitPending() const
+ {
+ return m_commitState != COMMIT_STATE_IDLE;
+ }
+
+ bool redrawPending() const { return m_needsRedraw; }
+
+ enum Action {
+ ACTION_NONE,
+ ACTION_BEGIN_FRAME,
+ ACTION_BEGIN_UPDATE_MORE_RESOURCES,
+ ACTION_COMMIT,
+ ACTION_DRAW_IF_POSSIBLE,
+ ACTION_DRAW_FORCED,
+ ACTION_BEGIN_CONTEXT_RECREATION,
+ ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
+ };
+ Action nextAction() const;
+ void updateState(Action);
+
+ // Indicates whether the scheduler needs a vsync callback in order to make
+ // progress.
+ bool vsyncCallbackNeeded() const;
+
+ // Indicates that the system has entered and left a vsync callback.
+ // The scheduler will not draw more than once in a given vsync callback.
+ void didEnterVSync();
+ void didLeaveVSync();
+
+ // Indicates whether the LayerTreeHostImpl is visible.
+ void setVisible(bool);
+
+ // Indicates that a redraw is required, either due to the impl tree changing
+ // or the screen being damaged and simply needing redisplay.
+ void setNeedsRedraw();
+
+ // As setNeedsRedraw(), but ensures the draw will definitely happen even if
+ // we are not visible.
+ void setNeedsForcedRedraw();
+
+ // Indicates whether ACTION_DRAW_IF_POSSIBLE drew to the screen or not.
+ void didDrawIfPossibleCompleted(bool success);
+
+ // Indicates that a new commit flow needs to be performed, either to pull
+ // updates from the main thread to the impl, or to push deltas from the impl
+ // thread to main.
+ void setNeedsCommit();
+
+ // As setNeedsCommit(), but ensures the beginFrame will definitely happen even if
+ // we are not visible.
+ void setNeedsForcedCommit();
+
+ // Call this only in response to receiving an ACTION_BEGIN_FRAME
+ // from nextState. Indicates that all painting is complete and that
+ // updating of compositor resources can begin.
+ void beginFrameComplete();
+
+ // Call this only in response to receiving an ACTION_BEGIN_FRAME
+ // from nextState if the client rejects the beginFrame message.
+ void beginFrameAborted();
+
+ // Call this only in response to receiving an ACTION_UPDATE_MORE_RESOURCES
+ // from nextState. Indicates that the specific update request completed.
+ void beginUpdateMoreResourcesComplete(bool morePending);
+
+ // Request exclusive access to the textures that back single buffered
+ // layers on behalf of the main thread. Upon acqusition,
+ // ACTION_DRAW_IF_POSSIBLE will not draw until the main thread releases the
+ // textures to the impl thread by committing the layers.
+ void setMainThreadNeedsLayerTextures();
+
+ // Indicates whether we can successfully begin a frame at this time.
+ void setCanBeginFrame(bool can) { m_canBeginFrame = can; }
+
+ // Indicates whether drawing would, at this time, make sense.
+ // canDraw can be used to supress flashes or checkerboarding
+ // when such behavior would be undesirable.
+ void setCanDraw(bool can) { m_canDraw = can; }
+
+ void didLoseContext();
+ void didRecreateContext();
+
+ // Exposed for testing purposes.
+ void setMaximumNumberOfFailedDrawsBeforeDrawIsForced(int);
+
+protected:
+ bool shouldDrawForced() const;
+ bool drawSuspendedUntilCommit() const;
+ bool scheduledToDraw() const;
+ bool shouldDraw() const;
+ bool shouldAcquireLayerTexturesForMainThread() const;
+ bool hasDrawnThisFrame() const;
+
+ CommitState m_commitState;
+
+ int m_currentFrameNumber;
+ int m_lastFrameNumberWhereDrawWasCalled;
+ int m_consecutiveFailedDraws;
+ int m_maximumNumberOfFailedDrawsBeforeDrawIsForced;
+ bool m_needsRedraw;
+ bool m_needsForcedRedraw;
+ bool m_needsForcedRedrawAfterNextCommit;
+ bool m_needsCommit;
+ bool m_needsForcedCommit;
+ bool m_mainThreadNeedsLayerTextures;
+ bool m_updateMoreResourcesPending;
+ bool m_insideVSync;
+ bool m_visible;
+ bool m_canBeginFrame;
+ bool m_canDraw;
+ bool m_drawIfPossibleFailed;
+ TextureState m_textureState;
+ ContextState m_contextState;
+};
+
+}
+
+#endif // CCSchedulerStateMachine_h
diff --git a/cc/CCSchedulerStateMachineTest.cpp b/cc/CCSchedulerStateMachineTest.cpp
new file mode 100644
index 0000000..7289189
--- /dev/null
+++ b/cc/CCSchedulerStateMachineTest.cpp
@@ -0,0 +1,1045 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSchedulerStateMachine.h"
+
+#include <gtest/gtest.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WTF;
+using namespace WebCore;
+
+namespace {
+
+const CCSchedulerStateMachine::CommitState allCommitStates[] = {
+ CCSchedulerStateMachine::COMMIT_STATE_IDLE,
+ CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES,
+ CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
+ CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW
+};
+
+// Exposes the protected state fields of the CCSchedulerStateMachine for testing
+class StateMachine : public CCSchedulerStateMachine {
+public:
+ void setCommitState(CommitState cs) { m_commitState = cs; }
+ CommitState commitState() const { return m_commitState; }
+
+ void setNeedsCommit(bool b) { m_needsCommit = b; }
+ bool needsCommit() const { return m_needsCommit; }
+
+ void setNeedsForcedCommit(bool b) { m_needsForcedCommit = b; }
+ bool needsForcedCommit() const { return m_needsForcedCommit; }
+
+ void setNeedsRedraw(bool b) { m_needsRedraw = b; }
+ bool needsRedraw() const { return m_needsRedraw; }
+
+ void setNeedsForcedRedraw(bool b) { m_needsForcedRedraw = b; }
+ bool needsForcedRedraw() const { return m_needsForcedRedraw; }
+
+ bool canDraw() const { return m_canDraw; }
+ bool insideVSync() const { return m_insideVSync; }
+ bool visible() const { return m_visible; }
+
+ void setUpdateMoreResourcesPending(bool b) { m_updateMoreResourcesPending = b; }
+ bool updateMoreResourcesPending() const { return m_updateMoreResourcesPending; }
+};
+
+TEST(CCSchedulerStateMachineTest, TestNextActionBeginsFrameIfNeeded)
+{
+ // If no commit needed, do nothing
+ {
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE);
+ state.setCanBeginFrame(true);
+ state.setNeedsRedraw(false);
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ }
+
+ // If commit requested but canBeginFrame is still false, do nothing.
+ {
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE);
+ state.setNeedsRedraw(false);
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ }
+
+
+ // If commit requested, begin a frame
+ {
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE);
+ state.setCanBeginFrame(true);
+ state.setNeedsRedraw(false);
+ state.setNeedsCommit(true);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+ }
+
+ // Begin the frame, make sure needsCommit and commitState update correctly.
+ {
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
+ EXPECT_FALSE(state.needsCommit());
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+ }
+}
+
+TEST(CCSchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw)
+{
+ CCSchedulerStateMachine state;
+ state.setNeedsForcedRedraw();
+ EXPECT_FALSE(state.redrawPending());
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.redrawPending());
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+
+ // We're drawing now.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ EXPECT_FALSE(state.redrawPending());
+ EXPECT_FALSE(state.commitPending());
+
+ // Failing the draw makes us require a commit.
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_TRUE(state.redrawPending());
+ EXPECT_TRUE(state.commitPending());
+}
+
+TEST(CCSchedulerStateMachineTest, TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.redrawPending());
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+
+ // We're drawing now.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ EXPECT_FALSE(state.redrawPending());
+ EXPECT_FALSE(state.commitPending());
+
+ // While still in the same vsync callback, set needs redraw again.
+ // This should not redraw.
+ state.setNeedsRedraw();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Failing the draw makes us require a commit.
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ EXPECT_TRUE(state.redrawPending());
+}
+
+TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedDrawAllowsDrawInSameFrame)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start a commit.
+ state.setNeedsCommit();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_TRUE(state.commitPending());
+
+ // Then initiate a draw.
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ EXPECT_TRUE(state.redrawPending());
+
+ // Fail the draw.
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_TRUE(state.redrawPending());
+ // But the commit is ongoing.
+ EXPECT_TRUE(state.commitPending());
+
+ // Finish the commit.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_TRUE(state.redrawPending());
+
+ // And we should be allowed to draw again.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start a commit.
+ state.setNeedsCommit();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_TRUE(state.commitPending());
+
+ // Then initiate a draw.
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ EXPECT_TRUE(state.redrawPending());
+
+ // Fail the draw.
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_TRUE(state.redrawPending());
+ // But the commit is ongoing.
+ EXPECT_TRUE(state.commitPending());
+
+ // Force a draw.
+ state.setNeedsForcedRedraw();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
+
+ // Do the forced draw.
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_FORCED);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ EXPECT_FALSE(state.redrawPending());
+ // And the commit is still ongoing.
+ EXPECT_TRUE(state.commitPending());
+
+ // Finish the commit.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_TRUE(state.redrawPending());
+
+ // And we should not be allowed to draw again in the same frame..
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+ state.setMaximumNumberOfFailedDrawsBeforeDrawIsForced(1);
+
+ // Start a commit.
+ state.setNeedsCommit();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_TRUE(state.commitPending());
+
+ // Then initiate a draw.
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ EXPECT_TRUE(state.redrawPending());
+
+ // Fail the draw.
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_TRUE(state.redrawPending());
+ // But the commit is ongoing.
+ EXPECT_TRUE(state.commitPending());
+
+ // Finish the commit. Note, we should not yet be forcing a draw, but should
+ // continue the commit as usual.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_TRUE(state.redrawPending());
+
+ // The redraw should be forced in this case.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFailedDrawIsRetriedNextVSync)
+{
+ CCSchedulerStateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start a draw.
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ EXPECT_TRUE(state.redrawPending());
+
+ // Fail the draw.
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didDrawIfPossibleCompleted(false);
+ EXPECT_TRUE(state.redrawPending());
+
+ // We should not be trying to draw again now, but we have a commit pending.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+
+ state.didLeaveVSync();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+
+ // We should try draw again in the next vsync.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame)
+{
+ CCSchedulerStateMachine state;
+ state.setVisible(true);
+ state.setNeedsRedraw();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+
+ // While still in the same vsync callback, set needs redraw again.
+ // This should not redraw.
+ state.setNeedsRedraw();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Move to another frame. This should now draw.
+ state.didDrawIfPossibleCompleted(true);
+ state.didLeaveVSync();
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ state.didEnterVSync();
+
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ state.didDrawIfPossibleCompleted(true);
+ EXPECT_FALSE(state.vsyncCallbackNeeded());
+}
+
+TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync)
+{
+ // When not on vsync, or on vsync but not visible, don't draw.
+ size_t numCommitStates = sizeof(allCommitStates) / sizeof(CCSchedulerStateMachine::CommitState);
+ for (size_t i = 0; i < numCommitStates; ++i) {
+ for (unsigned j = 0; j < 2; ++j) {
+ StateMachine state;
+ state.setCommitState(allCommitStates[i]);
+ bool visible = j;
+ if (!visible) {
+ state.didEnterVSync();
+ state.setVisible(false);
+ } else
+ state.setVisible(true);
+
+ // Case 1: needsCommit=false
+ state.setNeedsCommit(false);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+
+ // Case 2: needsCommit=true
+ state.setNeedsCommit(true);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ }
+ }
+
+ // When on vsync, or not on vsync but needsForcedRedraw set, should always draw except if you're ready to commit, in which case commit.
+ for (size_t i = 0; i < numCommitStates; ++i) {
+ for (unsigned j = 0; j < 2; ++j) {
+ StateMachine state;
+ state.setCommitState(allCommitStates[i]);
+ bool forcedDraw = j;
+ if (!forcedDraw) {
+ state.didEnterVSync();
+ state.setNeedsRedraw(true);
+ state.setVisible(true);
+ } else
+ state.setNeedsForcedRedraw(true);
+
+ CCSchedulerStateMachine::Action expectedAction;
+ if (allCommitStates[i] != CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT)
+ expectedAction = forcedDraw ? CCSchedulerStateMachine::ACTION_DRAW_FORCED : CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE;
+ else
+ expectedAction = CCSchedulerStateMachine::ACTION_COMMIT;
+
+ // Case 1: needsCommit=false updateMoreResourcesPending=false.
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(false);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ EXPECT_EQ(expectedAction, state.nextAction());
+
+ // Case 2: needsCommit=false updateMoreResourcesPending=true.
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(true);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ EXPECT_EQ(expectedAction, state.nextAction());
+
+ // Case 3: needsCommit=true updateMoreResourcesPending=false.
+ state.setNeedsCommit(true);
+ state.setUpdateMoreResourcesPending(false);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ EXPECT_EQ(expectedAction, state.nextAction());
+
+ // Case 4: needsCommit=true updateMoreResourcesPending=true.
+ state.setNeedsCommit(true);
+ state.setUpdateMoreResourcesPending(true);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+ EXPECT_EQ(expectedAction, state.nextAction());
+ }
+ }
+}
+
+TEST(CCSchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible)
+{
+ size_t numCommitStates = sizeof(allCommitStates) / sizeof(CCSchedulerStateMachine::CommitState);
+ for (size_t i = 0; i < numCommitStates; ++i) {
+ // There shouldn't be any drawing regardless of vsync.
+ for (unsigned j = 0; j < 2; ++j) {
+ StateMachine state;
+ state.setCommitState(allCommitStates[i]);
+ state.setVisible(false);
+ state.setNeedsRedraw(true);
+ state.setNeedsForcedRedraw(false);
+ if (j == 1)
+ state.didEnterVSync();
+
+ // Case 1: needsCommit=false updateMoreResourcesPending=false.
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(false);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+
+ // Case 2: needsCommit=false updateMoreResourcesPending=true.
+ state.setNeedsCommit(false);
+ state.setUpdateMoreResourcesPending(true);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+
+ // Case 3: needsCommit=true updateMoreResourcesPending=false.
+ state.setNeedsCommit(true);
+ state.setUpdateMoreResourcesPending(false);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+
+ // Case 4: needsCommit=true updateMoreResourcesPending=true.
+ state.setNeedsCommit(true);
+ state.setUpdateMoreResourcesPending(true);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ }
+ }
+}
+
+TEST(CCSchedulerStateMachineTest, TestCanRedraw_StopsDraw)
+{
+ size_t numCommitStates = sizeof(allCommitStates) / sizeof(CCSchedulerStateMachine::CommitState);
+ for (size_t i = 0; i < numCommitStates; ++i) {
+ // There shouldn't be any drawing regardless of vsync.
+ for (unsigned j = 0; j < 2; ++j) {
+ StateMachine state;
+ state.setCommitState(allCommitStates[i]);
+ state.setVisible(false);
+ state.setNeedsRedraw(true);
+ state.setNeedsForcedRedraw(false);
+ if (j == 1)
+ state.didEnterVSync();
+
+ state.setCanDraw(false);
+ EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ }
+ }
+}
+
+TEST(CCSchedulerStateMachineTest, TestCanRedrawWithWaitingForFirstDrawMakesProgress)
+{
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.setCanBeginFrame(true);
+ state.setNeedsCommit(true);
+ state.setNeedsRedraw(true);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+ state.setCanDraw(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestUpdates_NoRedraw_OneRoundOfUpdates)
+{
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES);
+ state.setNeedsRedraw(false);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+
+ // Verify we begin update, both for vsync and not vsync.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Begin an update.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+
+ // Verify we don't do anything, both for vsync and not vsync.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // End update with no more updates pending.
+ state.beginUpdateMoreResourcesComplete(false);
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestUpdates_NoRedraw_TwoRoundsOfUpdates)
+{
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES);
+ state.setNeedsRedraw(false);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+
+ // Verify the update begins, both for vsync and not vsync.
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Begin an update.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+
+ // Verify we do nothing, both for vsync and not vsync.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Ack the update with more pending.
+ state.beginUpdateMoreResourcesComplete(true);
+
+ // Verify we update more, both for vsync and not vsync.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Begin another update, while inside vsync. And, it updating.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+
+ // Make sure we commit, independent of vsync.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+}
+
+
+TEST(CCSchedulerStateMachineTest, TestVSyncNeededWhenUpdatesPendingButInvisible)
+{
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES);
+ state.setNeedsRedraw(false);
+ state.setVisible(false);
+ state.setUpdateMoreResourcesPending(true);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+
+ state.setUpdateMoreResourcesPending(false);
+ EXPECT_TRUE(state.vsyncCallbackNeeded());
+}
+
+TEST(CCSchedulerStateMachineTest, TestUpdates_WithRedraw_OneRoundOfUpdates)
+{
+ StateMachine state;
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES);
+ state.setNeedsRedraw(true);
+ state.setUpdateMoreResourcesPending(false);
+ state.setVisible(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Begin an update.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+
+ // Ensure we draw on the next vsync even though an update is in-progress.
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ state.didDrawIfPossibleCompleted(true);
+
+ // Ensure that we once we have drawn, we dont do anything else.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Leave the vsync before we finish the update.
+ state.didLeaveVSync();
+
+ // Finish update but leave more resources pending.
+ state.beginUpdateMoreResourcesComplete(true);
+
+ // Verify that regardless of vsync, we update some more.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Begin another update. Finish it immediately. Inside the vsync.
+ state.didEnterVSync();
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.didLeaveVSync();
+ state.beginUpdateMoreResourcesComplete(false);
+
+ // Verify we commit regardless of vsync state
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestSetNeedsCommitIsNotLost)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setNeedsCommit(true);
+ state.setVisible(true);
+
+ // Begin the frame.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(state.nextAction());
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
+
+ // Now, while the frame is in progress, set another commit.
+ state.setNeedsCommit(true);
+ EXPECT_TRUE(state.needsCommit());
+
+ // Let the frame finish.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
+
+ // Expect to commit regardless of vsync state.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+
+ // Commit and make sure we draw on next vsync
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ state.didDrawIfPossibleCompleted(true);
+
+ // Verify that another commit will begin.
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFullCycle)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start clean and set commit.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+
+ // Begin the frame.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
+ EXPECT_FALSE(state.needsCommit());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Tell the scheduler the frame finished.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Tell the scheduler the update began and finished
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+
+ // Commit.
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
+ EXPECT_TRUE(state.needsRedraw());
+
+ // Expect to do nothing until vsync.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // At vsync, draw.
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ state.didDrawIfPossibleCompleted(true);
+ state.didLeaveVSync();
+
+ // Should be synchronized, no draw needed, no action needed.
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
+ EXPECT_FALSE(state.needsRedraw());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start clean and set commit.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+
+ // Begin the frame.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
+ EXPECT_FALSE(state.needsCommit());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Request another commit while the commit is in flight.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Tell the scheduler the frame finished.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+
+ // Tell the scheduler the update began and finished
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES);
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+
+ // Commit.
+ state.updateState(CCSchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState());
+ EXPECT_TRUE(state.needsRedraw());
+
+ // Expect to do nothing until vsync.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // At vsync, draw.
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ state.didDrawIfPossibleCompleted(true);
+ state.didLeaveVSync();
+
+ // Should be synchronized, no draw needed, no action needed.
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
+ EXPECT_FALSE(state.needsRedraw());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestRequestCommitInvisible)
+{
+ StateMachine state;
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestGoesInvisibleBeforeBeginFrameCompletes)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Start clean and set commit.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+
+ // Begin the frame while visible.
+ state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME);
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState());
+ EXPECT_FALSE(state.needsCommit());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Become invisible and abort the beginFrame.
+ state.setVisible(false);
+ state.beginFrameAborted();
+
+ // We should now be back in the idle state as if we didn't start a frame at all.
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestContextLostWhenCompletelyIdle)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ state.didLoseContext();
+
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Once context recreation begins, nothing should happen.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Recreate the context
+ state.didRecreateContext();
+
+ // When the context is recreated, we should begin a commit
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhileRecreating)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ state.didLoseContext();
+
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Once context recreation begins, nothing should happen.
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // While context is recreating, commits shouldn't begin.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Recreate the context
+ state.didRecreateContext();
+
+ // When the context is recreated, we should begin a commit
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Once the context is recreated, whether we draw should be based on
+ // setCanDraw.
+ state.setNeedsRedraw(true);
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.setCanDraw(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.setCanDraw(true);
+ state.didLeaveVSync();
+}
+
+TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgress)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Get a commit in flight.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Set damage and expect a draw.
+ state.setNeedsRedraw(true);
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(state.nextAction());
+ state.didLeaveVSync();
+
+ // Cause a lost context while the begin frame is in flight.
+ state.didLoseContext();
+
+ // Ask for another draw. Expect nothing happens.
+ state.setNeedsRedraw(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Finish the frame, update resources, and commit.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(state.nextAction());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Expect to be told to begin context recreation, independent of vsync state
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnotherCommitRequested)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+
+ // Get a commit in flight.
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Set damage and expect a draw.
+ state.setNeedsRedraw(true);
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(state.nextAction());
+ state.didLeaveVSync();
+
+ // Cause a lost context while the begin frame is in flight.
+ state.didLoseContext();
+
+ // Ask for another draw and also set needs commit. Expect nothing happens.
+ // Setting another commit will put us into
+ // COMMIT_STATE_WAITING_FOR_FIRST_DRAW after we finish the frame on the main
+ // thread.
+ state.setNeedsRedraw(true);
+ state.setNeedsCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+
+ // Finish the frame, update resources, and commit.
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(state.nextAction());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(state.nextAction());
+
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState());
+
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Expect to be told to begin context recreation, independent of vsync state
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+ state.didLeaveVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+}
+
+
+TEST(CCSchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost)
+{
+ StateMachine state;
+ state.setVisible(true);
+
+ // Cause a lost context lost.
+ state.didLoseContext();
+
+ // Ask a forced redraw and verify it ocurrs.
+ state.setNeedsForcedRedraw(true);
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
+ state.didLeaveVSync();
+
+ // Clear the forced redraw bit.
+ state.setNeedsForcedRedraw(false);
+
+ // Expect to be told to begin context recreation, independent of vsync state
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_CONTEXT_RECREATION, state.nextAction());
+ state.updateState(state.nextAction());
+
+ // Ask a forced redraw and verify it ocurrs.
+ state.setNeedsForcedRedraw(true);
+ state.didEnterVSync();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction());
+ state.didLeaveVSync();
+}
+
+TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenInvisibleAndForceCommit)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(false);
+ state.setNeedsCommit(true);
+ state.setNeedsForcedCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenCanBeginFrameFalseAndForceCommit)
+{
+ StateMachine state;
+ state.setVisible(true);
+ state.setNeedsCommit(true);
+ state.setNeedsForcedCommit(true);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenCommitInProgress)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(false);
+ state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
+ state.setNeedsCommit(true);
+ state.setNeedsForcedCommit(true);
+
+ state.beginFrameComplete();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction());
+ state.updateState(state.nextAction());
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction());
+ state.beginUpdateMoreResourcesComplete(false);
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction());
+ state.updateState(state.nextAction());
+
+ EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState());
+
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenContextLost)
+{
+ StateMachine state;
+ state.setCanBeginFrame(true);
+ state.setVisible(true);
+ state.setNeedsCommit(true);
+ state.setNeedsForcedCommit(true);
+ state.didLoseContext();
+ EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction());
+}
+
+}
diff --git a/cc/CCSchedulerTest.cpp b/cc/CCSchedulerTest.cpp
new file mode 100644
index 0000000..ec68c71
--- /dev/null
+++ b/cc/CCSchedulerTest.cpp
@@ -0,0 +1,472 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScheduler.h"
+
+#include "CCSchedulerTestCommon.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wtf/OwnPtr.h>
+
+using namespace WTF;
+using namespace WebCore;
+using namespace WebKitTests;
+
+namespace {
+
+class FakeCCSchedulerClient : public CCSchedulerClient {
+public:
+ FakeCCSchedulerClient() { reset(); }
+ void reset()
+ {
+ m_actions.clear();
+ m_hasMoreResourceUpdates = false;
+ m_canDraw = true;
+ m_drawWillHappen = true;
+ m_swapWillHappenIfDrawHappens = true;
+ m_numDraws = 0;
+ }
+
+ void setHasMoreResourceUpdates(bool b) { m_hasMoreResourceUpdates = b; }
+ void setCanDraw(bool b) { m_canDraw = b; }
+
+ int numDraws() const { return m_numDraws; }
+ int numActions() const { return static_cast<int>(m_actions.size()); }
+ const char* action(int i) const { return m_actions[i]; }
+
+ bool hasAction(const char* action) const
+ {
+ for (size_t i = 0; i < m_actions.size(); i++)
+ if (!strcmp(m_actions[i], action))
+ return true;
+ return false;
+ }
+
+ virtual bool canDraw() OVERRIDE { return m_canDraw; }
+ virtual bool hasMoreResourceUpdates() const OVERRIDE { return m_hasMoreResourceUpdates; }
+ virtual void scheduledActionBeginFrame() OVERRIDE { m_actions.push_back("scheduledActionBeginFrame"); }
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE
+ {
+ m_actions.push_back("scheduledActionDrawAndSwapIfPossible");
+ m_numDraws++;
+ return CCScheduledActionDrawAndSwapResult(m_drawWillHappen, m_drawWillHappen && m_swapWillHappenIfDrawHappens);
+ }
+
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE
+ {
+ m_actions.push_back("scheduledActionDrawAndSwapForced");
+ return CCScheduledActionDrawAndSwapResult(true, m_swapWillHappenIfDrawHappens);
+ }
+
+ virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { m_actions.push_back("scheduledActionUpdateMoreResources"); }
+ virtual void scheduledActionCommit() OVERRIDE { m_actions.push_back("scheduledActionCommit"); }
+ virtual void scheduledActionBeginContextRecreation() OVERRIDE { m_actions.push_back("scheduledActionBeginContextRecreation"); }
+ virtual void scheduledActionAcquireLayerTexturesForMainThread() OVERRIDE { m_actions.push_back("scheduledActionAcquireLayerTexturesForMainThread"); }
+
+ void setDrawWillHappen(bool drawWillHappen) { m_drawWillHappen = drawWillHappen; }
+ void setSwapWillHappenIfDrawHappens(bool swapWillHappenIfDrawHappens) { m_swapWillHappenIfDrawHappens = swapWillHappenIfDrawHappens; }
+
+protected:
+ bool m_hasMoreResourceUpdates;
+ bool m_canDraw;
+ bool m_drawWillHappen;
+ bool m_swapWillHappenIfDrawHappens;
+ int m_numDraws;
+ std::vector<const char*> m_actions;
+};
+
+TEST(CCSchedulerTest, RequestCommit)
+{
+ FakeCCSchedulerClient client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ // SetNeedsCommit should begin the frame.
+ scheduler->setNeedsCommit();
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
+ EXPECT_FALSE(timeSource->active());
+ client.reset();
+
+ // Since, hasMoreResourceUpdates is set to false,
+ // beginFrameComplete should commit
+ scheduler->beginFrameComplete();
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionCommit", client.action(0));
+ EXPECT_TRUE(timeSource->active());
+ client.reset();
+
+ // Tick should draw.
+ timeSource->tick();
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0));
+ EXPECT_FALSE(timeSource->active());
+ client.reset();
+
+ // Timer should be off.
+ EXPECT_FALSE(timeSource->active());
+}
+
+TEST(CCSchedulerTest, RequestCommitAfterBeginFrame)
+{
+ FakeCCSchedulerClient client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ // SetNedsCommit should begin the frame.
+ scheduler->setNeedsCommit();
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
+ client.reset();
+
+ // Now setNeedsCommit again. Calling here means we need a second frame.
+ scheduler->setNeedsCommit();
+
+ // Since, hasMoreResourceUpdates is set to false, and another commit is
+ // needed, beginFrameComplete should commit, then begin another frame.
+ scheduler->beginFrameComplete();
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionCommit", client.action(0));
+ client.reset();
+
+ // Tick should draw but then begin another frame.
+ timeSource->tick();
+ EXPECT_FALSE(timeSource->active());
+ EXPECT_EQ(2, client.numActions());
+ EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0));
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(1));
+ client.reset();
+}
+
+TEST(CCSchedulerTest, TextureAcquisitionCollision)
+{
+ FakeCCSchedulerClient client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ scheduler->setNeedsCommit();
+ scheduler->setMainThreadNeedsLayerTextures();
+ EXPECT_EQ(2, client.numActions());
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
+ EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1));
+ client.reset();
+
+ // Compositor not scheduled to draw because textures are locked by main thread
+ EXPECT_FALSE(timeSource->active());
+
+ // Trigger the commit
+ scheduler->beginFrameComplete();
+ EXPECT_TRUE(timeSource->active());
+ client.reset();
+
+ // Between commit and draw, texture acquisition for main thread delayed,
+ // and main thread blocks.
+ scheduler->setMainThreadNeedsLayerTextures();
+ EXPECT_EQ(0, client.numActions());
+ client.reset();
+
+ // Once compositor draw complete, the delayed texture acquisition fires.
+ timeSource->tick();
+ EXPECT_EQ(3, client.numActions());
+ EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0));
+ EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1));
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(2));
+ client.reset();
+}
+
+TEST(CCSchedulerTest, VisibilitySwitchWithTextureAcquisition)
+{
+ FakeCCSchedulerClient client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ scheduler->setNeedsCommit();
+ scheduler->beginFrameComplete();
+ scheduler->setMainThreadNeedsLayerTextures();
+ client.reset();
+ // Verify that pending texture acquisition fires when visibility
+ // is lost in order to avoid a deadlock.
+ scheduler->setVisible(false);
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(0));
+ client.reset();
+
+ // Regaining visibility with textures acquired by main thread while
+ // compositor is waiting for first draw should result in a request
+ // for a new frame in order to escape a deadlock.
+ scheduler->setVisible(true);
+ EXPECT_EQ(1, client.numActions());
+ EXPECT_STREQ("scheduledActionBeginFrame", client.action(0));
+ client.reset();
+}
+
+class SchedulerClientThatSetNeedsDrawInsideDraw : public FakeCCSchedulerClient {
+public:
+ SchedulerClientThatSetNeedsDrawInsideDraw()
+ : m_scheduler(0) { }
+
+ void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; }
+
+ virtual void scheduledActionBeginFrame() OVERRIDE { }
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE
+ {
+ // Only setNeedsRedraw the first time this is called
+ if (!m_numDraws)
+ m_scheduler->setNeedsRedraw();
+ return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible();
+ }
+
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE
+ {
+ ASSERT_NOT_REACHED();
+ return CCScheduledActionDrawAndSwapResult(true, true);
+ }
+
+ virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { }
+ virtual void scheduledActionCommit() OVERRIDE { }
+ virtual void scheduledActionBeginContextRecreation() OVERRIDE { }
+
+protected:
+ CCScheduler* m_scheduler;
+};
+
+// Tests for two different situations:
+// 1. the scheduler dropping setNeedsRedraw requests that happen inside
+// a scheduledActionDrawAndSwap
+// 2. the scheduler drawing twice inside a single tick
+TEST(CCSchedulerTest, RequestRedrawInsideDraw)
+{
+ SchedulerClientThatSetNeedsDrawInsideDraw client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ client.setScheduler(scheduler.get());
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+ EXPECT_EQ(0, client.numDraws());
+
+ timeSource->tick();
+ EXPECT_EQ(1, client.numDraws());
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ timeSource->tick();
+ EXPECT_EQ(2, client.numDraws());
+ EXPECT_FALSE(scheduler->redrawPending());
+ EXPECT_FALSE(timeSource->active());
+}
+
+// Test that requesting redraw inside a failed draw doesn't lose the request.
+TEST(CCSchedulerTest, RequestRedrawInsideFailedDraw)
+{
+ SchedulerClientThatSetNeedsDrawInsideDraw client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ client.setScheduler(scheduler.get());
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+ client.setDrawWillHappen(false);
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+ EXPECT_EQ(0, client.numDraws());
+
+ // Fail the draw.
+ timeSource->tick();
+ EXPECT_EQ(1, client.numDraws());
+
+ // We have a commit pending and the draw failed, and we didn't lose the redraw request.
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ // Fail the draw again.
+ timeSource->tick();
+ EXPECT_EQ(2, client.numDraws());
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ // Draw successfully.
+ client.setDrawWillHappen(true);
+ timeSource->tick();
+ EXPECT_EQ(3, client.numDraws());
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_FALSE(scheduler->redrawPending());
+ EXPECT_FALSE(timeSource->active());
+}
+
+class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeCCSchedulerClient {
+public:
+ SchedulerClientThatSetNeedsCommitInsideDraw()
+ : m_scheduler(0) { }
+
+ void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; }
+
+ virtual void scheduledActionBeginFrame() OVERRIDE { }
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE
+ {
+ // Only setNeedsCommit the first time this is called
+ if (!m_numDraws)
+ m_scheduler->setNeedsCommit();
+ return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible();
+ }
+
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE
+ {
+ ASSERT_NOT_REACHED();
+ return CCScheduledActionDrawAndSwapResult(true, true);
+ }
+
+ virtual void scheduledActionUpdateMoreResources(double) OVERRIDE { }
+ virtual void scheduledActionCommit() OVERRIDE { }
+ virtual void scheduledActionBeginContextRecreation() OVERRIDE { }
+
+protected:
+ CCScheduler* m_scheduler;
+};
+
+// Tests for the scheduler infinite-looping on setNeedsCommit requests that
+// happen inside a scheduledActionDrawAndSwap
+TEST(CCSchedulerTest, RequestCommitInsideDraw)
+{
+ SchedulerClientThatSetNeedsCommitInsideDraw client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ client.setScheduler(scheduler.get());
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_EQ(0, client.numDraws());
+ EXPECT_TRUE(timeSource->active());
+
+ timeSource->tick();
+ EXPECT_FALSE(timeSource->active());
+ EXPECT_EQ(1, client.numDraws());
+ EXPECT_TRUE(scheduler->commitPending());
+ scheduler->beginFrameComplete();
+
+ timeSource->tick();
+ EXPECT_EQ(2, client.numDraws());
+ EXPECT_FALSE(timeSource->active());
+ EXPECT_FALSE(scheduler->redrawPending());
+}
+
+// Tests that when a draw fails then the pending commit should not be dropped.
+TEST(CCSchedulerTest, RequestCommitInsideFailedDraw)
+{
+ SchedulerClientThatSetNeedsDrawInsideDraw client;
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource)));
+ client.setScheduler(scheduler.get());
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+ client.setDrawWillHappen(false);
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+ EXPECT_EQ(0, client.numDraws());
+
+ // Fail the draw.
+ timeSource->tick();
+ EXPECT_EQ(1, client.numDraws());
+
+ // We have a commit pending and the draw failed, and we didn't lose the commit request.
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ // Fail the draw again.
+ timeSource->tick();
+ EXPECT_EQ(2, client.numDraws());
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ // Draw successfully.
+ client.setDrawWillHappen(true);
+ timeSource->tick();
+ EXPECT_EQ(3, client.numDraws());
+ EXPECT_TRUE(scheduler->commitPending());
+ EXPECT_FALSE(scheduler->redrawPending());
+ EXPECT_FALSE(timeSource->active());
+}
+
+TEST(CCSchedulerTest, NoBeginFrameWhenDrawFails)
+{
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ SchedulerClientThatSetNeedsCommitInsideDraw client;
+ OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource));
+ FakeCCFrameRateController* controllerPtr = controller.get();
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release());
+ client.setScheduler(scheduler.get());
+ scheduler->setCanBeginFrame(true);
+ scheduler->setVisible(true);
+
+ EXPECT_EQ(0, controllerPtr->numFramesPending());
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+ EXPECT_EQ(0, client.numDraws());
+
+ // Draw successfully, this starts a new frame.
+ timeSource->tick();
+ EXPECT_EQ(1, client.numDraws());
+ EXPECT_EQ(1, controllerPtr->numFramesPending());
+ scheduler->didSwapBuffersComplete();
+ EXPECT_EQ(0, controllerPtr->numFramesPending());
+
+ scheduler->setNeedsRedraw();
+ EXPECT_TRUE(scheduler->redrawPending());
+ EXPECT_TRUE(timeSource->active());
+
+ // Fail to draw, this should not start a frame.
+ client.setDrawWillHappen(false);
+ timeSource->tick();
+ EXPECT_EQ(2, client.numDraws());
+ EXPECT_EQ(0, controllerPtr->numFramesPending());
+}
+
+TEST(CCSchedulerTest, NoBeginFrameWhenSwapFailsDuringForcedCommit)
+{
+ RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource());
+ FakeCCSchedulerClient client;
+ OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource));
+ FakeCCFrameRateController* controllerPtr = controller.get();
+ OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release());
+
+ EXPECT_EQ(0, controllerPtr->numFramesPending());
+
+ // Tell the client that it will fail to swap.
+ client.setDrawWillHappen(true);
+ client.setSwapWillHappenIfDrawHappens(false);
+
+ // Get the compositor to do a scheduledActionDrawAndSwapForced.
+ scheduler->setNeedsRedraw();
+ scheduler->setNeedsForcedRedraw();
+ EXPECT_TRUE(client.hasAction("scheduledActionDrawAndSwapForced"));
+
+ // We should not have told the frame rate controller that we began a frame.
+ EXPECT_EQ(0, controllerPtr->numFramesPending());
+}
+
+}
diff --git a/cc/CCScopedTexture.cpp b/cc/CCScopedTexture.cpp
new file mode 100644
index 0000000..b032680
--- /dev/null
+++ b/cc/CCScopedTexture.cpp
@@ -0,0 +1,51 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScopedTexture.h"
+
+namespace WebCore {
+
+CCScopedTexture::CCScopedTexture(CCResourceProvider* resourceProvider)
+ : m_resourceProvider(resourceProvider)
+{
+ ASSERT(m_resourceProvider);
+}
+
+CCScopedTexture::~CCScopedTexture()
+{
+ free();
+}
+
+bool CCScopedTexture::allocate(int pool, const IntSize& size, GC3Denum format, CCResourceProvider::TextureUsageHint hint)
+{
+ ASSERT(!id());
+ ASSERT(!size.isEmpty());
+
+ setDimensions(size, format);
+ setId(m_resourceProvider->createResource(pool, size, format, hint));
+
+#if !ASSERT_DISABLED
+ m_allocateThreadIdentifier = WTF::currentThread();
+#endif
+
+ return id();
+}
+
+void CCScopedTexture::free()
+{
+ if (id()) {
+ ASSERT(m_allocateThreadIdentifier == WTF::currentThread());
+ m_resourceProvider->deleteResource(id());
+ }
+ setId(0);
+}
+
+void CCScopedTexture::leak()
+{
+ setId(0);
+}
+
+}
diff --git a/cc/CCScopedTexture.h b/cc/CCScopedTexture.h
new file mode 100644
index 0000000..c82b8bc
--- /dev/null
+++ b/cc/CCScopedTexture.h
@@ -0,0 +1,44 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScopedTexture_h
+#define CCScopedTexture_h
+
+#include "CCTexture.h"
+
+#if !ASSERT_DISABLED
+#include <wtf/MainThread.h>
+#endif
+
+namespace WebCore {
+
+class CCScopedTexture : protected CCTexture {
+ WTF_MAKE_NONCOPYABLE(CCScopedTexture);
+public:
+ static PassOwnPtr<CCScopedTexture> create(CCResourceProvider* resourceProvider) { return adoptPtr(new CCScopedTexture(resourceProvider)); }
+ virtual ~CCScopedTexture();
+
+ using CCTexture::id;
+ using CCTexture::size;
+ using CCTexture::format;
+ using CCTexture::bytes;
+
+ bool allocate(int pool, const IntSize&, GC3Denum format, CCResourceProvider::TextureUsageHint);
+ void free();
+ void leak();
+
+protected:
+ explicit CCScopedTexture(CCResourceProvider*);
+
+private:
+ CCResourceProvider* m_resourceProvider;
+
+#if !ASSERT_DISABLED
+ ThreadIdentifier m_allocateThreadIdentifier;
+#endif
+};
+
+}
+
+#endif
diff --git a/cc/CCScopedTextureTest.cpp b/cc/CCScopedTextureTest.cpp
new file mode 100644
index 0000000..b39efca
--- /dev/null
+++ b/cc/CCScopedTextureTest.cpp
@@ -0,0 +1,108 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScopedTexture.h"
+
+#include "CCRenderer.h"
+#include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
+#include "CCTiledLayerTestCommon.h"
+#include "FakeCCGraphicsContext.h"
+#include "GraphicsContext3D.h"
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WebKitTests;
+
+namespace {
+
+TEST(CCScopedTextureTest, NewScopedTexture)
+{
+ OwnPtr<CCGraphicsContext> context(createFakeCCGraphicsContext());
+ DebugScopedSetImplThread implThread;
+ OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get()));
+ OwnPtr<CCScopedTexture> texture = CCScopedTexture::create(resourceProvider.get());
+
+ // New scoped textures do not hold a texture yet.
+ EXPECT_EQ(0u, texture->id());
+
+ // New scoped textures do not have a size yet.
+ EXPECT_EQ(IntSize(), texture->size());
+ EXPECT_EQ(0u, texture->bytes());
+}
+
+TEST(CCScopedTextureTest, CreateScopedTexture)
+{
+ OwnPtr<CCGraphicsContext> context(createFakeCCGraphicsContext());
+ DebugScopedSetImplThread implThread;
+ OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get()));
+ OwnPtr<CCScopedTexture> texture = CCScopedTexture::create(resourceProvider.get());
+ texture->allocate(CCRenderer::ImplPool, IntSize(30, 30), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+
+ // The texture has an allocated byte-size now.
+ size_t expectedBytes = 30 * 30 * 4;
+ EXPECT_EQ(expectedBytes, texture->bytes());
+
+ EXPECT_LT(0u, texture->id());
+ EXPECT_EQ(GraphicsContext3D::RGBA, texture->format());
+ EXPECT_EQ(IntSize(30, 30), texture->size());
+}
+
+TEST(CCScopedTextureTest, ScopedTextureIsDeleted)
+{
+ OwnPtr<CCGraphicsContext> context(createFakeCCGraphicsContext());
+ DebugScopedSetImplThread implThread;
+ OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get()));
+
+ {
+ OwnPtr<CCScopedTexture> texture = CCScopedTexture::create(resourceProvider.get());
+
+ EXPECT_EQ(0u, resourceProvider->numResources());
+ texture->allocate(CCRenderer::ImplPool, IntSize(30, 30), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+ EXPECT_LT(0u, texture->id());
+ EXPECT_EQ(1u, resourceProvider->numResources());
+ }
+
+ EXPECT_EQ(0u, resourceProvider->numResources());
+
+ {
+ OwnPtr<CCScopedTexture> texture = CCScopedTexture::create(resourceProvider.get());
+ EXPECT_EQ(0u, resourceProvider->numResources());
+ texture->allocate(CCRenderer::ImplPool, IntSize(30, 30), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+ EXPECT_LT(0u, texture->id());
+ EXPECT_EQ(1u, resourceProvider->numResources());
+ texture->free();
+ EXPECT_EQ(0u, resourceProvider->numResources());
+ }
+}
+
+TEST(CCScopedTextureTest, LeakScopedTexture)
+{
+ OwnPtr<CCGraphicsContext> context(createFakeCCGraphicsContext());
+ DebugScopedSetImplThread implThread;
+ OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get()));
+
+ {
+ OwnPtr<CCScopedTexture> texture = CCScopedTexture::create(resourceProvider.get());
+
+ EXPECT_EQ(0u, resourceProvider->numResources());
+ texture->allocate(CCRenderer::ImplPool, IntSize(30, 30), GraphicsContext3D::RGBA, CCResourceProvider::TextureUsageAny);
+ EXPECT_LT(0u, texture->id());
+ EXPECT_EQ(1u, resourceProvider->numResources());
+
+ texture->leak();
+ EXPECT_EQ(0u, texture->id());
+ EXPECT_EQ(1u, resourceProvider->numResources());
+
+ texture->free();
+ EXPECT_EQ(0u, texture->id());
+ EXPECT_EQ(1u, resourceProvider->numResources());
+ }
+
+ EXPECT_EQ(1u, resourceProvider->numResources());
+}
+
+}
diff --git a/cc/CCScopedThreadProxy.h b/cc/CCScopedThreadProxy.h
new file mode 100644
index 0000000..e30587c
--- /dev/null
+++ b/cc/CCScopedThreadProxy.h
@@ -0,0 +1,71 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScopedThreadProxy_h
+#define CCScopedThreadProxy_h
+
+#include "CCThreadTask.h"
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+
+// This class is a proxy used to post tasks to an target thread from any other thread. The proxy may be shut down at
+// any point from the target thread after which no more tasks posted to the proxy will run. In other words, all
+// tasks posted via a proxy are scoped to the lifecycle of the proxy. Use this when posting tasks to an object that
+// might die with tasks in flight.
+//
+// The proxy must be created and shut down from the target thread, tasks may be posted from any thread.
+//
+// Implementation note: Unlike ScopedRunnableMethodFactory in Chromium, pending tasks are not cancelled by actually
+// destroying the proxy. Instead each pending task holds a reference to the proxy to avoid maintaining an explicit
+// list of outstanding tasks.
+class CCScopedThreadProxy : public ThreadSafeRefCounted<CCScopedThreadProxy> {
+public:
+ static PassRefPtr<CCScopedThreadProxy> create(CCThread* targetThread)
+ {
+ ASSERT(currentThread() == targetThread->threadID());
+ return adoptRef(new CCScopedThreadProxy(targetThread));
+ }
+
+ // Can be called from any thread. Posts a task to the target thread that runs unless
+ // shutdown() is called before it runs.
+ void postTask(PassOwnPtr<CCThread::Task> task)
+ {
+ ref();
+ m_targetThread->postTask(createCCThreadTask(this, &CCScopedThreadProxy::runTaskIfNotShutdown, task));
+ }
+
+ void shutdown()
+ {
+ ASSERT(currentThread() == m_targetThread->threadID());
+ ASSERT(!m_shutdown);
+ m_shutdown = true;
+ }
+
+private:
+ explicit CCScopedThreadProxy(CCThread* targetThread)
+ : m_targetThread(targetThread)
+ , m_shutdown(false) { }
+
+ void runTaskIfNotShutdown(PassOwnPtr<CCThread::Task> popTask)
+ {
+ OwnPtr<CCThread::Task> task = popTask;
+ // If our shutdown flag is set, it's possible that m_targetThread has already been destroyed so don't
+ // touch it.
+ if (m_shutdown) {
+ deref();
+ return;
+ }
+ ASSERT(currentThread() == m_targetThread->threadID());
+ task->performTask();
+ deref();
+ }
+
+ CCThread* m_targetThread;
+ bool m_shutdown; // Only accessed on the target thread
+};
+
+}
+
+#endif
diff --git a/cc/CCScrollbarAnimationController.cpp b/cc/CCScrollbarAnimationController.cpp
new file mode 100644
index 0000000..fa5ef0d
--- /dev/null
+++ b/cc/CCScrollbarAnimationController.cpp
@@ -0,0 +1,92 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScrollbarAnimationController.h"
+
+#include "CCScrollbarLayerImpl.h"
+#include <wtf/CurrentTime.h>
+
+#if OS(ANDROID)
+#include "CCScrollbarAnimationControllerLinearFade.h"
+#endif
+
+namespace WebCore {
+
+#if OS(ANDROID)
+PassOwnPtr<CCScrollbarAnimationController> CCScrollbarAnimationController::create(CCLayerImpl* scrollLayer)
+{
+ static const double fadeoutDelay = 0.3;
+ static const double fadeoutLength = 0.3;
+ return CCScrollbarAnimationControllerLinearFade::create(scrollLayer, fadeoutDelay, fadeoutLength);
+}
+#else
+PassOwnPtr<CCScrollbarAnimationController> CCScrollbarAnimationController::create(CCLayerImpl* scrollLayer)
+{
+ return adoptPtr(new CCScrollbarAnimationController(scrollLayer));
+}
+#endif
+
+CCScrollbarAnimationController::CCScrollbarAnimationController(CCLayerImpl* scrollLayer)
+ : m_horizontalScrollbarLayer(0)
+ , m_verticalScrollbarLayer(0)
+{
+ CCScrollbarAnimationController::updateScrollOffsetAtTime(scrollLayer, 0);
+}
+
+CCScrollbarAnimationController::~CCScrollbarAnimationController()
+{
+}
+
+void CCScrollbarAnimationController::didPinchGestureBegin()
+{
+ didPinchGestureBeginAtTime(monotonicallyIncreasingTime());
+}
+
+void CCScrollbarAnimationController::didPinchGestureUpdate()
+{
+ didPinchGestureUpdateAtTime(monotonicallyIncreasingTime());
+}
+
+void CCScrollbarAnimationController::didPinchGestureEnd()
+{
+ didPinchGestureEndAtTime(monotonicallyIncreasingTime());
+}
+
+void CCScrollbarAnimationController::updateScrollOffset(CCLayerImpl* scrollLayer)
+{
+ updateScrollOffsetAtTime(scrollLayer, monotonicallyIncreasingTime());
+}
+
+IntSize CCScrollbarAnimationController::getScrollLayerBounds(const CCLayerImpl* scrollLayer)
+{
+ if (!scrollLayer->children().size())
+ return IntSize();
+ // Copy & paste from CCLayerTreeHostImpl...
+ // FIXME: Hardcoding the first child here is weird. Think of
+ // a cleaner way to get the contentBounds on the Impl side.
+ return scrollLayer->children()[0]->bounds();
+}
+
+void CCScrollbarAnimationController::updateScrollOffsetAtTime(CCLayerImpl* scrollLayer, double)
+{
+ m_currentPos = scrollLayer->scrollPosition() + scrollLayer->scrollDelta();
+ m_totalSize = getScrollLayerBounds(scrollLayer);
+ m_maximum = scrollLayer->maxScrollPosition();
+
+ if (m_horizontalScrollbarLayer) {
+ m_horizontalScrollbarLayer->setCurrentPos(m_currentPos.x());
+ m_horizontalScrollbarLayer->setTotalSize(m_totalSize.width());
+ m_horizontalScrollbarLayer->setMaximum(m_maximum.width());
+ }
+
+ if (m_verticalScrollbarLayer) {
+ m_verticalScrollbarLayer->setCurrentPos(m_currentPos.y());
+ m_verticalScrollbarLayer->setTotalSize(m_totalSize.height());
+ m_verticalScrollbarLayer->setMaximum(m_maximum.height());
+ }
+}
+
+} // namespace WebCore
diff --git a/cc/CCScrollbarAnimationController.h b/cc/CCScrollbarAnimationController.h
new file mode 100644
index 0000000..256b463
--- /dev/null
+++ b/cc/CCScrollbarAnimationController.h
@@ -0,0 +1,64 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScrollbarAnimationController_h
+#define CCScrollbarAnimationController_h
+
+#include "FloatPoint.h"
+#include "IntSize.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCLayerImpl;
+class CCScrollbarLayerImpl;
+
+// This abstract class represents the compositor-side analogy of ScrollbarAnimator.
+// Individual platforms should subclass it to provide specialized implementation.
+class CCScrollbarAnimationController {
+public:
+ // Implemented by subclass.
+ static PassOwnPtr<CCScrollbarAnimationController> create(CCLayerImpl* scrollLayer);
+
+ virtual ~CCScrollbarAnimationController();
+
+ virtual bool animate(double monotonicTime) { return false; }
+ void didPinchGestureBegin();
+ void didPinchGestureUpdate();
+ void didPinchGestureEnd();
+ void updateScrollOffset(CCLayerImpl* scrollLayer);
+
+ void setHorizontalScrollbarLayer(CCScrollbarLayerImpl* layer) { m_horizontalScrollbarLayer = layer; }
+ CCScrollbarLayerImpl* horizontalScrollbarLayer() const { return m_horizontalScrollbarLayer; }
+
+ void setVerticalScrollbarLayer(CCScrollbarLayerImpl* layer) { m_verticalScrollbarLayer = layer; }
+ CCScrollbarLayerImpl* verticalScrollbarLayer() const { return m_verticalScrollbarLayer; }
+
+ FloatPoint currentPos() const { return m_currentPos; }
+ IntSize totalSize() const { return m_totalSize; }
+ IntSize maximum() const { return m_maximum; }
+
+ virtual void didPinchGestureBeginAtTime(double monotonicTime) { }
+ virtual void didPinchGestureUpdateAtTime(double monotonicTime) { }
+ virtual void didPinchGestureEndAtTime(double monotonicTime) { }
+ virtual void updateScrollOffsetAtTime(CCLayerImpl* scrollLayer, double monotonicTime);
+
+protected:
+ explicit CCScrollbarAnimationController(CCLayerImpl* scrollLayer);
+
+private:
+ static IntSize getScrollLayerBounds(const CCLayerImpl*);
+
+ // Beware of dangling pointer. Always update these during tree synchronization.
+ CCScrollbarLayerImpl* m_horizontalScrollbarLayer;
+ CCScrollbarLayerImpl* m_verticalScrollbarLayer;
+
+ FloatPoint m_currentPos;
+ IntSize m_totalSize;
+ IntSize m_maximum;
+};
+
+} // namespace WebCore
+
+#endif // CCScrollbarAnimationController_h
diff --git a/cc/CCScrollbarAnimationControllerLinearFade.cpp b/cc/CCScrollbarAnimationControllerLinearFade.cpp
new file mode 100644
index 0000000..0207fb1
--- /dev/null
+++ b/cc/CCScrollbarAnimationControllerLinearFade.cpp
@@ -0,0 +1,78 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScrollbarAnimationControllerLinearFade.h"
+
+#include "CCScrollbarLayerImpl.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCScrollbarAnimationControllerLinearFade> CCScrollbarAnimationControllerLinearFade::create(CCLayerImpl* scrollLayer, double fadeoutDelay, double fadeoutLength)
+{
+ return adoptPtr(new CCScrollbarAnimationControllerLinearFade(scrollLayer, fadeoutDelay, fadeoutLength));
+}
+
+CCScrollbarAnimationControllerLinearFade::CCScrollbarAnimationControllerLinearFade(CCLayerImpl* scrollLayer, double fadeoutDelay, double fadeoutLength)
+ : CCScrollbarAnimationController(scrollLayer)
+ , m_lastAwakenTime(-100000000) // arbitrary invalid timestamp
+ , m_pinchGestureInEffect(false)
+ , m_fadeoutDelay(fadeoutDelay)
+ , m_fadeoutLength(fadeoutLength)
+{
+}
+
+CCScrollbarAnimationControllerLinearFade::~CCScrollbarAnimationControllerLinearFade()
+{
+}
+
+bool CCScrollbarAnimationControllerLinearFade::animate(double monotonicTime)
+{
+ float opacity = opacityAtTime(monotonicTime);
+ if (horizontalScrollbarLayer())
+ horizontalScrollbarLayer()->setOpacity(opacity);
+ if (verticalScrollbarLayer())
+ verticalScrollbarLayer()->setOpacity(opacity);
+ return opacity;
+}
+
+void CCScrollbarAnimationControllerLinearFade::didPinchGestureUpdateAtTime(double)
+{
+ m_pinchGestureInEffect = true;
+}
+
+void CCScrollbarAnimationControllerLinearFade::didPinchGestureEndAtTime(double monotonicTime)
+{
+ m_pinchGestureInEffect = false;
+ m_lastAwakenTime = monotonicTime;
+}
+
+void CCScrollbarAnimationControllerLinearFade::updateScrollOffsetAtTime(CCLayerImpl* scrollLayer, double monotonicTime)
+{
+ FloatPoint previousPos = currentPos();
+ CCScrollbarAnimationController::updateScrollOffsetAtTime(scrollLayer, monotonicTime);
+
+ if (previousPos == currentPos())
+ return;
+
+ m_lastAwakenTime = monotonicTime;
+}
+
+float CCScrollbarAnimationControllerLinearFade::opacityAtTime(double monotonicTime)
+{
+ if (m_pinchGestureInEffect)
+ return 1;
+
+ double delta = monotonicTime - m_lastAwakenTime;
+
+ if (delta <= m_fadeoutDelay)
+ return 1;
+ if (delta < m_fadeoutDelay + m_fadeoutLength)
+ return (m_fadeoutDelay + m_fadeoutLength - delta) / m_fadeoutLength;
+ return 0;
+}
+
+} // namespace WebCore
+
diff --git a/cc/CCScrollbarAnimationControllerLinearFade.h b/cc/CCScrollbarAnimationControllerLinearFade.h
new file mode 100644
index 0000000..76bf7d6
--- /dev/null
+++ b/cc/CCScrollbarAnimationControllerLinearFade.h
@@ -0,0 +1,39 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScrollbarAnimationControllerLinearFade_h
+#define CCScrollbarAnimationControllerLinearFade_h
+
+#include "CCScrollbarAnimationController.h"
+
+namespace WebCore {
+
+class CCScrollbarAnimationControllerLinearFade : public CCScrollbarAnimationController {
+public:
+ static PassOwnPtr<CCScrollbarAnimationControllerLinearFade> create(CCLayerImpl* scrollLayer, double fadeoutDelay, double fadeoutLength);
+
+ virtual ~CCScrollbarAnimationControllerLinearFade();
+
+ virtual bool animate(double monotonicTime) OVERRIDE;
+
+ virtual void didPinchGestureUpdateAtTime(double monotonicTime) OVERRIDE;
+ virtual void didPinchGestureEndAtTime(double monotonicTime) OVERRIDE;
+ virtual void updateScrollOffsetAtTime(CCLayerImpl* scrollLayer, double monotonicTime) OVERRIDE;
+
+protected:
+ CCScrollbarAnimationControllerLinearFade(CCLayerImpl* scrollLayer, double fadeoutDelay, double fadeoutLength);
+
+private:
+ float opacityAtTime(double monotonicTime);
+
+ double m_lastAwakenTime;
+ bool m_pinchGestureInEffect;
+
+ double m_fadeoutDelay;
+ double m_fadeoutLength;
+};
+
+} // namespace WebCore
+
+#endif // CCScrollbarAnimationControllerLinearFade_h
diff --git a/cc/CCScrollbarAnimationControllerLinearFadeTest.cpp b/cc/CCScrollbarAnimationControllerLinearFadeTest.cpp
new file mode 100644
index 0000000..9294c65
--- /dev/null
+++ b/cc/CCScrollbarAnimationControllerLinearFadeTest.cpp
@@ -0,0 +1,120 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCScrollbarAnimationControllerLinearFade.h"
+
+#include "CCScrollbarLayerImpl.h"
+#include "CCSingleThreadProxy.h"
+#include <gtest/gtest.h>
+#include <wtf/OwnPtr.h>
+
+using namespace WebCore;
+
+namespace {
+
+class CCScrollbarAnimationControllerLinearFadeTest : public testing::Test {
+protected:
+ virtual void SetUp()
+ {
+ m_scrollLayer = CCLayerImpl::create(1);
+ m_scrollLayer->addChild(CCLayerImpl::create(2));
+ m_contentLayer = m_scrollLayer->children()[0].get();
+ m_scrollbarLayer = CCScrollbarLayerImpl::create(3);
+
+ m_scrollLayer->setMaxScrollPosition(IntSize(50, 50));
+ m_contentLayer->setBounds(IntSize(50, 50));
+
+ m_scrollbarController = CCScrollbarAnimationControllerLinearFade::create(m_scrollLayer.get(), 2, 3);
+ m_scrollbarController->setHorizontalScrollbarLayer(m_scrollbarLayer.get());
+ }
+
+ DebugScopedSetImplThread implThread;
+
+ OwnPtr<CCScrollbarAnimationControllerLinearFade> m_scrollbarController;
+ OwnPtr<CCLayerImpl> m_scrollLayer;
+ CCLayerImpl* m_contentLayer;
+ OwnPtr<CCScrollbarLayerImpl> m_scrollbarLayer;
+
+};
+
+TEST_F(CCScrollbarAnimationControllerLinearFadeTest, verifyHiddenInBegin)
+{
+ m_scrollbarController->animate(0);
+ EXPECT_FLOAT_EQ(0, m_scrollbarLayer->opacity());
+ m_scrollbarController->updateScrollOffsetAtTime(m_scrollLayer.get(), 0);
+ m_scrollbarController->animate(0);
+ EXPECT_FLOAT_EQ(0, m_scrollbarLayer->opacity());
+}
+
+TEST_F(CCScrollbarAnimationControllerLinearFadeTest, verifyAwakenByScroll)
+{
+ m_scrollLayer->setScrollDelta(IntSize(1, 1));
+ m_scrollbarController->updateScrollOffsetAtTime(m_scrollLayer.get(), 0);
+ m_scrollbarController->animate(0);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(1);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollLayer->setScrollDelta(IntSize(2, 2));
+ m_scrollbarController->updateScrollOffsetAtTime(m_scrollLayer.get(), 1);
+ m_scrollbarController->animate(2);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(3);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(4);
+ // Note that we use 3.0f to avoid "argument is truncated from 'double' to
+ // 'float'" warnings on Windows.
+ EXPECT_FLOAT_EQ(2 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(5);
+ EXPECT_FLOAT_EQ(1 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollLayer->setScrollDelta(IntSize(3, 3));
+ m_scrollbarController->updateScrollOffsetAtTime(m_scrollLayer.get(), 5);
+ m_scrollbarController->animate(6);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(7);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(8);
+ EXPECT_FLOAT_EQ(2 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(9);
+ EXPECT_FLOAT_EQ(1 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(10);
+ EXPECT_FLOAT_EQ(0, m_scrollbarLayer->opacity());
+}
+
+TEST_F(CCScrollbarAnimationControllerLinearFadeTest, verifyForceAwakenByPinch)
+{
+ m_scrollbarController->didPinchGestureBeginAtTime(0);
+ m_scrollbarController->didPinchGestureUpdateAtTime(0);
+ m_scrollbarController->animate(0);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(1);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollLayer->setScrollDelta(IntSize(1, 1));
+ m_scrollbarController->updateScrollOffsetAtTime(m_scrollLayer.get(), 1);
+ m_scrollbarController->animate(2);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(3);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(4);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(5);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(6);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->didPinchGestureEndAtTime(6);
+ m_scrollbarController->animate(7);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(8);
+ EXPECT_FLOAT_EQ(1, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(9);
+ EXPECT_FLOAT_EQ(2 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(10);
+ EXPECT_FLOAT_EQ(1 / 3.0f, m_scrollbarLayer->opacity());
+ m_scrollbarController->animate(11);
+ EXPECT_FLOAT_EQ(0, m_scrollbarLayer->opacity());
+
+}
+
+}
diff --git a/cc/CCScrollbarLayerImpl.cpp b/cc/CCScrollbarLayerImpl.cpp
new file mode 100644
index 0000000..b81844a
--- /dev/null
+++ b/cc/CCScrollbarLayerImpl.cpp
@@ -0,0 +1,193 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCScrollbarLayerImpl.h"
+
+#include "CCQuadSink.h"
+#include "CCScrollbarAnimationController.h"
+#include "CCTextureDrawQuad.h"
+
+using WebKit::WebRect;
+using WebKit::WebScrollbar;
+
+namespace WebCore {
+
+PassOwnPtr<CCScrollbarLayerImpl> CCScrollbarLayerImpl::create(int id)
+{
+ return adoptPtr(new CCScrollbarLayerImpl(id));
+}
+
+CCScrollbarLayerImpl::CCScrollbarLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_scrollbar(this)
+ , m_backTrackResourceId(0)
+ , m_foreTrackResourceId(0)
+ , m_thumbResourceId(0)
+ , m_scrollbarOverlayStyle(WebScrollbar::ScrollbarOverlayStyleDefault)
+ , m_orientation(WebScrollbar::Horizontal)
+ , m_controlSize(WebScrollbar::RegularScrollbar)
+ , m_pressedPart(WebScrollbar::NoPart)
+ , m_hoveredPart(WebScrollbar::NoPart)
+ , m_isScrollableAreaActive(false)
+ , m_isScrollViewScrollbar(false)
+ , m_enabled(false)
+ , m_isCustomScrollbar(false)
+ , m_isOverlayScrollbar(false)
+{
+}
+
+void CCScrollbarLayerImpl::setScrollbarGeometry(PassOwnPtr<WebKit::WebScrollbarThemeGeometry> geometry)
+{
+ m_geometry = geometry;
+}
+
+void CCScrollbarLayerImpl::setScrollbarData(const WebScrollbar* scrollbar)
+{
+ m_scrollbarOverlayStyle = scrollbar->scrollbarOverlayStyle();
+ m_orientation = scrollbar->orientation();
+ m_controlSize = scrollbar->controlSize();
+ m_pressedPart = scrollbar->pressedPart();
+ m_hoveredPart = scrollbar->hoveredPart();
+ m_isScrollableAreaActive = scrollbar->isScrollableAreaActive();
+ m_isScrollViewScrollbar = scrollbar->isScrollViewScrollbar();
+ m_enabled = scrollbar->enabled();
+ m_isCustomScrollbar = scrollbar->isCustomScrollbar();
+ m_isOverlayScrollbar = scrollbar->isOverlay();
+
+ scrollbar->getTickmarks(m_tickmarks);
+}
+
+static FloatRect toUVRect(const WebRect& r, const IntRect& bounds)
+{
+ return FloatRect(static_cast<float>(r.x) / bounds.width(), static_cast<float>(r.y) / bounds.height(),
+ static_cast<float>(r.width) / bounds.width(), static_cast<float>(r.height) / bounds.height());
+}
+
+void CCScrollbarLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ bool premultipledAlpha = false;
+ bool flipped = false;
+ FloatRect uvRect(0, 0, 1, 1);
+ IntRect boundsRect(IntPoint(), contentBounds());
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ WebRect thumbRect, backTrackRect, foreTrackRect;
+ m_geometry->splitTrack(&m_scrollbar, m_geometry->trackRect(&m_scrollbar), backTrackRect, thumbRect, foreTrackRect);
+ if (!m_geometry->hasThumb(&m_scrollbar))
+ thumbRect = WebRect();
+
+ if (m_thumbResourceId && !thumbRect.isEmpty()) {
+ OwnPtr<CCTextureDrawQuad> quad = CCTextureDrawQuad::create(sharedQuadState, IntRect(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height), m_thumbResourceId, premultipledAlpha, uvRect, flipped);
+ quad->setNeedsBlending();
+ quadSink.append(quad.release());
+ }
+
+ if (!m_backTrackResourceId)
+ return;
+
+ // We only paint the track in two parts if we were given a texture for the forward track part.
+ if (m_foreTrackResourceId && !foreTrackRect.isEmpty())
+ quadSink.append(CCTextureDrawQuad::create(sharedQuadState, IntRect(foreTrackRect.x, foreTrackRect.y, foreTrackRect.width, foreTrackRect.height), m_foreTrackResourceId, premultipledAlpha, toUVRect(foreTrackRect, boundsRect), flipped));
+
+ // Order matters here: since the back track texture is being drawn to the entire contents rect, we must append it after the thumb and
+ // fore track quads. The back track texture contains (and displays) the buttons.
+ if (!boundsRect.isEmpty())
+ quadSink.append(CCTextureDrawQuad::create(sharedQuadState, IntRect(boundsRect), m_backTrackResourceId, premultipledAlpha, uvRect, flipped));
+}
+
+void CCScrollbarLayerImpl::didLoseContext()
+{
+ m_backTrackResourceId = 0;
+ m_foreTrackResourceId = 0;
+ m_thumbResourceId = 0;
+}
+
+bool CCScrollbarLayerImpl::CCScrollbar::isOverlay() const
+{
+ return m_owner->m_isOverlayScrollbar;
+}
+
+int CCScrollbarLayerImpl::CCScrollbar::value() const
+{
+ return m_owner->m_currentPos;
+}
+
+WebKit::WebPoint CCScrollbarLayerImpl::CCScrollbar::location() const
+{
+ return WebKit::WebPoint();
+}
+
+WebKit::WebSize CCScrollbarLayerImpl::CCScrollbar::size() const
+{
+ return WebKit::WebSize(m_owner->contentBounds().width(), m_owner->contentBounds().height());
+}
+
+bool CCScrollbarLayerImpl::CCScrollbar::enabled() const
+{
+ return m_owner->m_enabled;
+}
+
+int CCScrollbarLayerImpl::CCScrollbar::maximum() const
+{
+ return m_owner->m_maximum;
+}
+
+int CCScrollbarLayerImpl::CCScrollbar::totalSize() const
+{
+ return m_owner->m_totalSize;
+}
+
+bool CCScrollbarLayerImpl::CCScrollbar::isScrollViewScrollbar() const
+{
+ return m_owner->m_isScrollViewScrollbar;
+}
+
+bool CCScrollbarLayerImpl::CCScrollbar::isScrollableAreaActive() const
+{
+ return m_owner->m_isScrollableAreaActive;
+}
+
+void CCScrollbarLayerImpl::CCScrollbar::getTickmarks(WebKit::WebVector<WebRect>& tickmarks) const
+{
+ tickmarks = m_owner->m_tickmarks;
+}
+
+WebScrollbar::ScrollbarControlSize CCScrollbarLayerImpl::CCScrollbar::controlSize() const
+{
+ return m_owner->m_controlSize;
+}
+
+WebScrollbar::ScrollbarPart CCScrollbarLayerImpl::CCScrollbar::pressedPart() const
+{
+ return m_owner->m_pressedPart;
+}
+
+WebScrollbar::ScrollbarPart CCScrollbarLayerImpl::CCScrollbar::hoveredPart() const
+{
+ return m_owner->m_hoveredPart;
+}
+
+WebScrollbar::ScrollbarOverlayStyle CCScrollbarLayerImpl::CCScrollbar::scrollbarOverlayStyle() const
+{
+ return m_owner->m_scrollbarOverlayStyle;
+}
+
+WebScrollbar::Orientation CCScrollbarLayerImpl::CCScrollbar::orientation() const
+{
+ return m_owner->m_orientation;
+}
+
+bool CCScrollbarLayerImpl::CCScrollbar::isCustomScrollbar() const
+{
+ return m_owner->m_isCustomScrollbar;
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCScrollbarLayerImpl.h b/cc/CCScrollbarLayerImpl.h
new file mode 100644
index 0000000..ebe3faa
--- /dev/null
+++ b/cc/CCScrollbarLayerImpl.h
@@ -0,0 +1,109 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCScrollbarLayerImpl_h
+#define CCScrollbarLayerImpl_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerImpl.h"
+#include <public/WebRect.h>
+#include <public/WebScrollbar.h>
+#include <public/WebScrollbarThemeGeometry.h>
+#include <public/WebVector.h>
+
+namespace WebCore {
+
+class ScrollView;
+
+class CCScrollbarLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCScrollbarLayerImpl> create(int id);
+
+ WebKit::WebScrollbarThemeGeometry* scrollbarGeometry() const { return m_geometry.get(); }
+ void setScrollbarGeometry(PassOwnPtr<WebKit::WebScrollbarThemeGeometry>);
+ void setScrollbarData(const WebKit::WebScrollbar*);
+
+ void setBackTrackResourceId(CCResourceProvider::ResourceId id) { m_backTrackResourceId = id; }
+ void setForeTrackResourceId(CCResourceProvider::ResourceId id) { m_foreTrackResourceId = id; }
+ void setThumbResourceId(CCResourceProvider::ResourceId id) { m_thumbResourceId = id; }
+
+ float currentPos() const { return m_currentPos; }
+ void setCurrentPos(float currentPos) { m_currentPos = currentPos; }
+
+ int totalSize() const { return m_totalSize; }
+ void setTotalSize(int totalSize) { m_totalSize = totalSize; }
+
+ int maximum() const { return m_maximum; }
+ void setMaximum(int maximum) { m_maximum = maximum; }
+
+ WebKit::WebScrollbar::Orientation orientation() const { return m_orientation; }
+
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+
+ virtual void didLoseContext() OVERRIDE;
+
+protected:
+ explicit CCScrollbarLayerImpl(int id);
+
+private:
+ // nested class only to avoid namespace problem
+ class CCScrollbar : public WebKit::WebScrollbar {
+ public:
+ explicit CCScrollbar(CCScrollbarLayerImpl* owner) : m_owner(owner) { }
+
+ // WebScrollbar implementation
+ virtual bool isOverlay() const;
+ virtual int value() const;
+ virtual WebKit::WebPoint location() const;
+ virtual WebKit::WebSize size() const;
+ virtual bool enabled() const;
+ virtual int maximum() const;
+ virtual int totalSize() const;
+ virtual bool isScrollViewScrollbar() const;
+ virtual bool isScrollableAreaActive() const;
+ virtual void getTickmarks(WebKit::WebVector<WebKit::WebRect>& tickmarks) const;
+ virtual WebScrollbar::ScrollbarControlSize controlSize() const;
+ virtual WebScrollbar::ScrollbarPart pressedPart() const;
+ virtual WebScrollbar::ScrollbarPart hoveredPart() const;
+ virtual WebScrollbar::ScrollbarOverlayStyle scrollbarOverlayStyle() const;
+ virtual WebScrollbar::Orientation orientation() const;
+ virtual bool isCustomScrollbar() const;
+
+ private:
+ CCScrollbarLayerImpl* m_owner;
+
+ };
+
+ CCScrollbar m_scrollbar;
+
+ CCResourceProvider::ResourceId m_backTrackResourceId;
+ CCResourceProvider::ResourceId m_foreTrackResourceId;
+ CCResourceProvider::ResourceId m_thumbResourceId;
+
+ OwnPtr<WebKit::WebScrollbarThemeGeometry> m_geometry;
+
+ // Data to implement CCScrollbar
+ WebKit::WebScrollbar::ScrollbarOverlayStyle m_scrollbarOverlayStyle;
+ WebKit::WebVector<WebKit::WebRect> m_tickmarks;
+ WebKit::WebScrollbar::Orientation m_orientation;
+ WebKit::WebScrollbar::ScrollbarControlSize m_controlSize;
+ WebKit::WebScrollbar::ScrollbarPart m_pressedPart;
+ WebKit::WebScrollbar::ScrollbarPart m_hoveredPart;
+
+ float m_currentPos;
+ int m_totalSize;
+ int m_maximum;
+
+ bool m_isScrollableAreaActive;
+ bool m_isScrollViewScrollbar;
+ bool m_enabled;
+ bool m_isCustomScrollbar;
+ bool m_isOverlayScrollbar;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/CCSettings.cpp b/cc/CCSettings.cpp
new file mode 100644
index 0000000..32c023c
--- /dev/null
+++ b/cc/CCSettings.cpp
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSettings.h"
+
+namespace {
+static bool s_perTilePaintingEnabled = false;
+static bool s_partialSwapEnabled = false;
+static bool s_acceleratedAnimationEnabled = false;
+} // namespace
+
+namespace WebCore {
+
+bool CCSettings::perTilePaintingEnabled() { return s_perTilePaintingEnabled; }
+void CCSettings::setPerTilePaintingEnabled(bool enabled) { s_perTilePaintingEnabled = enabled; }
+
+bool CCSettings::partialSwapEnabled() { return s_partialSwapEnabled; }
+void CCSettings::setPartialSwapEnabled(bool enabled) { s_partialSwapEnabled = enabled; }
+
+bool CCSettings::acceleratedAnimationEnabled() { return s_acceleratedAnimationEnabled; }
+void CCSettings::setAcceleratedAnimationEnabled(bool enabled) { s_acceleratedAnimationEnabled = enabled; }
+
+void CCSettings::reset()
+{
+ s_perTilePaintingEnabled = false;
+ s_partialSwapEnabled = false;
+ s_acceleratedAnimationEnabled = false;
+}
+
+} // namespace WebCore
diff --git a/cc/CCSettings.h b/cc/CCSettings.h
new file mode 100644
index 0000000..5bfbb86
--- /dev/null
+++ b/cc/CCSettings.h
@@ -0,0 +1,31 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSettings_h
+#define CCSettings_h
+
+#include "IntSize.h"
+
+namespace WebCore {
+
+class CCSettings {
+public:
+ static bool perTilePaintingEnabled();
+ static bool partialSwapEnabled();
+ static bool acceleratedAnimationEnabled();
+
+ // These setters should only be used on the main thread before the layer
+ // renderer is initialized.
+ static void setPerTilePaintingEnabled(bool);
+ static void setPartialSwapEnabled(bool);
+ static void setAcceleratedAnimationEnabled(bool);
+
+ // These settings are meant to be set only once, and only read thereafter.
+ // This function is only for resetting settings in tests.
+ static void reset();
+};
+
+} // namespace WebCore
+
+#endif // CCSettings_h
diff --git a/cc/CCSharedQuadState.cpp b/cc/CCSharedQuadState.cpp
new file mode 100644
index 0000000..05a6622
--- /dev/null
+++ b/cc/CCSharedQuadState.cpp
@@ -0,0 +1,28 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSharedQuadState.h"
+
+#include "FloatQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCSharedQuadState> CCSharedQuadState::create(const WebKit::WebTransformationMatrix& quadTransform, const IntRect& visibleContentRect, const IntRect& clippedRectInTarget, float opacity, bool opaque)
+{
+ return adoptPtr(new CCSharedQuadState(quadTransform, visibleContentRect, clippedRectInTarget, opacity, opaque));
+}
+
+CCSharedQuadState::CCSharedQuadState(const WebKit::WebTransformationMatrix& quadTransform, const IntRect& visibleContentRect, const IntRect& clippedRectInTarget, float opacity, bool opaque)
+ : id(-1)
+ , quadTransform(quadTransform)
+ , visibleContentRect(visibleContentRect)
+ , clippedRectInTarget(clippedRectInTarget)
+ , opacity(opacity)
+ , opaque(opaque)
+{
+}
+
+}
diff --git a/cc/CCSharedQuadState.h b/cc/CCSharedQuadState.h
new file mode 100644
index 0000000..9e591b7
--- /dev/null
+++ b/cc/CCSharedQuadState.h
@@ -0,0 +1,32 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSharedQuadState_h
+#define CCSharedQuadState_h
+
+#include "IntRect.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+struct CCSharedQuadState {
+ int id;
+
+ // Transforms from quad's original content space to its target content space.
+ WebKit::WebTransformationMatrix quadTransform;
+ // This rect lives in the content space for the quad's originating layer.
+ IntRect visibleContentRect;
+ IntRect clippedRectInTarget;
+ float opacity;
+ bool opaque;
+
+ static PassOwnPtr<CCSharedQuadState> create(const WebKit::WebTransformationMatrix& quadTransform, const IntRect& visibleContentRect, const IntRect& clippedRectInTarget, float opacity, bool opaque);
+ CCSharedQuadState(const WebKit::WebTransformationMatrix& quadTransform, const IntRect& visibleContentRect, const IntRect& clippedRectInTarget, float opacity, bool opaque);
+ bool isLayerAxisAlignedIntRect() const;
+};
+
+}
+
+#endif
diff --git a/cc/CCSingleThreadProxy.cpp b/cc/CCSingleThreadProxy.cpp
new file mode 100644
index 0000000..1ecee49
--- /dev/null
+++ b/cc/CCSingleThreadProxy.cpp
@@ -0,0 +1,340 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSingleThreadProxy.h"
+
+#include "CCDrawQuad.h"
+#include "CCGraphicsContext.h"
+#include "CCLayerTreeHost.h"
+#include "CCTextureUpdateController.h"
+#include "CCTimer.h"
+#include "TraceEvent.h"
+#include <wtf/CurrentTime.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+PassOwnPtr<CCProxy> CCSingleThreadProxy::create(CCLayerTreeHost* layerTreeHost)
+{
+ return adoptPtr(new CCSingleThreadProxy(layerTreeHost));
+}
+
+CCSingleThreadProxy::CCSingleThreadProxy(CCLayerTreeHost* layerTreeHost)
+ : m_layerTreeHost(layerTreeHost)
+ , m_contextLost(false)
+ , m_compositorIdentifier(-1)
+ , m_rendererInitialized(false)
+ , m_nextFrameIsNewlyCommittedFrame(false)
+{
+ TRACE_EVENT0("cc", "CCSingleThreadProxy::CCSingleThreadProxy");
+ ASSERT(CCProxy::isMainThread());
+}
+
+void CCSingleThreadProxy::start()
+{
+ DebugScopedSetImplThread impl;
+ m_layerTreeHostImpl = m_layerTreeHost->createLayerTreeHostImpl(this);
+}
+
+CCSingleThreadProxy::~CCSingleThreadProxy()
+{
+ TRACE_EVENT0("cc", "CCSingleThreadProxy::~CCSingleThreadProxy");
+ ASSERT(CCProxy::isMainThread());
+ ASSERT(!m_layerTreeHostImpl && !m_layerTreeHost); // make sure stop() got called.
+}
+
+bool CCSingleThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect)
+{
+ TRACE_EVENT0("cc", "CCSingleThreadProxy::compositeAndReadback");
+ ASSERT(CCProxy::isMainThread());
+
+ if (!commitAndComposite())
+ return false;
+
+ m_layerTreeHostImpl->readback(pixels, rect);
+
+ if (m_layerTreeHostImpl->isContextLost())
+ return false;
+
+ m_layerTreeHostImpl->swapBuffers();
+ didSwapFrame();
+
+ return true;
+}
+
+void CCSingleThreadProxy::startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double duration)
+{
+ m_layerTreeHostImpl->startPageScaleAnimation(targetPosition, useAnchor, scale, monotonicallyIncreasingTime(), duration);
+}
+
+void CCSingleThreadProxy::finishAllRendering()
+{
+ ASSERT(CCProxy::isMainThread());
+ {
+ DebugScopedSetImplThread impl;
+ m_layerTreeHostImpl->finishAllRendering();
+ }
+}
+
+bool CCSingleThreadProxy::isStarted() const
+{
+ ASSERT(CCProxy::isMainThread());
+ return m_layerTreeHostImpl;
+}
+
+bool CCSingleThreadProxy::initializeContext()
+{
+ ASSERT(CCProxy::isMainThread());
+ OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext();
+ if (!context)
+ return false;
+ m_contextBeforeInitialization = context.release();
+ return true;
+}
+
+void CCSingleThreadProxy::setSurfaceReady()
+{
+ // Scheduling is controlled by the embedder in the single thread case, so nothing to do.
+}
+
+void CCSingleThreadProxy::setVisible(bool visible)
+{
+ DebugScopedSetImplThread impl;
+ m_layerTreeHostImpl->setVisible(visible);
+}
+
+bool CCSingleThreadProxy::initializeRenderer()
+{
+ ASSERT(CCProxy::isMainThread());
+ ASSERT(m_contextBeforeInitialization);
+ {
+ DebugScopedSetImplThread impl;
+ bool ok = m_layerTreeHostImpl->initializeRenderer(m_contextBeforeInitialization.release(), UnthrottledUploader);
+ if (ok) {
+ m_rendererInitialized = true;
+ m_RendererCapabilitiesForMainThread = m_layerTreeHostImpl->rendererCapabilities();
+ }
+
+ return ok;
+ }
+}
+
+bool CCSingleThreadProxy::recreateContext()
+{
+ TRACE_EVENT0("cc", "CCSingleThreadProxy::recreateContext");
+ ASSERT(CCProxy::isMainThread());
+ ASSERT(m_contextLost);
+
+ OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext();
+ if (!context)
+ return false;
+
+ bool initialized;
+ {
+ DebugScopedSetImplThread impl;
+ if (!m_layerTreeHostImpl->contentsTexturesPurged())
+ m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
+ initialized = m_layerTreeHostImpl->initializeRenderer(context.release(), UnthrottledUploader);
+ if (initialized) {
+ m_RendererCapabilitiesForMainThread = m_layerTreeHostImpl->rendererCapabilities();
+ }
+ }
+
+ if (initialized)
+ m_contextLost = false;
+
+ return initialized;
+}
+
+void CCSingleThreadProxy::implSideRenderingStats(CCRenderingStats& stats)
+{
+ m_layerTreeHostImpl->renderingStats(stats);
+}
+
+const RendererCapabilities& CCSingleThreadProxy::rendererCapabilities() const
+{
+ ASSERT(m_rendererInitialized);
+ // Note: this gets called during the commit by the "impl" thread
+ return m_RendererCapabilitiesForMainThread;
+}
+
+void CCSingleThreadProxy::loseContext()
+{
+ ASSERT(CCProxy::isMainThread());
+ m_layerTreeHost->didLoseContext();
+ m_contextLost = true;
+}
+
+void CCSingleThreadProxy::setNeedsAnimate()
+{
+ // CCThread-only feature
+ ASSERT_NOT_REACHED();
+}
+
+void CCSingleThreadProxy::doCommit(CCTextureUpdateQueue& queue)
+{
+ ASSERT(CCProxy::isMainThread());
+ // Commit immediately
+ {
+ DebugScopedSetMainThreadBlocked mainThreadBlocked;
+ DebugScopedSetImplThread impl;
+
+ m_layerTreeHostImpl->beginCommit();
+
+ m_layerTreeHost->beginCommitOnImplThread(m_layerTreeHostImpl.get());
+
+ // CCTextureUpdateController::updateTextures is non-blocking and will
+ // return without updating any textures if the uploader is busy. This
+ // shouldn't be a problem here as the throttled uploader isn't used in
+ // single thread mode. For correctness, loop until no more updates are
+ // pending.
+ while (queue.hasMoreUpdates())
+ CCTextureUpdateController::updateTextures(m_layerTreeHostImpl->resourceProvider(), m_layerTreeHostImpl->renderer()->textureCopier(), m_layerTreeHostImpl->renderer()->textureUploader(), &queue, maxPartialTextureUpdates());
+
+ m_layerTreeHost->finishCommitOnImplThread(m_layerTreeHostImpl.get());
+
+ m_layerTreeHostImpl->commitComplete();
+
+#if !ASSERT_DISABLED
+ // In the single-threaded case, the scroll deltas should never be
+ // touched on the impl layer tree.
+ OwnPtr<CCScrollAndScaleSet> scrollInfo = m_layerTreeHostImpl->processScrollDeltas();
+ ASSERT(!scrollInfo->scrolls.size());
+#endif
+ }
+ m_layerTreeHost->commitComplete();
+ m_nextFrameIsNewlyCommittedFrame = true;
+}
+
+void CCSingleThreadProxy::setNeedsCommit()
+{
+ ASSERT(CCProxy::isMainThread());
+ m_layerTreeHost->scheduleComposite();
+}
+
+void CCSingleThreadProxy::setNeedsRedraw()
+{
+ // FIXME: Once we move render_widget scheduling into this class, we can
+ // treat redraw requests more efficiently than commitAndRedraw requests.
+ m_layerTreeHostImpl->setFullRootLayerDamage();
+ setNeedsCommit();
+}
+
+bool CCSingleThreadProxy::commitRequested() const
+{
+ return false;
+}
+
+void CCSingleThreadProxy::didAddAnimation()
+{
+}
+
+void CCSingleThreadProxy::stop()
+{
+ TRACE_EVENT0("cc", "CCSingleThreadProxy::stop");
+ ASSERT(CCProxy::isMainThread());
+ {
+ DebugScopedSetMainThreadBlocked mainThreadBlocked;
+ DebugScopedSetImplThread impl;
+
+ if (!m_layerTreeHostImpl->contentsTexturesPurged())
+ m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
+ m_layerTreeHostImpl.clear();
+ }
+ m_layerTreeHost = 0;
+}
+
+void CCSingleThreadProxy::postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector> events, double wallClockTime)
+{
+ ASSERT(CCProxy::isImplThread());
+ DebugScopedSetMainThread main;
+ m_layerTreeHost->setAnimationEvents(events, wallClockTime);
+}
+
+// Called by the legacy scheduling path (e.g. where render_widget does the scheduling)
+void CCSingleThreadProxy::compositeImmediately()
+{
+ if (commitAndComposite()) {
+ m_layerTreeHostImpl->swapBuffers();
+ didSwapFrame();
+ }
+}
+
+void CCSingleThreadProxy::forceSerializeOnSwapBuffers()
+{
+ {
+ DebugScopedSetImplThread impl;
+ if (m_rendererInitialized)
+ m_layerTreeHostImpl->renderer()->doNoOp();
+ }
+}
+
+bool CCSingleThreadProxy::commitAndComposite()
+{
+ ASSERT(CCProxy::isMainThread());
+
+
+ if (!m_layerTreeHost->initializeRendererIfNeeded())
+ return false;
+
+ if (m_layerTreeHostImpl->contentsTexturesPurged())
+ m_layerTreeHost->evictAllContentTextures();
+
+ CCTextureUpdateQueue queue;
+ m_layerTreeHost->updateLayers(queue, m_layerTreeHostImpl->memoryAllocationLimitBytes());
+ m_layerTreeHostImpl->resetContentsTexturesPurged();
+
+ m_layerTreeHost->willCommit();
+ doCommit(queue);
+ bool result = doComposite();
+ m_layerTreeHost->didBeginFrame();
+ return result;
+}
+
+bool CCSingleThreadProxy::doComposite()
+{
+ ASSERT(!m_contextLost);
+ {
+ DebugScopedSetImplThread impl;
+
+ if (!m_layerTreeHostImpl->visible())
+ return false;
+
+ double monotonicTime = monotonicallyIncreasingTime();
+ double wallClockTime = currentTime();
+ m_layerTreeHostImpl->animate(monotonicTime, wallClockTime);
+
+ // We guard prepareToDraw() with canDraw() because it always returns a valid frame, so can only
+ // be used when such a frame is possible. Since drawLayers() depends on the result of
+ // prepareToDraw(), it is guarded on canDraw() as well.
+ if (!m_layerTreeHostImpl->canDraw())
+ return false;
+
+ CCLayerTreeHostImpl::FrameData frame;
+ m_layerTreeHostImpl->prepareToDraw(frame);
+ m_layerTreeHostImpl->drawLayers(frame);
+ m_layerTreeHostImpl->didDrawAllLayers(frame);
+ }
+
+ if (m_layerTreeHostImpl->isContextLost()) {
+ m_contextLost = true;
+ m_layerTreeHost->didLoseContext();
+ return false;
+ }
+
+ return true;
+}
+
+void CCSingleThreadProxy::didSwapFrame()
+{
+ if (m_nextFrameIsNewlyCommittedFrame) {
+ m_nextFrameIsNewlyCommittedFrame = false;
+ m_layerTreeHost->didCommitAndDrawFrame();
+ }
+}
+
+}
diff --git a/cc/CCSingleThreadProxy.h b/cc/CCSingleThreadProxy.h
new file mode 100644
index 0000000..37a970c
--- /dev/null
+++ b/cc/CCSingleThreadProxy.h
@@ -0,0 +1,122 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSingleThreadProxy_h
+#define CCSingleThreadProxy_h
+
+#include "CCAnimationEvents.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCProxy.h"
+#include <limits>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class CCLayerTreeHost;
+
+class CCSingleThreadProxy : public CCProxy, CCLayerTreeHostImplClient {
+public:
+ static PassOwnPtr<CCProxy> create(CCLayerTreeHost*);
+ virtual ~CCSingleThreadProxy();
+
+ // CCProxy implementation
+ virtual bool compositeAndReadback(void *pixels, const IntRect&) OVERRIDE;
+ virtual void startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double duration) OVERRIDE;
+ virtual void finishAllRendering() OVERRIDE;
+ virtual bool isStarted() const OVERRIDE;
+ virtual bool initializeContext() OVERRIDE;
+ virtual void setSurfaceReady() OVERRIDE;
+ virtual void setVisible(bool) OVERRIDE;
+ virtual bool initializeRenderer() OVERRIDE;
+ virtual bool recreateContext() OVERRIDE;
+ virtual int compositorIdentifier() const OVERRIDE { return m_compositorIdentifier; }
+ virtual void implSideRenderingStats(CCRenderingStats&) OVERRIDE;
+ virtual const RendererCapabilities& rendererCapabilities() const OVERRIDE;
+ virtual void loseContext() OVERRIDE;
+ virtual void setNeedsAnimate() OVERRIDE;
+ virtual void setNeedsCommit() OVERRIDE;
+ virtual void setNeedsRedraw() OVERRIDE;
+ virtual bool commitRequested() const OVERRIDE;
+ virtual void didAddAnimation() OVERRIDE;
+ virtual void start() OVERRIDE;
+ virtual void stop() OVERRIDE;
+ virtual size_t maxPartialTextureUpdates() const OVERRIDE { return std::numeric_limits<size_t>::max(); }
+ virtual void acquireLayerTextures() OVERRIDE { }
+ virtual void forceSerializeOnSwapBuffers() OVERRIDE;
+
+ // CCLayerTreeHostImplClient implementation
+ virtual void didLoseContextOnImplThread() OVERRIDE { }
+ virtual void onSwapBuffersCompleteOnImplThread() OVERRIDE { ASSERT_NOT_REACHED(); }
+ virtual void onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds) OVERRIDE { }
+ virtual void setNeedsRedrawOnImplThread() OVERRIDE { m_layerTreeHost->scheduleComposite(); }
+ virtual void setNeedsCommitOnImplThread() OVERRIDE { m_layerTreeHost->scheduleComposite(); }
+ virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) OVERRIDE;
+
+ // Called by the legacy path where RenderWidget does the scheduling.
+ void compositeImmediately();
+
+private:
+ explicit CCSingleThreadProxy(CCLayerTreeHost*);
+
+ bool commitAndComposite();
+ void doCommit(CCTextureUpdateQueue&);
+ bool doComposite();
+ void didSwapFrame();
+
+ // Accessed on main thread only.
+ CCLayerTreeHost* m_layerTreeHost;
+ bool m_contextLost;
+ int m_compositorIdentifier;
+
+ // Holds on to the context between initializeContext() and initializeRenderer() calls. Shouldn't
+ // be used for anything else.
+ OwnPtr<CCGraphicsContext> m_contextBeforeInitialization;
+
+ // Used on the CCThread, but checked on main thread during initialization/shutdown.
+ OwnPtr<CCLayerTreeHostImpl> m_layerTreeHostImpl;
+ bool m_rendererInitialized;
+ RendererCapabilities m_RendererCapabilitiesForMainThread;
+
+ bool m_nextFrameIsNewlyCommittedFrame;
+};
+
+// For use in the single-threaded case. In debug builds, it pretends that the
+// code is running on the thread to satisfy assertion checks.
+class DebugScopedSetImplThread {
+public:
+ DebugScopedSetImplThread()
+ {
+#if !ASSERT_DISABLED
+ CCProxy::setCurrentThreadIsImplThread(true);
+#endif
+ }
+ ~DebugScopedSetImplThread()
+ {
+#if !ASSERT_DISABLED
+ CCProxy::setCurrentThreadIsImplThread(false);
+#endif
+ }
+};
+
+// For use in the single-threaded case. In debug builds, it pretends that the
+// code is running on the main thread to satisfy assertion checks.
+class DebugScopedSetMainThread {
+public:
+ DebugScopedSetMainThread()
+ {
+#if !ASSERT_DISABLED
+ CCProxy::setCurrentThreadIsImplThread(false);
+#endif
+ }
+ ~DebugScopedSetMainThread()
+ {
+#if !ASSERT_DISABLED
+ CCProxy::setCurrentThreadIsImplThread(true);
+#endif
+ }
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCSolidColorDrawQuad.cpp b/cc/CCSolidColorDrawQuad.cpp
new file mode 100644
index 0000000..26f0d8a
--- /dev/null
+++ b/cc/CCSolidColorDrawQuad.cpp
@@ -0,0 +1,32 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSolidColorDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCSolidColorDrawQuad> CCSolidColorDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, SkColor color)
+{
+ return adoptPtr(new CCSolidColorDrawQuad(sharedQuadState, quadRect, color));
+}
+
+CCSolidColorDrawQuad::CCSolidColorDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, SkColor color)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::SolidColor, quadRect)
+ , m_color(color)
+{
+ if (SkColorGetA(m_color) < 255)
+ m_quadOpaque = false;
+ else
+ m_opaqueRect = quadRect;
+}
+
+const CCSolidColorDrawQuad* CCSolidColorDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::SolidColor);
+ return static_cast<const CCSolidColorDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCSolidColorDrawQuad.h b/cc/CCSolidColorDrawQuad.h
new file mode 100644
index 0000000..a14c81a
--- /dev/null
+++ b/cc/CCSolidColorDrawQuad.h
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSolidColorDrawQuad_h
+#define CCSolidColorDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "SkColor.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCSolidColorDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCSolidColorDrawQuad> create(const CCSharedQuadState*, const IntRect&, SkColor);
+
+ SkColor color() const { return m_color; };
+
+ static const CCSolidColorDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCSolidColorDrawQuad(const CCSharedQuadState*, const IntRect&, SkColor);
+
+ SkColor m_color;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCSolidColorLayerImpl.cpp b/cc/CCSolidColorLayerImpl.cpp
new file mode 100644
index 0000000..4609d56
--- /dev/null
+++ b/cc/CCSolidColorLayerImpl.cpp
@@ -0,0 +1,49 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCSolidColorLayerImpl.h"
+
+#include "CCQuadSink.h"
+#include "CCSolidColorDrawQuad.h"
+#include <wtf/MathExtras.h>
+#include <wtf/text/WTFString.h>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+CCSolidColorLayerImpl::CCSolidColorLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_tileSize(256)
+{
+}
+
+CCSolidColorLayerImpl::~CCSolidColorLayerImpl()
+{
+}
+
+void CCSolidColorLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ // We create a series of smaller quads instead of just one large one so that the
+ // culler can reduce the total pixels drawn.
+ int width = contentBounds().width();
+ int height = contentBounds().height();
+ for (int x = 0; x < width; x += m_tileSize) {
+ for (int y = 0; y < height; y += m_tileSize) {
+ IntRect solidTileRect(x, y, min(width - x, m_tileSize), min(height - y, m_tileSize));
+ quadSink.append(CCSolidColorDrawQuad::create(sharedQuadState, solidTileRect, backgroundColor()));
+ }
+ }
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCSolidColorLayerImpl.h b/cc/CCSolidColorLayerImpl.h
new file mode 100644
index 0000000..ea1000a
--- /dev/null
+++ b/cc/CCSolidColorLayerImpl.h
@@ -0,0 +1,34 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSolidColorLayerImpl_h
+#define CCSolidColorLayerImpl_h
+
+#include "CCLayerImpl.h"
+#include <public/WebTransformationMatrix.h>
+
+namespace WebCore {
+
+class CCSolidColorLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCSolidColorLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCSolidColorLayerImpl(id));
+ }
+ virtual ~CCSolidColorLayerImpl();
+
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+
+protected:
+ explicit CCSolidColorLayerImpl(int id);
+
+private:
+ virtual const char* layerTypeAsString() const OVERRIDE { return "SolidColorLayer"; }
+
+ const int m_tileSize;
+};
+
+}
+
+#endif // CCSolidColorLayerImpl_h
diff --git a/cc/CCSolidColorLayerImplTest.cpp b/cc/CCSolidColorLayerImplTest.cpp
new file mode 100644
index 0000000..18db936
--- /dev/null
+++ b/cc/CCSolidColorLayerImplTest.cpp
@@ -0,0 +1,92 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCSolidColorLayerImpl.h"
+
+#include "CCLayerTestCommon.h"
+#include "CCSingleThreadProxy.h"
+#include "CCSolidColorDrawQuad.h"
+#include "MockCCQuadCuller.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace CCLayerTestCommon;
+
+namespace {
+
+TEST(CCSolidColorLayerImplTest, verifyTilingCompleteAndNoOverlap)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ MockCCQuadCuller quadCuller;
+ IntSize layerSize = IntSize(800, 600);
+ IntRect visibleContentRect = IntRect(IntPoint(), layerSize);
+
+ OwnPtr<CCSolidColorLayerImpl> layer = CCSolidColorLayerImpl::create(1);
+ layer->setVisibleContentRect(visibleContentRect);
+ layer->setBounds(layerSize);
+ layer->setContentBounds(layerSize);
+ layer->createRenderSurface();
+ layer->setRenderTarget(layer.get());
+
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+
+ verifyQuadsExactlyCoverRect(quadCuller.quadList(), visibleContentRect);
+}
+
+TEST(CCSolidColorLayerImplTest, verifyCorrectBackgroundColorInQuad)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ SkColor testColor = 0xFFA55AFF;
+
+ MockCCQuadCuller quadCuller;
+ IntSize layerSize = IntSize(100, 100);
+ IntRect visibleContentRect = IntRect(IntPoint(), layerSize);
+
+ OwnPtr<CCSolidColorLayerImpl> layer = CCSolidColorLayerImpl::create(1);
+ layer->setVisibleContentRect(visibleContentRect);
+ layer->setBounds(layerSize);
+ layer->setContentBounds(layerSize);
+ layer->setBackgroundColor(testColor);
+ layer->createRenderSurface();
+ layer->setRenderTarget(layer.get());
+
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(CCSolidColorDrawQuad::materialCast(quadCuller.quadList()[0].get())->color(), testColor);
+}
+
+TEST(CCSolidColorLayerImplTest, verifyCorrectOpacityInQuad)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ const float opacity = 0.5f;
+
+ MockCCQuadCuller quadCuller;
+ IntSize layerSize = IntSize(100, 100);
+ IntRect visibleContentRect = IntRect(IntPoint(), layerSize);
+
+ OwnPtr<CCSolidColorLayerImpl> layer = CCSolidColorLayerImpl::create(1);
+ layer->setVisibleContentRect(visibleContentRect);
+ layer->setBounds(layerSize);
+ layer->setContentBounds(layerSize);
+ layer->setDrawOpacity(opacity);
+ layer->createRenderSurface();
+ layer->setRenderTarget(layer.get());
+
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(opacity, CCSolidColorDrawQuad::materialCast(quadCuller.quadList()[0].get())->opacity());
+}
+
+} // namespace
diff --git a/cc/CCStreamVideoDrawQuad.cpp b/cc/CCStreamVideoDrawQuad.cpp
new file mode 100644
index 0000000..c766f98
--- /dev/null
+++ b/cc/CCStreamVideoDrawQuad.cpp
@@ -0,0 +1,29 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCStreamVideoDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCStreamVideoDrawQuad> CCStreamVideoDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, unsigned textureId, const WebKit::WebTransformationMatrix& matrix)
+{
+ return adoptPtr(new CCStreamVideoDrawQuad(sharedQuadState, quadRect, textureId, matrix));
+}
+
+CCStreamVideoDrawQuad::CCStreamVideoDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, unsigned textureId, const WebKit::WebTransformationMatrix& matrix)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::StreamVideoContent, quadRect)
+ , m_textureId(textureId)
+ , m_matrix(matrix)
+{
+}
+
+const CCStreamVideoDrawQuad* CCStreamVideoDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::StreamVideoContent);
+ return static_cast<const CCStreamVideoDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCStreamVideoDrawQuad.h b/cc/CCStreamVideoDrawQuad.h
new file mode 100644
index 0000000..2ad0ed8
--- /dev/null
+++ b/cc/CCStreamVideoDrawQuad.h
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCStreamVideoDrawQuad_h
+#define CCStreamVideoDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include <public/WebTransformationMatrix.h>
+
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCStreamVideoDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCStreamVideoDrawQuad> create(const CCSharedQuadState*, const IntRect&, unsigned textureId, const WebKit::WebTransformationMatrix&);
+
+ unsigned textureId() const { return m_textureId; }
+ const WebKit::WebTransformationMatrix& matrix() const { return m_matrix; }
+
+ static const CCStreamVideoDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCStreamVideoDrawQuad(const CCSharedQuadState*, const IntRect&, unsigned textureId, const WebKit::WebTransformationMatrix&);
+
+ unsigned m_textureId;
+ WebKit::WebTransformationMatrix m_matrix;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCTexture.cpp b/cc/CCTexture.cpp
new file mode 100644
index 0000000..bcd1ea3
--- /dev/null
+++ b/cc/CCTexture.cpp
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTexture.h"
+
+namespace WebCore {
+
+void CCTexture::setDimensions(const IntSize& size, GC3Denum format)
+{
+ m_size = size;
+ m_format = format;
+}
+
+size_t CCTexture::bytes() const
+{
+ if (m_size.isEmpty())
+ return 0u;
+
+ return memorySizeBytes(m_size, m_format);
+}
+
+size_t CCTexture::memorySizeBytes(const IntSize& size, GC3Denum format)
+{
+ unsigned int componentsPerPixel;
+ unsigned int bytesPerComponent;
+ if (!GraphicsContext3D::computeFormatAndTypeParameters(format, GraphicsContext3D::UNSIGNED_BYTE, &componentsPerPixel, &bytesPerComponent)) {
+ ASSERT_NOT_REACHED();
+ return 0u;
+ }
+ return componentsPerPixel * bytesPerComponent * size.width() * size.height();
+}
+
+}
diff --git a/cc/CCTexture.h b/cc/CCTexture.h
new file mode 100644
index 0000000..02699d27
--- /dev/null
+++ b/cc/CCTexture.h
@@ -0,0 +1,42 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTexture_h
+#define CCTexture_h
+
+#include "CCResourceProvider.h"
+#include "CCTexture.h"
+#include "GraphicsContext3D.h"
+#include "IntSize.h"
+
+namespace WebCore {
+
+class CCTexture {
+public:
+ CCTexture() : m_id(0) { }
+ CCTexture(unsigned id, IntSize size, GC3Denum format)
+ : m_id(id)
+ , m_size(size)
+ , m_format(format) { }
+
+ CCResourceProvider::ResourceId id() const { return m_id; }
+ const IntSize& size() const { return m_size; }
+ GC3Denum format() const { return m_format; }
+
+ void setId(CCResourceProvider::ResourceId id) { m_id = id; }
+ void setDimensions(const IntSize&, GC3Denum format);
+
+ size_t bytes() const;
+
+ static size_t memorySizeBytes(const IntSize&, GC3Denum format);
+
+private:
+ CCResourceProvider::ResourceId m_id;
+ IntSize m_size;
+ GC3Denum m_format;
+};
+
+}
+
+#endif
diff --git a/cc/CCTextureDrawQuad.cpp b/cc/CCTextureDrawQuad.cpp
new file mode 100644
index 0000000..4451ad2
--- /dev/null
+++ b/cc/CCTextureDrawQuad.cpp
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTextureDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCTextureDrawQuad> CCTextureDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, unsigned resourceId, bool premultipliedAlpha, const FloatRect& uvRect, bool flipped)
+{
+ return adoptPtr(new CCTextureDrawQuad(sharedQuadState, quadRect, resourceId, premultipliedAlpha, uvRect, flipped));
+}
+
+CCTextureDrawQuad::CCTextureDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, unsigned resourceId, bool premultipliedAlpha, const FloatRect& uvRect, bool flipped)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::TextureContent, quadRect)
+ , m_resourceId(resourceId)
+ , m_premultipliedAlpha(premultipliedAlpha)
+ , m_uvRect(uvRect)
+ , m_flipped(flipped)
+{
+}
+
+void CCTextureDrawQuad::setNeedsBlending()
+{
+ m_needsBlending = true;
+}
+
+const CCTextureDrawQuad* CCTextureDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::TextureContent);
+ return static_cast<const CCTextureDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCTextureDrawQuad.h b/cc/CCTextureDrawQuad.h
new file mode 100644
index 0000000..fd0a6d0
--- /dev/null
+++ b/cc/CCTextureDrawQuad.h
@@ -0,0 +1,41 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTextureDrawQuad_h
+#define CCTextureDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "FloatRect.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCTextureDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCTextureDrawQuad> create(const CCSharedQuadState*, const IntRect&, unsigned resourceId, bool premultipliedAlpha, const FloatRect& uvRect, bool flipped);
+ FloatRect uvRect() const { return m_uvRect; }
+
+ unsigned resourceId() const { return m_resourceId; }
+ bool premultipliedAlpha() const { return m_premultipliedAlpha; }
+ bool flipped() const { return m_flipped; }
+
+ void setNeedsBlending();
+
+ static const CCTextureDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCTextureDrawQuad(const CCSharedQuadState*, const IntRect&, unsigned resourceId, bool premultipliedAlpha, const FloatRect& uvRect, bool flipped);
+
+ unsigned m_resourceId;
+ bool m_premultipliedAlpha;
+ FloatRect m_uvRect;
+ bool m_flipped;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCTextureLayerImpl.cpp b/cc/CCTextureLayerImpl.cpp
new file mode 100644
index 0000000..943c423
--- /dev/null
+++ b/cc/CCTextureLayerImpl.cpp
@@ -0,0 +1,79 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCTextureLayerImpl.h"
+
+#include "CCQuadSink.h"
+#include "CCRenderer.h"
+#include "CCTextureDrawQuad.h"
+#include "TextStream.h"
+
+namespace WebCore {
+
+CCTextureLayerImpl::CCTextureLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_textureId(0)
+ , m_externalTextureResource(0)
+ , m_premultipliedAlpha(true)
+ , m_flipped(true)
+ , m_uvRect(0, 0, 1, 1)
+{
+}
+
+CCTextureLayerImpl::~CCTextureLayerImpl()
+{
+}
+
+void CCTextureLayerImpl::willDraw(CCResourceProvider* resourceProvider)
+{
+ if (!m_textureId)
+ return;
+ ASSERT(!m_externalTextureResource);
+ m_externalTextureResource = resourceProvider->createResourceFromExternalTexture(m_textureId);
+}
+
+void CCTextureLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ if (!m_externalTextureResource)
+ return;
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ IntRect quadRect(IntPoint(), contentBounds());
+ quadSink.append(CCTextureDrawQuad::create(sharedQuadState, quadRect, m_externalTextureResource, m_premultipliedAlpha, m_uvRect, m_flipped));
+}
+
+void CCTextureLayerImpl::didDraw(CCResourceProvider* resourceProvider)
+{
+ if (!m_externalTextureResource)
+ return;
+ // FIXME: the following assert will not be true when sending resources to a
+ // parent compositor. A synchronization scheme (double-buffering or
+ // pipelining of updates) for the client will need to exist to solve this.
+ ASSERT(!resourceProvider->inUseByConsumer(m_externalTextureResource));
+ resourceProvider->deleteResource(m_externalTextureResource);
+ m_externalTextureResource = 0;
+}
+
+void CCTextureLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << "texture layer texture id: " << m_textureId << " premultiplied: " << m_premultipliedAlpha << "\n";
+ CCLayerImpl::dumpLayerProperties(ts, indent);
+}
+
+void CCTextureLayerImpl::didLoseContext()
+{
+ m_textureId = 0;
+ m_externalTextureResource = 0;
+}
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCTextureLayerImpl.h b/cc/CCTextureLayerImpl.h
new file mode 100644
index 0000000..37cf83f
--- /dev/null
+++ b/cc/CCTextureLayerImpl.h
@@ -0,0 +1,48 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTextureLayerImpl_h
+#define CCTextureLayerImpl_h
+
+#include "CCLayerImpl.h"
+
+namespace WebCore {
+
+class CCTextureLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCTextureLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCTextureLayerImpl(id));
+ }
+ virtual ~CCTextureLayerImpl();
+
+ virtual void willDraw(CCResourceProvider*) OVERRIDE;
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+ virtual void didDraw(CCResourceProvider*) OVERRIDE;
+
+ virtual void didLoseContext() OVERRIDE;
+
+ virtual void dumpLayerProperties(TextStream&, int indent) const OVERRIDE;
+
+ unsigned textureId() const { return m_textureId; }
+ void setTextureId(unsigned id) { m_textureId = id; }
+ void setPremultipliedAlpha(bool premultipliedAlpha) { m_premultipliedAlpha = premultipliedAlpha; }
+ void setFlipped(bool flipped) { m_flipped = flipped; }
+ void setUVRect(const FloatRect& rect) { m_uvRect = rect; }
+
+private:
+ explicit CCTextureLayerImpl(int);
+
+ virtual const char* layerTypeAsString() const OVERRIDE { return "TextureLayer"; }
+
+ unsigned m_textureId;
+ CCResourceProvider::ResourceId m_externalTextureResource;
+ bool m_premultipliedAlpha;
+ bool m_flipped;
+ FloatRect m_uvRect;
+};
+
+}
+
+#endif // CCTextureLayerImpl_h
diff --git a/cc/CCTextureUpdateController.cpp b/cc/CCTextureUpdateController.cpp
new file mode 100644
index 0000000..25d41cc
--- /dev/null
+++ b/cc/CCTextureUpdateController.cpp
@@ -0,0 +1,168 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTextureUpdateController.h"
+
+#include "GraphicsContext3D.h"
+#include "TextureCopier.h"
+#include "TextureUploader.h"
+#include <wtf/CurrentTime.h>
+
+namespace {
+
+// Number of textures to update with each call to updateMoreTexturesIfEnoughTimeRemaining().
+static const size_t textureUpdatesPerTick = 12;
+
+// Measured in seconds.
+static const double textureUpdateTickRate = 0.004;
+
+// Flush interval when performing texture uploads.
+static const int textureUploadFlushPeriod = 4;
+
+} // anonymous namespace
+
+namespace WebCore {
+
+size_t CCTextureUpdateController::maxPartialTextureUpdates()
+{
+ return textureUpdatesPerTick;
+}
+
+void CCTextureUpdateController::updateTextures(CCResourceProvider* resourceProvider, TextureCopier* copier, TextureUploader* uploader, CCTextureUpdateQueue* queue, size_t count)
+{
+ if (queue->fullUploadSize() || queue->partialUploadSize()) {
+ if (uploader->isBusy())
+ return;
+
+ uploader->beginUploads();
+
+ size_t fullUploadCount = 0;
+ while (queue->fullUploadSize() && fullUploadCount < count) {
+ uploader->uploadTexture(resourceProvider, queue->takeFirstFullUpload());
+ fullUploadCount++;
+ if (!(fullUploadCount % textureUploadFlushPeriod))
+ resourceProvider->shallowFlushIfSupported();
+ }
+
+ // Make sure there are no dangling uploads without a flush.
+ if (fullUploadCount % textureUploadFlushPeriod)
+ resourceProvider->shallowFlushIfSupported();
+
+ bool moreUploads = queue->fullUploadSize();
+
+ ASSERT(queue->partialUploadSize() <= count);
+ // We need another update batch if the number of updates remaining
+ // in |count| is greater than the remaining partial entries.
+ if ((count - fullUploadCount) < queue->partialUploadSize())
+ moreUploads = true;
+
+ if (moreUploads) {
+ uploader->endUploads();
+ return;
+ }
+
+ size_t partialUploadCount = 0;
+ while (queue->partialUploadSize()) {
+ uploader->uploadTexture(resourceProvider, queue->takeFirstPartialUpload());
+ partialUploadCount++;
+ if (!(partialUploadCount % textureUploadFlushPeriod))
+ resourceProvider->shallowFlushIfSupported();
+ }
+
+ // Make sure there are no dangling partial uploads without a flush.
+ if (partialUploadCount % textureUploadFlushPeriod)
+ resourceProvider->shallowFlushIfSupported();
+
+ uploader->endUploads();
+ }
+
+ size_t copyCount = 0;
+ while (queue->copySize()) {
+ copier->copyTexture(queue->takeFirstCopy());
+ copyCount++;
+ }
+
+ // If we've performed any texture copies, we need to insert a flush here into the compositor context
+ // before letting the main thread proceed as it may make draw calls to the source texture of one of
+ // our copy operations.
+ if (copyCount)
+ copier->flush();
+}
+
+CCTextureUpdateController::CCTextureUpdateController(CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureCopier* copier, TextureUploader* uploader)
+ : m_timer(adoptPtr(new CCTimer(thread, this)))
+ , m_queue(queue)
+ , m_resourceProvider(resourceProvider)
+ , m_copier(copier)
+ , m_uploader(uploader)
+ , m_monotonicTimeLimit(0)
+ , m_firstUpdateAttempt(true)
+{
+}
+
+CCTextureUpdateController::~CCTextureUpdateController()
+{
+}
+
+bool CCTextureUpdateController::hasMoreUpdates() const
+{
+ return m_queue->hasMoreUpdates();
+}
+
+void CCTextureUpdateController::updateMoreTextures(double monotonicTimeLimit)
+{
+ m_monotonicTimeLimit = monotonicTimeLimit;
+
+ if (!m_queue->hasMoreUpdates())
+ return;
+
+ // Call updateMoreTexturesNow() directly unless it's the first update
+ // attempt. This ensures that we empty the update queue in a finite
+ // amount of time.
+ if (m_firstUpdateAttempt) {
+ updateMoreTexturesIfEnoughTimeRemaining();
+ m_firstUpdateAttempt = false;
+ } else
+ updateMoreTexturesNow();
+}
+
+void CCTextureUpdateController::onTimerFired()
+{
+ if (!m_queue->hasMoreUpdates())
+ return;
+
+ updateMoreTexturesIfEnoughTimeRemaining();
+}
+
+double CCTextureUpdateController::monotonicTimeNow() const
+{
+ return monotonicallyIncreasingTime();
+}
+
+double CCTextureUpdateController::updateMoreTexturesTime() const
+{
+ return textureUpdateTickRate;
+}
+
+size_t CCTextureUpdateController::updateMoreTexturesSize() const
+{
+ return textureUpdatesPerTick;
+}
+
+void CCTextureUpdateController::updateMoreTexturesIfEnoughTimeRemaining()
+{
+ bool hasTimeRemaining = monotonicTimeNow() < m_monotonicTimeLimit - updateMoreTexturesTime();
+ if (hasTimeRemaining)
+ updateMoreTexturesNow();
+}
+
+void CCTextureUpdateController::updateMoreTexturesNow()
+{
+ m_timer->startOneShot(updateMoreTexturesTime());
+ updateTextures(m_resourceProvider, m_copier, m_uploader, m_queue.get(), updateMoreTexturesSize());
+}
+
+}
diff --git a/cc/CCTextureUpdateController.h b/cc/CCTextureUpdateController.h
new file mode 100644
index 0000000..6fc8db3
--- /dev/null
+++ b/cc/CCTextureUpdateController.h
@@ -0,0 +1,59 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTextureUpdateController_h
+#define CCTextureUpdateController_h
+
+#include "CCTextureUpdateQueue.h"
+#include "CCTimer.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class TextureCopier;
+class TextureUploader;
+
+class CCTextureUpdateController : public CCTimerClient {
+ WTF_MAKE_NONCOPYABLE(CCTextureUpdateController);
+public:
+ static PassOwnPtr<CCTextureUpdateController> create(CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureCopier* copier, TextureUploader* uploader)
+ {
+ return adoptPtr(new CCTextureUpdateController(thread, queue, resourceProvider, copier, uploader));
+ }
+ static size_t maxPartialTextureUpdates();
+ static void updateTextures(CCResourceProvider*, TextureCopier*, TextureUploader*, CCTextureUpdateQueue*, size_t count);
+
+ virtual ~CCTextureUpdateController();
+
+ bool hasMoreUpdates() const;
+ void updateMoreTextures(double monotonicTimeLimit);
+
+ // CCTimerClient implementation.
+ virtual void onTimerFired() OVERRIDE;
+
+ // Virtual for testing.
+ virtual double monotonicTimeNow() const;
+ virtual double updateMoreTexturesTime() const;
+ virtual size_t updateMoreTexturesSize() const;
+
+protected:
+ CCTextureUpdateController(CCThread*, PassOwnPtr<CCTextureUpdateQueue>, CCResourceProvider*, TextureCopier*, TextureUploader*);
+
+ void updateMoreTexturesIfEnoughTimeRemaining();
+ void updateMoreTexturesNow();
+
+ OwnPtr<CCTimer> m_timer;
+ OwnPtr<CCTextureUpdateQueue> m_queue;
+ bool m_contentsTexturesPurged;
+ CCResourceProvider* m_resourceProvider;
+ TextureCopier* m_copier;
+ TextureUploader* m_uploader;
+ double m_monotonicTimeLimit;
+ bool m_firstUpdateAttempt;
+};
+
+}
+
+#endif // CCTextureUpdateController_h
diff --git a/cc/CCTextureUpdateControllerTest.cpp b/cc/CCTextureUpdateControllerTest.cpp
new file mode 100644
index 0000000..2ed0e59
--- /dev/null
+++ b/cc/CCTextureUpdateControllerTest.cpp
@@ -0,0 +1,668 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTextureUpdateController.h"
+
+#include "CCSchedulerTestCommon.h"
+#include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
+#include "CCTiledLayerTestCommon.h"
+#include "FakeWebCompositorOutputSurface.h"
+#include "FakeWebGraphicsContext3D.h"
+#include <gtest/gtest.h>
+#include <public/WebCompositor.h>
+#include <public/WebThread.h>
+#include <wtf/RefPtr.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WebKitTests;
+using testing::Test;
+
+
+namespace {
+
+const int kFlushPeriodFull = 4;
+const int kFlushPeriodPartial = kFlushPeriodFull;
+
+class CCTextureUpdateControllerTest;
+
+class WebGraphicsContext3DForUploadTest : public FakeWebGraphicsContext3D {
+public:
+ WebGraphicsContext3DForUploadTest(CCTextureUpdateControllerTest *test)
+ : m_test(test)
+ , m_supportShallowFlush(true)
+ { }
+
+ virtual void flush(void);
+ virtual void shallowFlushCHROMIUM(void);
+ virtual GrGLInterface* onCreateGrGLInterface() { return 0; }
+
+ virtual WebString getString(WGC3Denum name)
+ {
+ if (m_supportShallowFlush)
+ return WebString("GL_CHROMIUM_shallow_flush");
+ return WebString("");
+ }
+
+private:
+ CCTextureUpdateControllerTest* m_test;
+ bool m_supportShallowFlush;
+};
+
+
+class TextureUploaderForUploadTest : public FakeTextureUploader {
+public:
+ TextureUploaderForUploadTest(CCTextureUpdateControllerTest *test) : m_test(test) { }
+
+ virtual void beginUploads() OVERRIDE;
+ virtual void endUploads() OVERRIDE;
+ virtual void uploadTexture(WebCore::CCResourceProvider*, Parameters) OVERRIDE;
+
+private:
+ CCTextureUpdateControllerTest* m_test;
+};
+
+class TextureForUploadTest : public LayerTextureUpdater::Texture {
+public:
+ TextureForUploadTest() : LayerTextureUpdater::Texture(adoptPtr<CCPrioritizedTexture>(0)) { }
+ virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) { }
+};
+
+
+class CCTextureUpdateControllerTest : public Test {
+public:
+ CCTextureUpdateControllerTest()
+ : m_queue(adoptPtr(new CCTextureUpdateQueue))
+ , m_uploader(this)
+ , m_fullUploadCountExpected(0)
+ , m_partialCountExpected(0)
+ , m_totalUploadCountExpected(0)
+ , m_maxUploadCountPerUpdate(0)
+ , m_numBeginUploads(0)
+ , m_numEndUploads(0)
+ , m_numConsecutiveFlushes(0)
+ , m_numDanglingUploads(0)
+ , m_numTotalUploads(0)
+ , m_numTotalFlushes(0)
+ , m_numPreviousUploads(0)
+ , m_numPreviousFlushes(0)
+ { }
+
+public:
+ void onFlush()
+ {
+ // Check for back-to-back flushes.
+ EXPECT_EQ(0, m_numConsecutiveFlushes) << "Back-to-back flushes detected.";
+
+ // Check for premature flushes
+ if (m_numPreviousUploads != m_maxUploadCountPerUpdate) {
+ if (m_numTotalUploads < m_fullUploadCountExpected)
+ EXPECT_GE(m_numDanglingUploads, kFlushPeriodFull) << "Premature flush detected in full uploads.";
+ else if (m_numTotalUploads > m_fullUploadCountExpected && m_numTotalUploads < m_totalUploadCountExpected)
+ EXPECT_GE(m_numDanglingUploads, kFlushPeriodPartial) << "Premature flush detected in partial uploads.";
+ }
+
+ m_numDanglingUploads = 0;
+ m_numConsecutiveFlushes++;
+ m_numTotalFlushes++;
+ m_numPreviousFlushes++;
+ }
+
+ void onBeginUploads()
+ {
+ m_numPreviousFlushes = 0;
+ m_numPreviousUploads = 0;
+ m_numBeginUploads++;
+ }
+
+ void onUpload()
+ {
+ // Check for too many consecutive uploads
+ if (m_numTotalUploads < m_fullUploadCountExpected)
+ EXPECT_LT(m_numDanglingUploads, kFlushPeriodFull) << "Too many consecutive full uploads detected.";
+ else
+ EXPECT_LT(m_numDanglingUploads, kFlushPeriodPartial) << "Too many consecutive partial uploads detected.";
+
+ m_numConsecutiveFlushes = 0;
+ m_numDanglingUploads++;
+ m_numTotalUploads++;
+ m_numPreviousUploads++;
+ }
+
+ void onEndUploads()
+ {
+ EXPECT_EQ(0, m_numDanglingUploads) << "Last upload wasn't followed by a flush.";
+
+ // Note: The m_numTotalUploads != m_fullUploadCountExpected comparison
+ // allows for the quota not to be hit in the case where we are trasitioning
+ // from full uploads to partial uploads.
+ if (m_numTotalUploads != m_totalUploadCountExpected && m_numTotalUploads != m_fullUploadCountExpected) {
+ EXPECT_EQ(m_maxUploadCountPerUpdate, m_numPreviousUploads)
+ << "endUpload() was called when there are textures to upload, but the upload quota hasn't been filled.";
+ }
+
+ m_numEndUploads++;
+ }
+
+protected:
+ virtual void SetUp()
+ {
+ OwnPtr<WebThread> thread;
+ WebCompositor::initialize(thread.get());
+
+ m_context = FakeWebCompositorOutputSurface::create(adoptPtr(new WebGraphicsContext3DForUploadTest(this)));
+ DebugScopedSetImplThread implThread;
+ m_resourceProvider = CCResourceProvider::create(m_context.get());
+ }
+
+ virtual void TearDown()
+ {
+ WebCompositor::shutdown();
+ }
+
+ void appendFullUploadsToUpdateQueue(int count)
+ {
+ m_fullUploadCountExpected += count;
+ m_totalUploadCountExpected += count;
+
+ const IntRect rect(0, 0, 300, 150);
+ const TextureUploader::Parameters upload = { &m_texture, rect, IntSize() };
+ for (int i = 0; i < count; i++)
+ m_queue->appendFullUpload(upload);
+ }
+
+ void appendPartialUploadsToUpdateQueue(int count)
+ {
+ m_partialCountExpected += count;
+ m_totalUploadCountExpected += count;
+
+ const IntRect rect(0, 0, 100, 100);
+ const TextureUploader::Parameters upload = { &m_texture, rect, IntSize() };
+ for (int i = 0; i < count; i++)
+ m_queue->appendPartialUpload(upload);
+ }
+
+ void setMaxUploadCountPerUpdate(int count)
+ {
+ m_maxUploadCountPerUpdate = count;
+ }
+
+protected:
+ // Classes required to interact and test the CCTextureUpdateController
+ OwnPtr<CCGraphicsContext> m_context;
+ OwnPtr<CCResourceProvider> m_resourceProvider;
+ OwnPtr<CCTextureUpdateQueue> m_queue;
+ TextureForUploadTest m_texture;
+ FakeTextureCopier m_copier;
+ TextureUploaderForUploadTest m_uploader;
+
+ // Properties / expectations of this test
+ int m_fullUploadCountExpected;
+ int m_partialCountExpected;
+ int m_totalUploadCountExpected;
+ int m_maxUploadCountPerUpdate;
+
+ // Dynamic properties of this test
+ int m_numBeginUploads;
+ int m_numEndUploads;
+ int m_numConsecutiveFlushes;
+ int m_numDanglingUploads;
+ int m_numTotalUploads;
+ int m_numTotalFlushes;
+ int m_numPreviousUploads;
+ int m_numPreviousFlushes;
+};
+
+void WebGraphicsContext3DForUploadTest::flush(void)
+{
+ m_test->onFlush();
+}
+
+void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM(void)
+{
+ m_test->onFlush();
+}
+
+void TextureUploaderForUploadTest::beginUploads()
+{
+ m_test->onBeginUploads();
+}
+
+void TextureUploaderForUploadTest::endUploads()
+{
+ m_test->onEndUploads();
+}
+
+void TextureUploaderForUploadTest::uploadTexture(WebCore::CCResourceProvider*, Parameters)
+{
+ m_test->onUpload();
+}
+
+
+// ZERO UPLOADS TESTS
+TEST_F(CCTextureUpdateControllerTest, ZeroUploads)
+{
+ appendFullUploadsToUpdateQueue(0);
+ appendPartialUploadsToUpdateQueue(0);
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(0, m_numBeginUploads);
+ EXPECT_EQ(0, m_numEndUploads);
+ EXPECT_EQ(0, m_numPreviousFlushes);
+ EXPECT_EQ(0, m_numPreviousUploads);
+}
+
+
+// ONE UPLOAD TESTS
+TEST_F(CCTextureUpdateControllerTest, OneFullUpload)
+{
+ appendFullUploadsToUpdateQueue(1);
+ appendPartialUploadsToUpdateQueue(0);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(1, m_numPreviousFlushes);
+ EXPECT_EQ(1, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, OnePartialUpload)
+{
+ appendFullUploadsToUpdateQueue(0);
+ appendPartialUploadsToUpdateQueue(1);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(1, m_numPreviousFlushes);
+ EXPECT_EQ(1, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, OneFullOnePartialUpload)
+{
+ appendFullUploadsToUpdateQueue(1);
+ appendPartialUploadsToUpdateQueue(1);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ // We expect the full uploads to be followed by a flush
+ // before the partial uploads begin.
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(2, m_numPreviousFlushes);
+ EXPECT_EQ(2, m_numPreviousUploads);
+}
+
+
+// NO REMAINDER TESTS
+// This class of tests upload a number of textures that is a multiple of the flush period.
+const int fullUploadFlushMultipler = 7;
+const int fullNoRemainderCount = fullUploadFlushMultipler * kFlushPeriodFull;
+
+const int partialUploadFlushMultipler = 11;
+const int partialNoRemainderCount = partialUploadFlushMultipler * kFlushPeriodPartial;
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullUploadsNoRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullNoRemainderCount);
+ appendPartialUploadsToUpdateQueue(0);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(fullUploadFlushMultipler, m_numPreviousFlushes);
+ EXPECT_EQ(fullNoRemainderCount, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, ManyPartialUploadsNoRemainder)
+{
+ appendFullUploadsToUpdateQueue(0);
+ appendPartialUploadsToUpdateQueue(partialNoRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(partialUploadFlushMultipler, m_numPreviousFlushes);
+ EXPECT_EQ(partialNoRemainderCount, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullManyPartialUploadsNoRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullNoRemainderCount);
+ appendPartialUploadsToUpdateQueue(partialNoRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(fullUploadFlushMultipler + partialUploadFlushMultipler, m_numPreviousFlushes);
+ EXPECT_EQ(fullNoRemainderCount + partialNoRemainderCount, m_numPreviousUploads);
+}
+
+
+// MIN/MAX REMAINDER TESTS
+// This class of tests mix and match uploading 1 more and 1 less texture
+// than a multiple of the flush period.
+
+const int fullMinRemainderCount = fullNoRemainderCount + 1;
+const int fullMaxRemainderCount = fullNoRemainderCount - 1;
+const int partialMinRemainderCount = partialNoRemainderCount + 1;
+const int partialMaxRemainderCount = partialNoRemainderCount - 1;
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullAndPartialMinRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullMinRemainderCount);
+ appendPartialUploadsToUpdateQueue(partialMinRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(fullUploadFlushMultipler + partialUploadFlushMultipler + 2, m_numPreviousFlushes);
+ EXPECT_EQ(fullMinRemainderCount + partialMinRemainderCount, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullAndPartialUploadsMaxRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullMaxRemainderCount);
+ appendPartialUploadsToUpdateQueue(partialMaxRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(fullUploadFlushMultipler + partialUploadFlushMultipler, m_numPreviousFlushes);
+ EXPECT_EQ(fullMaxRemainderCount + partialMaxRemainderCount, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullMinRemainderManyPartialMaxRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullMinRemainderCount);
+ appendPartialUploadsToUpdateQueue(partialMaxRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ((fullUploadFlushMultipler+1) + partialUploadFlushMultipler, m_numPreviousFlushes);
+ EXPECT_EQ(fullMinRemainderCount + partialMaxRemainderCount, m_numPreviousUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, ManyFullMaxRemainderManyPartialMinRemainder)
+{
+ appendFullUploadsToUpdateQueue(fullMaxRemainderCount);
+ appendPartialUploadsToUpdateQueue(partialMinRemainderCount);
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), m_totalUploadCountExpected);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(fullUploadFlushMultipler + (partialUploadFlushMultipler+1), m_numPreviousFlushes);
+ EXPECT_EQ(fullMaxRemainderCount + partialMinRemainderCount, m_numPreviousUploads);
+}
+
+
+// MULTIPLE UPDATE TESTS
+// These tests attempt to upload too many textures at once, requiring
+// multiple calls to update().
+
+int expectedFlushes(int uploads, int flushPeriod)
+{
+ return (uploads + flushPeriod - 1) / flushPeriod;
+}
+
+TEST_F(CCTextureUpdateControllerTest, TripleUpdateFinalUpdateFullAndPartial)
+{
+ const int kMaxUploadsPerUpdate = 40;
+ const int kFullUploads = 100;
+ const int kPartialUploads = 20;
+
+ int expectedPreviousFlushes = 0;
+ int expectedPreviousUploads = 0;
+
+ setMaxUploadCountPerUpdate(kMaxUploadsPerUpdate);
+ appendFullUploadsToUpdateQueue(kFullUploads);
+ appendPartialUploadsToUpdateQueue(kPartialUploads);
+
+ // First update (40 full)
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+
+ expectedPreviousFlushes = expectedFlushes(kMaxUploadsPerUpdate, kFlushPeriodFull);
+ EXPECT_EQ(expectedPreviousFlushes, m_numPreviousFlushes);
+
+ expectedPreviousUploads = kMaxUploadsPerUpdate;
+ EXPECT_EQ(expectedPreviousUploads, m_numPreviousUploads);
+
+ // Second update (40 full)
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(2, m_numBeginUploads);
+ EXPECT_EQ(2, m_numEndUploads);
+
+ expectedPreviousFlushes = expectedFlushes(kMaxUploadsPerUpdate, kFlushPeriodFull);
+ EXPECT_EQ(expectedPreviousFlushes, m_numPreviousFlushes);
+
+ expectedPreviousUploads = kMaxUploadsPerUpdate;
+ EXPECT_EQ(expectedPreviousUploads, m_numPreviousUploads);
+
+ // Third update (20 full, 20 partial)
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(3, m_numBeginUploads);
+ EXPECT_EQ(3, m_numEndUploads);
+
+ expectedPreviousFlushes = expectedFlushes(kFullUploads-kMaxUploadsPerUpdate*2, kFlushPeriodFull) +
+ expectedFlushes(kPartialUploads, kFlushPeriodPartial);
+ EXPECT_EQ(expectedPreviousFlushes, m_numPreviousFlushes);
+
+ expectedPreviousUploads = (kFullUploads-kMaxUploadsPerUpdate*2)+kPartialUploads;
+ EXPECT_EQ(expectedPreviousUploads, m_numPreviousUploads);
+
+ // Final sanity checks
+ EXPECT_EQ(kFullUploads + kPartialUploads, m_numTotalUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, TripleUpdateFinalUpdateAllPartial)
+{
+ const int kMaxUploadsPerUpdate = 40;
+ const int kFullUploads = 70;
+ const int kPartialUploads = 30;
+
+ int expectedPreviousFlushes = 0;
+ int expectedPreviousUploads = 0;
+
+ setMaxUploadCountPerUpdate(kMaxUploadsPerUpdate);
+ appendFullUploadsToUpdateQueue(kFullUploads);
+ appendPartialUploadsToUpdateQueue(kPartialUploads);
+
+ // First update (40 full)
+ DebugScopedSetImplThread implThread;
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+
+ expectedPreviousFlushes = expectedFlushes(kMaxUploadsPerUpdate, kFlushPeriodFull);
+ EXPECT_EQ(expectedPreviousFlushes, m_numPreviousFlushes);
+
+ expectedPreviousUploads = kMaxUploadsPerUpdate;
+ EXPECT_EQ(expectedPreviousUploads, m_numPreviousUploads);
+
+ // Second update (30 full, optionally 10 partial)
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(2, m_numBeginUploads);
+ EXPECT_EQ(2, m_numEndUploads);
+ EXPECT_LE(m_numPreviousUploads, kMaxUploadsPerUpdate);
+ // Be lenient on the exact number of flushes here, as the number of flushes
+ // will depend on whether some partial uploads were performed.
+ // onFlush(), onUpload(), and onEndUpload() will do basic flush checks for us anyway.
+
+ // Third update (30 partial OR 20 partial if 10 partial uploaded in second update)
+ CCTextureUpdateController::updateTextures(m_resourceProvider.get(), &m_copier, &m_uploader, m_queue.get(), kMaxUploadsPerUpdate);
+
+ EXPECT_EQ(3, m_numBeginUploads);
+ EXPECT_EQ(3, m_numEndUploads);
+ EXPECT_LE(m_numPreviousUploads, kMaxUploadsPerUpdate);
+ // Be lenient on the exact number of flushes here as well.
+
+ // Final sanity checks
+ EXPECT_EQ(kFullUploads + kPartialUploads, m_numTotalUploads);
+}
+
+class FakeCCTextureUpdateController : public WebCore::CCTextureUpdateController {
+public:
+ static PassOwnPtr<FakeCCTextureUpdateController> create(WebCore::CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureCopier* copier, TextureUploader* uploader)
+ {
+ return adoptPtr(new FakeCCTextureUpdateController(thread, queue, resourceProvider, copier, uploader));
+ }
+
+ void setMonotonicTimeNow(double time) { m_monotonicTimeNow = time; }
+ virtual double monotonicTimeNow() const OVERRIDE { return m_monotonicTimeNow; }
+ void setUpdateMoreTexturesTime(double time) { m_updateMoreTexturesTime = time; }
+ virtual double updateMoreTexturesTime() const OVERRIDE { return m_updateMoreTexturesTime; }
+ void setUpdateMoreTexturesSize(size_t size) { m_updateMoreTexturesSize = size; }
+ virtual size_t updateMoreTexturesSize() const OVERRIDE { return m_updateMoreTexturesSize; }
+
+protected:
+ FakeCCTextureUpdateController(WebCore::CCThread* thread, PassOwnPtr<CCTextureUpdateQueue> queue, CCResourceProvider* resourceProvider, TextureCopier* copier, TextureUploader* uploader)
+ : WebCore::CCTextureUpdateController(thread, queue, resourceProvider, copier, uploader)
+ , m_monotonicTimeNow(0)
+ , m_updateMoreTexturesTime(0)
+ , m_updateMoreTexturesSize(0) { }
+
+ double m_monotonicTimeNow;
+ double m_updateMoreTexturesTime;
+ size_t m_updateMoreTexturesSize;
+};
+
+TEST_F(CCTextureUpdateControllerTest, UpdateMoreTextures)
+{
+ FakeCCThread thread;
+
+ setMaxUploadCountPerUpdate(1);
+ appendFullUploadsToUpdateQueue(3);
+ appendPartialUploadsToUpdateQueue(0);
+
+ DebugScopedSetImplThread implThread;
+ OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&thread, m_queue.release(), m_resourceProvider.get(), &m_copier, &m_uploader));
+
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.1);
+ controller->setUpdateMoreTexturesSize(1);
+ // Not enough time for any updates.
+ controller->updateMoreTextures(0.09);
+ EXPECT_FALSE(thread.hasPendingTask());
+ EXPECT_EQ(0, m_numBeginUploads);
+ EXPECT_EQ(0, m_numEndUploads);
+
+ thread.reset();
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.1);
+ controller->setUpdateMoreTexturesSize(1);
+ // Only enough time for 1 update.
+ controller->updateMoreTextures(0.12);
+ EXPECT_TRUE(thread.hasPendingTask());
+ controller->setMonotonicTimeNow(thread.pendingDelayMs() / 1000.0);
+ thread.runPendingTask();
+ EXPECT_EQ(1, m_numBeginUploads);
+ EXPECT_EQ(1, m_numEndUploads);
+ EXPECT_EQ(1, m_numTotalUploads);
+
+ thread.reset();
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.1);
+ controller->setUpdateMoreTexturesSize(1);
+ // Enough time for 2 updates.
+ controller->updateMoreTextures(0.22);
+ EXPECT_TRUE(thread.hasPendingTask());
+ controller->setMonotonicTimeNow(controller->monotonicTimeNow() + thread.pendingDelayMs() / 1000.0);
+ thread.runPendingTask();
+ EXPECT_EQ(3, m_numBeginUploads);
+ EXPECT_EQ(3, m_numEndUploads);
+ EXPECT_EQ(3, m_numTotalUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, NoMoreUpdates)
+{
+ FakeCCThread thread;
+
+ setMaxUploadCountPerUpdate(1);
+ appendFullUploadsToUpdateQueue(2);
+ appendPartialUploadsToUpdateQueue(0);
+
+ DebugScopedSetImplThread implThread;
+ OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&thread, m_queue.release(), m_resourceProvider.get(), &m_copier, &m_uploader));
+
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.1);
+ controller->setUpdateMoreTexturesSize(1);
+ // Enough time for 3 updates but only 2 necessary.
+ controller->updateMoreTextures(0.31);
+ EXPECT_TRUE(thread.hasPendingTask());
+ controller->setMonotonicTimeNow(controller->monotonicTimeNow() + thread.pendingDelayMs() / 1000.0);
+ thread.runPendingTask();
+ EXPECT_TRUE(thread.hasPendingTask());
+ controller->setMonotonicTimeNow(controller->monotonicTimeNow() + thread.pendingDelayMs() / 1000.0);
+ thread.runPendingTask();
+ EXPECT_EQ(2, m_numBeginUploads);
+ EXPECT_EQ(2, m_numEndUploads);
+ EXPECT_EQ(2, m_numTotalUploads);
+
+ thread.reset();
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.1);
+ controller->setUpdateMoreTexturesSize(1);
+ // Enough time for updates but no more updates left.
+ controller->updateMoreTextures(0.31);
+ EXPECT_FALSE(thread.hasPendingTask());
+ EXPECT_EQ(2, m_numBeginUploads);
+ EXPECT_EQ(2, m_numEndUploads);
+ EXPECT_EQ(2, m_numTotalUploads);
+}
+
+TEST_F(CCTextureUpdateControllerTest, UpdatesCompleteInFiniteTime)
+{
+ FakeCCThread thread;
+
+ setMaxUploadCountPerUpdate(1);
+ appendFullUploadsToUpdateQueue(2);
+ appendPartialUploadsToUpdateQueue(0);
+
+ DebugScopedSetImplThread implThread;
+ OwnPtr<FakeCCTextureUpdateController> controller(FakeCCTextureUpdateController::create(&thread, m_queue.release(), m_resourceProvider.get(), &m_copier, &m_uploader));
+
+ controller->setMonotonicTimeNow(0);
+ controller->setUpdateMoreTexturesTime(0.5);
+ controller->setUpdateMoreTexturesSize(1);
+
+ for (int i = 0; i < 100; i++) {
+ if (!controller->hasMoreUpdates())
+ break;
+
+ // Not enough time for any updates.
+ controller->updateMoreTextures(0.4);
+
+ if (thread.hasPendingTask()) {
+ controller->setMonotonicTimeNow(controller->monotonicTimeNow() + thread.pendingDelayMs() / 1000.0);
+ thread.runPendingTask();
+ }
+ }
+
+ EXPECT_EQ(2, m_numBeginUploads);
+ EXPECT_EQ(2, m_numEndUploads);
+ EXPECT_EQ(2, m_numTotalUploads);
+}
+
+} // namespace
diff --git a/cc/CCTextureUpdateQueue.cpp b/cc/CCTextureUpdateQueue.cpp
new file mode 100644
index 0000000..02f6c13
--- /dev/null
+++ b/cc/CCTextureUpdateQueue.cpp
@@ -0,0 +1,64 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCTextureUpdateQueue.h"
+
+namespace WebCore {
+
+CCTextureUpdateQueue::CCTextureUpdateQueue()
+{
+}
+
+CCTextureUpdateQueue::~CCTextureUpdateQueue()
+{
+}
+
+void CCTextureUpdateQueue::appendFullUpload(TextureUploader::Parameters upload)
+{
+ m_fullEntries.append(upload);
+}
+
+void CCTextureUpdateQueue::appendPartialUpload(TextureUploader::Parameters upload)
+{
+ m_partialEntries.append(upload);
+}
+
+void CCTextureUpdateQueue::appendCopy(TextureCopier::Parameters copy)
+{
+ m_copyEntries.append(copy);
+}
+
+void CCTextureUpdateQueue::clearUploads()
+{
+ m_fullEntries.clear();
+ m_partialEntries.clear();
+}
+
+TextureUploader::Parameters CCTextureUpdateQueue::takeFirstFullUpload()
+{
+ return m_fullEntries.takeFirst();
+}
+
+TextureUploader::Parameters CCTextureUpdateQueue::takeFirstPartialUpload()
+{
+ return m_partialEntries.takeFirst();
+}
+
+TextureCopier::Parameters CCTextureUpdateQueue::takeFirstCopy()
+{
+ return m_copyEntries.takeFirst();
+}
+
+bool CCTextureUpdateQueue::hasMoreUpdates() const
+{
+ return m_fullEntries.size() || m_partialEntries.size() || m_copyEntries.size();
+}
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCTextureUpdateQueue.h b/cc/CCTextureUpdateQueue.h
new file mode 100644
index 0000000..afe33b3
--- /dev/null
+++ b/cc/CCTextureUpdateQueue.h
@@ -0,0 +1,45 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTextureUpdateQueue_h
+#define CCTextureUpdateQueue_h
+
+#include "TextureCopier.h"
+#include "TextureUploader.h"
+#include <wtf/Deque.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class CCTextureUpdateQueue {
+ WTF_MAKE_NONCOPYABLE(CCTextureUpdateQueue);
+public:
+ CCTextureUpdateQueue();
+ virtual ~CCTextureUpdateQueue();
+
+ void appendFullUpload(TextureUploader::Parameters);
+ void appendPartialUpload(TextureUploader::Parameters);
+ void appendCopy(TextureCopier::Parameters);
+
+ void clearUploads();
+
+ TextureUploader::Parameters takeFirstFullUpload();
+ TextureUploader::Parameters takeFirstPartialUpload();
+ TextureCopier::Parameters takeFirstCopy();
+
+ size_t fullUploadSize() const { return m_fullEntries.size(); }
+ size_t partialUploadSize() const { return m_partialEntries.size(); }
+ size_t copySize() const { return m_copyEntries.size(); }
+
+ bool hasMoreUpdates() const;
+
+private:
+ Deque<TextureUploader::Parameters> m_fullEntries;
+ Deque<TextureUploader::Parameters> m_partialEntries;
+ Deque<TextureCopier::Parameters> m_copyEntries;
+};
+
+}
+
+#endif // CCTextureUpdateQueue_h
diff --git a/cc/CCThread.h b/cc/CCThread.h
new file mode 100644
index 0000000..d375932
--- /dev/null
+++ b/cc/CCThread.h
@@ -0,0 +1,41 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCThread_h
+#define CCThread_h
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+// CCThread provides basic infrastructure for messaging with the compositor in a
+// platform-neutral way.
+class CCThread {
+public:
+ virtual ~CCThread() { }
+
+ class Task {
+ WTF_MAKE_NONCOPYABLE(Task);
+ public:
+ virtual ~Task() { }
+ virtual void performTask() = 0;
+ void* instance() const { return m_instance; }
+ protected:
+ Task(void* instance) : m_instance(instance) { }
+ void* m_instance;
+ };
+
+ // Executes the task on context's thread asynchronously.
+ virtual void postTask(PassOwnPtr<Task>) = 0;
+
+ // Executes the task after the specified delay.
+ virtual void postDelayedTask(PassOwnPtr<Task>, long long delayMs) = 0;
+
+ virtual WTF::ThreadIdentifier threadID() const = 0;
+};
+
+}
+
+#endif
diff --git a/cc/CCThreadProxy.cpp b/cc/CCThreadProxy.cpp
new file mode 100644
index 0000000..8d0a8b0
--- /dev/null
+++ b/cc/CCThreadProxy.cpp
@@ -0,0 +1,892 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCThreadProxy.h"
+
+#include "CCDelayBasedTimeSource.h"
+#include "CCDrawQuad.h"
+#include "CCFrameRateController.h"
+#include "CCGraphicsContext.h"
+#include "CCInputHandler.h"
+#include "CCLayerTreeHost.h"
+#include "CCScheduler.h"
+#include "CCScopedThreadProxy.h"
+#include "CCTextureUpdateController.h"
+#include "CCThreadTask.h"
+#include "TraceEvent.h"
+#include <public/WebSharedGraphicsContext3D.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/MainThread.h>
+
+using namespace WTF;
+
+using WebKit::WebSharedGraphicsContext3D;
+namespace {
+
+// Measured in seconds.
+static const double contextRecreationTickRate = 0.03;
+
+} // anonymous namespace
+
+namespace WebCore {
+
+namespace {
+
+// Type of texture uploader to use for texture updates.
+static TextureUploaderOption textureUploader = ThrottledUploader;
+
+} // anonymous namespace
+
+PassOwnPtr<CCProxy> CCThreadProxy::create(CCLayerTreeHost* layerTreeHost)
+{
+ return adoptPtr(new CCThreadProxy(layerTreeHost));
+}
+
+CCThreadProxy::CCThreadProxy(CCLayerTreeHost* layerTreeHost)
+ : m_animateRequested(false)
+ , m_commitRequested(false)
+ , m_forcedCommitRequested(false)
+ , m_layerTreeHost(layerTreeHost)
+ , m_compositorIdentifier(-1)
+ , m_rendererInitialized(false)
+ , m_started(false)
+ , m_texturesAcquired(true)
+ , m_inCompositeAndReadback(false)
+ , m_mainThreadProxy(CCScopedThreadProxy::create(CCProxy::mainThread()))
+ , m_beginFrameCompletionEventOnImplThread(0)
+ , m_readbackRequestOnImplThread(0)
+ , m_commitCompletionEventOnImplThread(0)
+ , m_textureAcquisitionCompletionEventOnImplThread(0)
+ , m_resetContentsTexturesPurgedAfterCommitOnImplThread(false)
+ , m_nextFrameIsNewlyCommittedFrameOnImplThread(false)
+ , m_renderVSyncEnabled(layerTreeHost->settings().renderVSyncEnabled)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::CCThreadProxy");
+ ASSERT(isMainThread());
+}
+
+CCThreadProxy::~CCThreadProxy()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::~CCThreadProxy");
+ ASSERT(isMainThread());
+ ASSERT(!m_started);
+}
+
+bool CCThreadProxy::compositeAndReadback(void *pixels, const IntRect& rect)
+{
+ TRACE_EVENT0("cc", "CCThreadPRoxy::compositeAndReadback");
+ ASSERT(isMainThread());
+ ASSERT(m_layerTreeHost);
+
+ if (!m_layerTreeHost->initializeRendererIfNeeded()) {
+ TRACE_EVENT0("cc", "compositeAndReadback_EarlyOut_LR_Uninitialized");
+ return false;
+ }
+
+
+ // Perform a synchronous commit.
+ CCCompletionEvent beginFrameCompletion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::forceBeginFrameOnImplThread, &beginFrameCompletion));
+ beginFrameCompletion.wait();
+ m_inCompositeAndReadback = true;
+ beginFrame();
+ m_inCompositeAndReadback = false;
+
+ // Perform a synchronous readback.
+ ReadbackRequest request;
+ request.rect = rect;
+ request.pixels = pixels;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::requestReadbackOnImplThread, &request));
+ request.completion.wait();
+ return request.success;
+}
+
+void CCThreadProxy::requestReadbackOnImplThread(ReadbackRequest* request)
+{
+ ASSERT(CCProxy::isImplThread());
+ ASSERT(!m_readbackRequestOnImplThread);
+ if (!m_layerTreeHostImpl) {
+ request->success = false;
+ request->completion.signal();
+ return;
+ }
+
+ m_readbackRequestOnImplThread = request;
+ m_schedulerOnImplThread->setNeedsRedraw();
+ m_schedulerOnImplThread->setNeedsForcedRedraw();
+}
+
+void CCThreadProxy::startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double duration)
+{
+ ASSERT(CCProxy::isMainThread());
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::requestStartPageScaleAnimationOnImplThread, targetPosition, useAnchor, scale, duration));
+}
+
+void CCThreadProxy::requestStartPageScaleAnimationOnImplThread(IntSize targetPosition, bool useAnchor, float scale, double duration)
+{
+ ASSERT(CCProxy::isImplThread());
+ if (m_layerTreeHostImpl)
+ m_layerTreeHostImpl->startPageScaleAnimation(targetPosition, useAnchor, scale, monotonicallyIncreasingTime(), duration);
+}
+
+void CCThreadProxy::finishAllRendering()
+{
+ ASSERT(CCProxy::isMainThread());
+
+ // Make sure all GL drawing is finished on the impl thread.
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::finishAllRenderingOnImplThread, &completion));
+ completion.wait();
+}
+
+bool CCThreadProxy::isStarted() const
+{
+ ASSERT(CCProxy::isMainThread());
+ return m_started;
+}
+
+bool CCThreadProxy::initializeContext()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::initializeContext");
+ OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext();
+ if (!context)
+ return false;
+
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeContextOnImplThread,
+ context.leakPtr()));
+ return true;
+}
+
+void CCThreadProxy::setSurfaceReady()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::setSurfaceReady");
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setSurfaceReadyOnImplThread));
+}
+
+void CCThreadProxy::setSurfaceReadyOnImplThread()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::setSurfaceReadyOnImplThread");
+ m_schedulerOnImplThread->setCanBeginFrame(true);
+}
+
+void CCThreadProxy::setVisible(bool visible)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::setVisible");
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setVisibleOnImplThread, &completion, visible));
+ completion.wait();
+}
+
+void CCThreadProxy::setVisibleOnImplThread(CCCompletionEvent* completion, bool visible)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::setVisibleOnImplThread");
+ m_layerTreeHostImpl->setVisible(visible);
+ m_schedulerOnImplThread->setVisible(visible);
+ completion->signal();
+}
+
+bool CCThreadProxy::initializeRenderer()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::initializeRenderer");
+ // Make a blocking call to initializeRendererOnImplThread. The results of that call
+ // are pushed into the initializeSucceeded and capabilities local variables.
+ CCCompletionEvent completion;
+ bool initializeSucceeded = false;
+ RendererCapabilities capabilities;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeRendererOnImplThread,
+ &completion,
+ &initializeSucceeded,
+ &capabilities));
+ completion.wait();
+
+ if (initializeSucceeded) {
+ m_rendererInitialized = true;
+ m_RendererCapabilitiesMainThreadCopy = capabilities;
+ }
+ return initializeSucceeded;
+}
+
+bool CCThreadProxy::recreateContext()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::recreateContext");
+ ASSERT(isMainThread());
+
+ // Try to create the context.
+ OwnPtr<CCGraphicsContext> context = m_layerTreeHost->createContext();
+ if (!context)
+ return false;
+ if (m_layerTreeHost->needsSharedContext())
+ if (!WebSharedGraphicsContext3D::createCompositorThreadContext())
+ return false;
+
+ // Make a blocking call to recreateContextOnImplThread. The results of that
+ // call are pushed into the recreateSucceeded and capabilities local
+ // variables.
+ CCCompletionEvent completion;
+ bool recreateSucceeded = false;
+ RendererCapabilities capabilities;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::recreateContextOnImplThread,
+ &completion,
+ context.leakPtr(),
+ &recreateSucceeded,
+ &capabilities));
+ completion.wait();
+
+ if (recreateSucceeded)
+ m_RendererCapabilitiesMainThreadCopy = capabilities;
+ return recreateSucceeded;
+}
+
+int CCThreadProxy::compositorIdentifier() const
+{
+ ASSERT(isMainThread());
+ return m_compositorIdentifier;
+}
+
+void CCThreadProxy::implSideRenderingStats(CCRenderingStats& stats)
+{
+ ASSERT(isMainThread());
+
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::implSideRenderingStatsOnImplThread,
+ &completion,
+ &stats));
+ completion.wait();
+}
+
+const RendererCapabilities& CCThreadProxy::rendererCapabilities() const
+{
+ ASSERT(m_rendererInitialized);
+ return m_RendererCapabilitiesMainThreadCopy;
+}
+
+void CCThreadProxy::loseContext()
+{
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::didLoseContextOnImplThread));
+}
+
+void CCThreadProxy::setNeedsAnimate()
+{
+ ASSERT(isMainThread());
+ if (m_animateRequested)
+ return;
+
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsAnimate");
+ m_animateRequested = true;
+ setNeedsCommit();
+}
+
+void CCThreadProxy::setNeedsCommit()
+{
+ ASSERT(isMainThread());
+ if (m_commitRequested)
+ return;
+
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsCommit");
+ m_commitRequested = true;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setNeedsCommitOnImplThread));
+}
+
+void CCThreadProxy::didLoseContextOnImplThread()
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::didLoseContextOnImplThread");
+ m_schedulerOnImplThread->didLoseContext();
+}
+
+void CCThreadProxy::onSwapBuffersCompleteOnImplThread()
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::onSwapBuffersCompleteOnImplThread");
+ m_schedulerOnImplThread->didSwapBuffersComplete();
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::didCompleteSwapBuffers));
+}
+
+void CCThreadProxy::onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds)
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::onVSyncParametersChanged");
+ m_schedulerOnImplThread->setTimebaseAndInterval(monotonicTimebase, intervalInSeconds);
+}
+
+void CCThreadProxy::setNeedsCommitOnImplThread()
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsCommitOnImplThread");
+ m_schedulerOnImplThread->setNeedsCommit();
+}
+
+void CCThreadProxy::setNeedsForcedCommitOnImplThread()
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsForcedCommitOnImplThread");
+ m_schedulerOnImplThread->setNeedsCommit();
+ m_schedulerOnImplThread->setNeedsForcedCommit();
+}
+
+void CCThreadProxy::postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector> events, double wallClockTime)
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::postAnimationEventsToMainThreadOnImplThread");
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::setAnimationEvents, events, wallClockTime));
+}
+
+void CCThreadProxy::setNeedsRedraw()
+{
+ ASSERT(isMainThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsRedraw");
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setFullRootLayerDamageOnImplThread));
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::setNeedsRedrawOnImplThread));
+}
+
+bool CCThreadProxy::commitRequested() const
+{
+ ASSERT(isMainThread());
+ return m_commitRequested;
+}
+
+void CCThreadProxy::setNeedsRedrawOnImplThread()
+{
+ ASSERT(isImplThread());
+ TRACE_EVENT0("cc", "CCThreadProxy::setNeedsRedrawOnImplThread");
+ m_schedulerOnImplThread->setNeedsRedraw();
+}
+
+void CCThreadProxy::start()
+{
+ ASSERT(isMainThread());
+ ASSERT(CCProxy::implThread());
+ // Create LayerTreeHostImpl.
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::initializeImplOnImplThread, &completion));
+ completion.wait();
+
+ m_started = true;
+}
+
+void CCThreadProxy::stop()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::stop");
+ ASSERT(isMainThread());
+ ASSERT(m_started);
+
+ // Synchronously deletes the impl.
+ {
+ DebugScopedSetMainThreadBlocked mainThreadBlocked;
+
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::layerTreeHostClosedOnImplThread, &completion));
+ completion.wait();
+ }
+
+ m_mainThreadProxy->shutdown(); // Stop running tasks posted to us.
+
+ ASSERT(!m_layerTreeHostImpl); // verify that the impl deleted.
+ m_layerTreeHost = 0;
+ m_started = false;
+}
+
+void CCThreadProxy::forceSerializeOnSwapBuffers()
+{
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::forceSerializeOnSwapBuffersOnImplThread, &completion));
+ completion.wait();
+}
+
+void CCThreadProxy::forceSerializeOnSwapBuffersOnImplThread(CCCompletionEvent* completion)
+{
+ if (m_rendererInitialized)
+ m_layerTreeHostImpl->renderer()->doNoOp();
+ completion->signal();
+}
+
+
+void CCThreadProxy::finishAllRenderingOnImplThread(CCCompletionEvent* completion)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::finishAllRenderingOnImplThread");
+ ASSERT(isImplThread());
+ m_layerTreeHostImpl->finishAllRendering();
+ completion->signal();
+}
+
+void CCThreadProxy::forceBeginFrameOnImplThread(CCCompletionEvent* completion)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::forceBeginFrameOnImplThread");
+ ASSERT(!m_beginFrameCompletionEventOnImplThread);
+
+ if (m_schedulerOnImplThread->commitPending()) {
+ completion->signal();
+ return;
+ }
+
+ m_beginFrameCompletionEventOnImplThread = completion;
+ setNeedsForcedCommitOnImplThread();
+}
+
+void CCThreadProxy::scheduledActionBeginFrame()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::scheduledActionBeginFrame");
+ ASSERT(!m_pendingBeginFrameRequest);
+ m_pendingBeginFrameRequest = adoptPtr(new BeginFrameAndCommitState());
+ m_pendingBeginFrameRequest->monotonicFrameBeginTime = monotonicallyIncreasingTime();
+ m_pendingBeginFrameRequest->scrollInfo = m_layerTreeHostImpl->processScrollDeltas();
+ m_pendingBeginFrameRequest->contentsTexturesWereDeleted = m_layerTreeHostImpl->contentsTexturesPurged();
+ m_pendingBeginFrameRequest->memoryAllocationLimitBytes = m_layerTreeHostImpl->memoryAllocationLimitBytes();
+
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrame));
+
+ if (m_beginFrameCompletionEventOnImplThread) {
+ m_beginFrameCompletionEventOnImplThread->signal();
+ m_beginFrameCompletionEventOnImplThread = 0;
+ }
+}
+
+void CCThreadProxy::beginFrame()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::beginFrame");
+ ASSERT(isMainThread());
+ if (!m_layerTreeHost)
+ return;
+
+ if (!m_pendingBeginFrameRequest) {
+ TRACE_EVENT0("cc", "EarlyOut_StaleBeginFrameMessage");
+ return;
+ }
+
+ if (m_layerTreeHost->needsSharedContext() && !WebSharedGraphicsContext3D::haveCompositorThreadContext())
+ WebSharedGraphicsContext3D::createCompositorThreadContext();
+
+ OwnPtr<BeginFrameAndCommitState> request(m_pendingBeginFrameRequest.release());
+
+ // Do not notify the impl thread of commit requests that occur during
+ // the apply/animate/layout part of the beginFrameAndCommit process since
+ // those commit requests will get painted immediately. Once we have done
+ // the paint, m_commitRequested will be set to false to allow new commit
+ // requests to be scheduled.
+ m_commitRequested = true;
+
+ // On the other hand, the animationRequested flag needs to be cleared
+ // here so that any animation requests generated by the apply or animate
+ // callbacks will trigger another frame.
+ m_animateRequested = false;
+
+ // FIXME: technically, scroll deltas need to be applied for dropped commits as well.
+ // Re-do the commit flow so that we don't send the scrollInfo on the BFAC message.
+ m_layerTreeHost->applyScrollAndScale(*request->scrollInfo);
+
+ if (!m_inCompositeAndReadback && !m_layerTreeHost->visible()) {
+ m_commitRequested = false;
+ m_forcedCommitRequested = false;
+
+ TRACE_EVENT0("cc", "EarlyOut_NotVisible");
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrameAbortedOnImplThread));
+ return;
+ }
+
+ m_layerTreeHost->willBeginFrame();
+
+ m_layerTreeHost->updateAnimations(request->monotonicFrameBeginTime);
+ m_layerTreeHost->layout();
+
+ // Clear the commit flag after updating animations and layout here --- objects that only
+ // layout when painted will trigger another setNeedsCommit inside
+ // updateLayers.
+ m_commitRequested = false;
+ m_forcedCommitRequested = false;
+
+ if (!m_layerTreeHost->initializeRendererIfNeeded())
+ return;
+
+ if (request->contentsTexturesWereDeleted)
+ m_layerTreeHost->evictAllContentTextures();
+
+ OwnPtr<CCTextureUpdateQueue> queue = adoptPtr(new CCTextureUpdateQueue);
+ m_layerTreeHost->updateLayers(*(queue.get()), request->memoryAllocationLimitBytes);
+
+ // Once single buffered layers are committed, they cannot be modified until
+ // they are drawn by the impl thread.
+ m_texturesAcquired = false;
+
+ m_layerTreeHost->willCommit();
+ // Before applying scrolls and calling animate, we set m_animateRequested to false.
+ // If it is true now, it means setNeedAnimate was called again. Call setNeedsCommit
+ // now so that we get begin frame when this one is done.
+ if (m_animateRequested)
+ setNeedsCommit();
+
+ // Notify the impl thread that the beginFrame has completed. This will
+ // begin the commit process, which is blocking from the main thread's
+ // point of view, but asynchronously performed on the impl thread,
+ // coordinated by the CCScheduler.
+ {
+ TRACE_EVENT0("cc", "commit");
+ DebugScopedSetMainThreadBlocked mainThreadBlocked;
+
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::beginFrameCompleteOnImplThread, &completion, queue.release(), request->contentsTexturesWereDeleted));
+ completion.wait();
+ }
+
+ m_layerTreeHost->commitComplete();
+ m_layerTreeHost->didBeginFrame();
+}
+
+void CCThreadProxy::beginFrameCompleteOnImplThread(CCCompletionEvent* completion, PassOwnPtr<CCTextureUpdateQueue> queue, bool contentsTexturesWereDeleted)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::beginFrameCompleteOnImplThread");
+ ASSERT(!m_commitCompletionEventOnImplThread);
+ ASSERT(isImplThread());
+ ASSERT(m_schedulerOnImplThread);
+ ASSERT(m_schedulerOnImplThread->commitPending());
+
+ if (!m_layerTreeHostImpl) {
+ completion->signal();
+ return;
+ }
+
+ if (!contentsTexturesWereDeleted && m_layerTreeHostImpl->contentsTexturesPurged()) {
+ // We purged the content textures on the impl thread between the time we
+ // posted the beginFrame task and now, meaning we have a bunch of
+ // uploads that are now invalid. Clear the uploads (they all go to
+ // content textures), and kick another commit to fill them again.
+ queue->clearUploads();
+ setNeedsCommitOnImplThread();
+ } else
+ m_resetContentsTexturesPurgedAfterCommitOnImplThread = true;
+
+ m_currentTextureUpdateControllerOnImplThread = CCTextureUpdateController::create(CCProxy::implThread(), queue, m_layerTreeHostImpl->resourceProvider(), m_layerTreeHostImpl->renderer()->textureCopier(), m_layerTreeHostImpl->renderer()->textureUploader());
+ m_commitCompletionEventOnImplThread = completion;
+
+ m_schedulerOnImplThread->beginFrameComplete();
+}
+
+void CCThreadProxy::beginFrameAbortedOnImplThread()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::beginFrameAbortedOnImplThread");
+ ASSERT(isImplThread());
+ ASSERT(m_schedulerOnImplThread);
+ ASSERT(m_schedulerOnImplThread->commitPending());
+
+ m_schedulerOnImplThread->beginFrameAborted();
+}
+
+bool CCThreadProxy::hasMoreResourceUpdates() const
+{
+ if (!m_currentTextureUpdateControllerOnImplThread)
+ return false;
+ return m_currentTextureUpdateControllerOnImplThread->hasMoreUpdates();
+}
+
+bool CCThreadProxy::canDraw()
+{
+ ASSERT(isImplThread());
+ if (!m_layerTreeHostImpl)
+ return false;
+ return m_layerTreeHostImpl->canDraw();
+}
+
+void CCThreadProxy::scheduledActionUpdateMoreResources(double monotonicTimeLimit)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::scheduledActionUpdateMoreResources");
+ ASSERT(m_currentTextureUpdateControllerOnImplThread);
+ m_currentTextureUpdateControllerOnImplThread->updateMoreTextures(monotonicTimeLimit);
+}
+
+void CCThreadProxy::scheduledActionCommit()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::scheduledActionCommit");
+ ASSERT(isImplThread());
+ ASSERT(m_currentTextureUpdateControllerOnImplThread);
+ ASSERT(!m_currentTextureUpdateControllerOnImplThread->hasMoreUpdates());
+ ASSERT(m_commitCompletionEventOnImplThread);
+
+ m_currentTextureUpdateControllerOnImplThread.clear();
+
+ m_layerTreeHostImpl->beginCommit();
+
+ m_layerTreeHost->beginCommitOnImplThread(m_layerTreeHostImpl.get());
+ m_layerTreeHost->finishCommitOnImplThread(m_layerTreeHostImpl.get());
+
+ if (m_resetContentsTexturesPurgedAfterCommitOnImplThread) {
+ m_resetContentsTexturesPurgedAfterCommitOnImplThread = false;
+ m_layerTreeHostImpl->resetContentsTexturesPurged();
+ }
+
+ m_layerTreeHostImpl->commitComplete();
+
+ m_nextFrameIsNewlyCommittedFrameOnImplThread = true;
+
+ m_commitCompletionEventOnImplThread->signal();
+ m_commitCompletionEventOnImplThread = 0;
+
+ // SetVisible kicks off the next scheduler action, so this must be last.
+ m_schedulerOnImplThread->setVisible(m_layerTreeHostImpl->visible());
+}
+
+void CCThreadProxy::scheduledActionBeginContextRecreation()
+{
+ ASSERT(isImplThread());
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::beginContextRecreation));
+}
+
+CCScheduledActionDrawAndSwapResult CCThreadProxy::scheduledActionDrawAndSwapInternal(bool forcedDraw)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::scheduledActionDrawAndSwap");
+ CCScheduledActionDrawAndSwapResult result;
+ result.didDraw = false;
+ result.didSwap = false;
+ ASSERT(isImplThread());
+ ASSERT(m_layerTreeHostImpl);
+ if (!m_layerTreeHostImpl)
+ return result;
+
+ ASSERT(m_layerTreeHostImpl->renderer());
+ if (!m_layerTreeHostImpl->renderer())
+ return result;
+
+ // FIXME: compute the frame display time more intelligently
+ double monotonicTime = monotonicallyIncreasingTime();
+ double wallClockTime = currentTime();
+
+ m_inputHandlerOnImplThread->animate(monotonicTime);
+ m_layerTreeHostImpl->animate(monotonicTime, wallClockTime);
+
+ // This method is called on a forced draw, regardless of whether we are able to produce a frame,
+ // as the calling site on main thread is blocked until its request completes, and we signal
+ // completion here. If canDraw() is false, we will indicate success=false to the caller, but we
+ // must still signal completion to avoid deadlock.
+
+ // We guard prepareToDraw() with canDraw() because it always returns a valid frame, so can only
+ // be used when such a frame is possible. Since drawLayers() depends on the result of
+ // prepareToDraw(), it is guarded on canDraw() as well.
+
+ CCLayerTreeHostImpl::FrameData frame;
+ bool drawFrame = m_layerTreeHostImpl->canDraw() && (m_layerTreeHostImpl->prepareToDraw(frame) || forcedDraw);
+ if (drawFrame) {
+ m_layerTreeHostImpl->drawLayers(frame);
+ result.didDraw = true;
+ }
+ m_layerTreeHostImpl->didDrawAllLayers(frame);
+
+ // Check for a pending compositeAndReadback.
+ if (m_readbackRequestOnImplThread) {
+ m_readbackRequestOnImplThread->success = false;
+ if (drawFrame) {
+ m_layerTreeHostImpl->readback(m_readbackRequestOnImplThread->pixels, m_readbackRequestOnImplThread->rect);
+ m_readbackRequestOnImplThread->success = !m_layerTreeHostImpl->isContextLost();
+ }
+ m_readbackRequestOnImplThread->completion.signal();
+ m_readbackRequestOnImplThread = 0;
+ } else if (drawFrame)
+ result.didSwap = m_layerTreeHostImpl->swapBuffers();
+
+ // Tell the main thread that the the newly-commited frame was drawn.
+ if (m_nextFrameIsNewlyCommittedFrameOnImplThread) {
+ m_nextFrameIsNewlyCommittedFrameOnImplThread = false;
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadProxy::didCommitAndDrawFrame));
+ }
+
+ return result;
+}
+
+void CCThreadProxy::acquireLayerTextures()
+{
+ // Called when the main thread needs to modify a layer texture that is used
+ // directly by the compositor.
+ // This method will block until the next compositor draw if there is a
+ // previously committed frame that is still undrawn. This is necessary to
+ // ensure that the main thread does not monopolize access to the textures.
+ ASSERT(isMainThread());
+
+ if (m_texturesAcquired)
+ return;
+
+ TRACE_EVENT0("cc", "CCThreadProxy::acquireLayerTextures");
+ CCCompletionEvent completion;
+ CCProxy::implThread()->postTask(createCCThreadTask(this, &CCThreadProxy::acquireLayerTexturesForMainThreadOnImplThread, &completion));
+ completion.wait(); // Block until it is safe to write to layer textures from the main thread.
+
+ m_texturesAcquired = true;
+}
+
+void CCThreadProxy::acquireLayerTexturesForMainThreadOnImplThread(CCCompletionEvent* completion)
+{
+ ASSERT(isImplThread());
+ ASSERT(!m_textureAcquisitionCompletionEventOnImplThread);
+
+ m_textureAcquisitionCompletionEventOnImplThread = completion;
+ m_schedulerOnImplThread->setMainThreadNeedsLayerTextures();
+}
+
+void CCThreadProxy::scheduledActionAcquireLayerTexturesForMainThread()
+{
+ ASSERT(m_textureAcquisitionCompletionEventOnImplThread);
+ m_textureAcquisitionCompletionEventOnImplThread->signal();
+ m_textureAcquisitionCompletionEventOnImplThread = 0;
+}
+
+CCScheduledActionDrawAndSwapResult CCThreadProxy::scheduledActionDrawAndSwapIfPossible()
+{
+ return scheduledActionDrawAndSwapInternal(false);
+}
+
+CCScheduledActionDrawAndSwapResult CCThreadProxy::scheduledActionDrawAndSwapForced()
+{
+ return scheduledActionDrawAndSwapInternal(true);
+}
+
+void CCThreadProxy::didCommitAndDrawFrame()
+{
+ ASSERT(isMainThread());
+ if (!m_layerTreeHost)
+ return;
+ m_layerTreeHost->didCommitAndDrawFrame();
+}
+
+void CCThreadProxy::didCompleteSwapBuffers()
+{
+ ASSERT(isMainThread());
+ if (!m_layerTreeHost)
+ return;
+ m_layerTreeHost->didCompleteSwapBuffers();
+}
+
+void CCThreadProxy::setAnimationEvents(PassOwnPtr<CCAnimationEventsVector> events, double wallClockTime)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::setAnimationEvents");
+ ASSERT(isMainThread());
+ if (!m_layerTreeHost)
+ return;
+ m_layerTreeHost->setAnimationEvents(events, wallClockTime);
+}
+
+class CCThreadProxyContextRecreationTimer : public CCTimer, CCTimerClient {
+public:
+ static PassOwnPtr<CCThreadProxyContextRecreationTimer> create(CCThreadProxy* proxy) { return adoptPtr(new CCThreadProxyContextRecreationTimer(proxy)); }
+
+ virtual void onTimerFired() OVERRIDE
+ {
+ m_proxy->tryToRecreateContext();
+ }
+
+private:
+ explicit CCThreadProxyContextRecreationTimer(CCThreadProxy* proxy)
+ : CCTimer(CCProxy::mainThread(), this)
+ , m_proxy(proxy)
+ {
+ }
+
+ CCThreadProxy* m_proxy;
+};
+
+void CCThreadProxy::beginContextRecreation()
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::beginContextRecreation");
+ ASSERT(isMainThread());
+ ASSERT(!m_contextRecreationTimer);
+ m_contextRecreationTimer = CCThreadProxyContextRecreationTimer::create(this);
+ m_layerTreeHost->didLoseContext();
+ m_contextRecreationTimer->startOneShot(contextRecreationTickRate);
+}
+
+void CCThreadProxy::tryToRecreateContext()
+{
+ ASSERT(isMainThread());
+ ASSERT(m_layerTreeHost);
+ CCLayerTreeHost::RecreateResult result = m_layerTreeHost->recreateContext();
+ if (result == CCLayerTreeHost::RecreateFailedButTryAgain)
+ m_contextRecreationTimer->startOneShot(contextRecreationTickRate);
+ else if (result == CCLayerTreeHost::RecreateSucceeded)
+ m_contextRecreationTimer.clear();
+}
+
+void CCThreadProxy::initializeImplOnImplThread(CCCompletionEvent* completion)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::initializeImplOnImplThread");
+ ASSERT(isImplThread());
+ m_layerTreeHostImpl = m_layerTreeHost->createLayerTreeHostImpl(this);
+ const double displayRefreshInterval = 1.0 / 60.0;
+ OwnPtr<CCFrameRateController> frameRateController;
+ if (m_renderVSyncEnabled)
+ frameRateController = adoptPtr(new CCFrameRateController(CCDelayBasedTimeSource::create(displayRefreshInterval, CCProxy::implThread())));
+ else
+ frameRateController = adoptPtr(new CCFrameRateController(CCProxy::implThread()));
+ m_schedulerOnImplThread = CCScheduler::create(this, frameRateController.release());
+ m_schedulerOnImplThread->setVisible(m_layerTreeHostImpl->visible());
+
+ m_inputHandlerOnImplThread = CCInputHandler::create(m_layerTreeHostImpl.get());
+ m_compositorIdentifier = m_inputHandlerOnImplThread->identifier();
+
+ completion->signal();
+}
+
+void CCThreadProxy::initializeContextOnImplThread(CCGraphicsContext* context)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::initializeContextOnImplThread");
+ ASSERT(isImplThread());
+ m_contextBeforeInitializationOnImplThread = adoptPtr(context);
+}
+
+void CCThreadProxy::initializeRendererOnImplThread(CCCompletionEvent* completion, bool* initializeSucceeded, RendererCapabilities* capabilities)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::initializeRendererOnImplThread");
+ ASSERT(isImplThread());
+ ASSERT(m_contextBeforeInitializationOnImplThread);
+ *initializeSucceeded = m_layerTreeHostImpl->initializeRenderer(m_contextBeforeInitializationOnImplThread.release(), textureUploader);
+ if (*initializeSucceeded) {
+ *capabilities = m_layerTreeHostImpl->rendererCapabilities();
+ if (capabilities->usingSwapCompleteCallback)
+ m_schedulerOnImplThread->setMaxFramesPending(2);
+ }
+
+ completion->signal();
+}
+
+void CCThreadProxy::layerTreeHostClosedOnImplThread(CCCompletionEvent* completion)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::layerTreeHostClosedOnImplThread");
+ ASSERT(isImplThread());
+ if (!m_layerTreeHostImpl->contentsTexturesPurged())
+ m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
+ m_inputHandlerOnImplThread.clear();
+ m_layerTreeHostImpl.clear();
+ m_schedulerOnImplThread.clear();
+ completion->signal();
+}
+
+void CCThreadProxy::setFullRootLayerDamageOnImplThread()
+{
+ ASSERT(isImplThread());
+ m_layerTreeHostImpl->setFullRootLayerDamage();
+}
+
+size_t CCThreadProxy::maxPartialTextureUpdates() const
+{
+ return CCTextureUpdateController::maxPartialTextureUpdates();
+}
+
+void CCThreadProxy::recreateContextOnImplThread(CCCompletionEvent* completion, CCGraphicsContext* contextPtr, bool* recreateSucceeded, RendererCapabilities* capabilities)
+{
+ TRACE_EVENT0("cc", "CCThreadProxy::recreateContextOnImplThread");
+ ASSERT(isImplThread());
+ if (!m_layerTreeHostImpl->contentsTexturesPurged())
+ m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
+ *recreateSucceeded = m_layerTreeHostImpl->initializeRenderer(adoptPtr(contextPtr), textureUploader);
+ if (*recreateSucceeded) {
+ *capabilities = m_layerTreeHostImpl->rendererCapabilities();
+ m_schedulerOnImplThread->didRecreateContext();
+ }
+ completion->signal();
+}
+
+void CCThreadProxy::implSideRenderingStatsOnImplThread(CCCompletionEvent* completion, CCRenderingStats* stats)
+{
+ ASSERT(isImplThread());
+ m_layerTreeHostImpl->renderingStats(*stats);
+ completion->signal();
+}
+
+} // namespace WebCore
diff --git a/cc/CCThreadProxy.h b/cc/CCThreadProxy.h
new file mode 100644
index 0000000..cc80ebd
--- /dev/null
+++ b/cc/CCThreadProxy.h
@@ -0,0 +1,180 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCThreadProxy_h
+#define CCThreadProxy_h
+
+#include "CCAnimationEvents.h"
+#include "CCCompletionEvent.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCProxy.h"
+#include "CCScheduler.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class CCInputHandler;
+class CCLayerTreeHost;
+class CCScheduler;
+class CCScopedThreadProxy;
+class CCTextureUpdateQueue;
+class CCTextureUpdateController;
+class CCThread;
+class CCThreadProxyContextRecreationTimer;
+
+class CCThreadProxy : public CCProxy, CCLayerTreeHostImplClient, CCSchedulerClient {
+public:
+ static PassOwnPtr<CCProxy> create(CCLayerTreeHost*);
+
+ virtual ~CCThreadProxy();
+
+ // CCProxy implementation
+ virtual bool compositeAndReadback(void *pixels, const IntRect&) OVERRIDE;
+ virtual void startPageScaleAnimation(const IntSize& targetPosition, bool useAnchor, float scale, double duration) OVERRIDE;
+ virtual void finishAllRendering() OVERRIDE;
+ virtual bool isStarted() const OVERRIDE;
+ virtual bool initializeContext() OVERRIDE;
+ virtual void setSurfaceReady() OVERRIDE;
+ virtual void setVisible(bool) OVERRIDE;
+ virtual bool initializeRenderer() OVERRIDE;
+ virtual bool recreateContext() OVERRIDE;
+ virtual int compositorIdentifier() const OVERRIDE;
+ virtual void implSideRenderingStats(CCRenderingStats&) OVERRIDE;
+ virtual const RendererCapabilities& rendererCapabilities() const OVERRIDE;
+ virtual void loseContext() OVERRIDE;
+ virtual void setNeedsAnimate() OVERRIDE;
+ virtual void setNeedsCommit() OVERRIDE;
+ virtual void setNeedsRedraw() OVERRIDE;
+ virtual bool commitRequested() const OVERRIDE;
+ virtual void didAddAnimation() OVERRIDE { }
+ virtual void start() OVERRIDE;
+ virtual void stop() OVERRIDE;
+ virtual size_t maxPartialTextureUpdates() const OVERRIDE;
+ virtual void acquireLayerTextures() OVERRIDE;
+ virtual void forceSerializeOnSwapBuffers() OVERRIDE;
+
+ // CCLayerTreeHostImplClient implementation
+ virtual void didLoseContextOnImplThread() OVERRIDE;
+ virtual void onSwapBuffersCompleteOnImplThread() OVERRIDE;
+ virtual void onVSyncParametersChanged(double monotonicTimebase, double intervalInSeconds) OVERRIDE;
+ virtual void setNeedsRedrawOnImplThread() OVERRIDE;
+ virtual void setNeedsCommitOnImplThread() OVERRIDE;
+ virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) OVERRIDE;
+
+ // CCSchedulerClient implementation
+ virtual bool canDraw() OVERRIDE;
+ virtual bool hasMoreResourceUpdates() const OVERRIDE;
+ virtual void scheduledActionBeginFrame() OVERRIDE;
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE;
+ virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE;
+ virtual void scheduledActionUpdateMoreResources(double monotonicTimeLimit) OVERRIDE;
+ virtual void scheduledActionCommit() OVERRIDE;
+ virtual void scheduledActionBeginContextRecreation() OVERRIDE;
+ virtual void scheduledActionAcquireLayerTexturesForMainThread() OVERRIDE;
+
+private:
+ explicit CCThreadProxy(CCLayerTreeHost*);
+ friend class CCThreadProxyContextRecreationTimer;
+
+ // Set on impl thread, read on main thread.
+ struct BeginFrameAndCommitState {
+ BeginFrameAndCommitState()
+ : monotonicFrameBeginTime(0)
+ {
+ }
+
+ double monotonicFrameBeginTime;
+ OwnPtr<CCScrollAndScaleSet> scrollInfo;
+ bool contentsTexturesWereDeleted;
+ size_t memoryAllocationLimitBytes;
+ };
+ OwnPtr<BeginFrameAndCommitState> m_pendingBeginFrameRequest;
+
+ // Called on main thread
+ void beginFrame();
+ void didCommitAndDrawFrame();
+ void didCompleteSwapBuffers();
+ void setAnimationEvents(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime);
+ void beginContextRecreation();
+ void tryToRecreateContext();
+
+ // Called on impl thread
+ struct ReadbackRequest {
+ CCCompletionEvent completion;
+ bool success;
+ void* pixels;
+ IntRect rect;
+ };
+ void forceBeginFrameOnImplThread(CCCompletionEvent*);
+ void beginFrameCompleteOnImplThread(CCCompletionEvent*, PassOwnPtr<CCTextureUpdateQueue>, bool contentsTexturesWereDeleted);
+ void beginFrameAbortedOnImplThread();
+ void requestReadbackOnImplThread(ReadbackRequest*);
+ void requestStartPageScaleAnimationOnImplThread(IntSize targetPosition, bool useAnchor, float scale, double durationSec);
+ void finishAllRenderingOnImplThread(CCCompletionEvent*);
+ void initializeImplOnImplThread(CCCompletionEvent*);
+ void setSurfaceReadyOnImplThread();
+ void setVisibleOnImplThread(CCCompletionEvent*, bool);
+ void initializeContextOnImplThread(CCGraphicsContext*);
+ void initializeRendererOnImplThread(CCCompletionEvent*, bool* initializeSucceeded, RendererCapabilities*);
+ void layerTreeHostClosedOnImplThread(CCCompletionEvent*);
+ void setFullRootLayerDamageOnImplThread();
+ void acquireLayerTexturesForMainThreadOnImplThread(CCCompletionEvent*);
+ void recreateContextOnImplThread(CCCompletionEvent*, CCGraphicsContext*, bool* recreateSucceeded, RendererCapabilities*);
+ void implSideRenderingStatsOnImplThread(CCCompletionEvent*, CCRenderingStats*);
+ CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapInternal(bool forcedDraw);
+ void forceSerializeOnSwapBuffersOnImplThread(CCCompletionEvent*);
+ void setNeedsForcedCommitOnImplThread();
+
+ // Accessed on main thread only.
+ bool m_animateRequested;
+ bool m_commitRequested;
+ bool m_forcedCommitRequested;
+ OwnPtr<CCThreadProxyContextRecreationTimer> m_contextRecreationTimer;
+ CCLayerTreeHost* m_layerTreeHost;
+ int m_compositorIdentifier;
+ bool m_rendererInitialized;
+ RendererCapabilities m_RendererCapabilitiesMainThreadCopy;
+ bool m_started;
+ bool m_texturesAcquired;
+ bool m_inCompositeAndReadback;
+
+ OwnPtr<CCLayerTreeHostImpl> m_layerTreeHostImpl;
+
+ OwnPtr<CCInputHandler> m_inputHandlerOnImplThread;
+
+ OwnPtr<CCScheduler> m_schedulerOnImplThread;
+
+ RefPtr<CCScopedThreadProxy> m_mainThreadProxy;
+
+ // Holds on to the context we might use for compositing in between initializeContext()
+ // and initializeRenderer() calls.
+ OwnPtr<CCGraphicsContext> m_contextBeforeInitializationOnImplThread;
+
+ // Set when the main thread is waiting on a scheduledActionBeginFrame to be issued.
+ CCCompletionEvent* m_beginFrameCompletionEventOnImplThread;
+
+ // Set when the main thread is waiting on a readback.
+ ReadbackRequest* m_readbackRequestOnImplThread;
+
+ // Set when the main thread is waiting on a commit to complete.
+ CCCompletionEvent* m_commitCompletionEventOnImplThread;
+
+ // Set when the main thread is waiting on layers to be drawn.
+ CCCompletionEvent* m_textureAcquisitionCompletionEventOnImplThread;
+
+ OwnPtr<CCTextureUpdateController> m_currentTextureUpdateControllerOnImplThread;
+
+ // Set when we need to reset the contentsTexturesPurged flag after the
+ // commit.
+ bool m_resetContentsTexturesPurgedAfterCommitOnImplThread;
+
+ // Set when the next draw should post didCommitAndDrawFrame to the main thread.
+ bool m_nextFrameIsNewlyCommittedFrameOnImplThread;
+
+ bool m_renderVSyncEnabled;
+};
+
+}
+
+#endif
diff --git a/cc/CCThreadTask.h b/cc/CCThreadTask.h
new file mode 100644
index 0000000..c9d843e
--- /dev/null
+++ b/cc/CCThreadTask.h
@@ -0,0 +1,305 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef CCThreadTask_h
+#define CCThreadTask_h
+
+#include "CCThread.h"
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+template<typename T>
+class CCThreadTask0 : public CCThread::Task {
+public:
+ typedef void (T::*Method)();
+ typedef CCThreadTask0<T> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method));
+ }
+
+private:
+ CCThreadTask0(T* instance, Method method)
+ : CCThread::Task(instance)
+ , m_method(method)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)();
+ }
+
+private:
+ Method m_method;
+};
+
+template<typename T, typename P1, typename MP1>
+class CCThreadTask1 : public CCThread::Task {
+public:
+ typedef void (T::*Method)(MP1);
+ typedef CCThreadTask1<T, P1, MP1> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method, P1 parameter1)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method, parameter1));
+ }
+
+private:
+ CCThreadTask1(T* instance, Method method, P1 parameter1)
+ : CCThread::Task(instance)
+ , m_method(method)
+ , m_parameter1(parameter1)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)(m_parameter1);
+ }
+
+private:
+ Method m_method;
+ P1 m_parameter1;
+};
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2>
+class CCThreadTask2 : public CCThread::Task {
+public:
+ typedef void (T::*Method)(MP1, MP2);
+ typedef CCThreadTask2<T, P1, MP1, P2, MP2> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method, P1 parameter1, P2 parameter2)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method, parameter1, parameter2));
+ }
+
+private:
+ CCThreadTask2(T* instance, Method method, P1 parameter1, P2 parameter2)
+ : CCThread::Task(instance)
+ , m_method(method)
+ , m_parameter1(parameter1)
+ , m_parameter2(parameter2)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)(m_parameter1, m_parameter2);
+ }
+
+private:
+ Method m_method;
+ P1 m_parameter1;
+ P2 m_parameter2;
+};
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3>
+class CCThreadTask3 : public CCThread::Task {
+public:
+ typedef void (T::*Method)(MP1, MP2, MP3);
+ typedef CCThreadTask3<T, P1, MP1, P2, MP2, P3, MP3> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method, parameter1, parameter2, parameter3));
+ }
+
+private:
+ CCThreadTask3(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3)
+ : CCThread::Task(instance)
+ , m_method(method)
+ , m_parameter1(parameter1)
+ , m_parameter2(parameter2)
+ , m_parameter3(parameter3)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)(m_parameter1, m_parameter2, m_parameter3);
+ }
+
+private:
+ Method m_method;
+ P1 m_parameter1;
+ P2 m_parameter2;
+ P3 m_parameter3;
+};
+
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4>
+class CCThreadTask4 : public CCThread::Task {
+public:
+ typedef void (T::*Method)(MP1, MP2, MP3, MP4);
+ typedef CCThreadTask4<T, P1, MP1, P2, MP2, P3, MP3, P4, MP4> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3, P4 parameter4)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method, parameter1, parameter2, parameter3, parameter4));
+ }
+
+private:
+ CCThreadTask4(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3, P4 parameter4)
+ : CCThread::Task(instance)
+ , m_method(method)
+ , m_parameter1(parameter1)
+ , m_parameter2(parameter2)
+ , m_parameter3(parameter3)
+ , m_parameter4(parameter4)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)(m_parameter1, m_parameter2, m_parameter3, m_parameter4);
+ }
+
+private:
+ Method m_method;
+ P1 m_parameter1;
+ P2 m_parameter2;
+ P3 m_parameter3;
+ P4 m_parameter4;
+};
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5>
+class CCThreadTask5 : public CCThread::Task {
+public:
+ typedef void (T::*Method)(MP1, MP2, MP3, MP4, MP5);
+ typedef CCThreadTask5<T, P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5> CCThreadTaskImpl;
+
+ static PassOwnPtr<CCThreadTaskImpl> create(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3, P4 parameter4, P5 parameter5)
+ {
+ return adoptPtr(new CCThreadTaskImpl(instance, method, parameter1, parameter2, parameter3, parameter4, parameter5));
+ }
+
+private:
+ CCThreadTask5(T* instance, Method method, P1 parameter1, P2 parameter2, P3 parameter3, P4 parameter4, P5 parameter5)
+ : CCThread::Task(instance)
+ , m_method(method)
+ , m_parameter1(parameter1)
+ , m_parameter2(parameter2)
+ , m_parameter3(parameter3)
+ , m_parameter4(parameter4)
+ , m_parameter5(parameter5)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ (*static_cast<T*>(instance()).*m_method)(m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5);
+ }
+
+private:
+ Method m_method;
+ P1 m_parameter1;
+ P2 m_parameter2;
+ P3 m_parameter3;
+ P4 m_parameter4;
+ P5 m_parameter5;
+};
+
+template<typename T>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)());
+
+template<typename T>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)())
+{
+ return CCThreadTask0<T>::create(
+ callee,
+ method);
+}
+
+template<typename T, typename P1, typename MP1>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)(MP1),
+ const P1& parameter1)
+{
+ return CCThreadTask1<T, P1, MP1>::create(
+ callee,
+ method,
+ parameter1);
+}
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)(MP1, MP2),
+ const P1& parameter1,
+ const P2& parameter2)
+{
+ return CCThreadTask2<T, P1, MP1, P2, MP2>::create(
+ callee,
+ method,
+ parameter1,
+ parameter2);
+}
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)(MP1, MP2, MP3),
+ const P1& parameter1,
+ const P2& parameter2,
+ const P3& parameter3)
+{
+ return CCThreadTask3<T, P1, MP1, P2, MP2, P3, MP3>::create(
+ callee,
+ method,
+ parameter1,
+ parameter2,
+ parameter3);
+}
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)(MP1, MP2, MP3, MP4),
+ const P1& parameter1,
+ const P2& parameter2,
+ const P3& parameter3,
+ const P4& parameter4)
+{
+ return CCThreadTask4<T, P1, MP1, P2, MP2, P3, MP3, P4, MP4>::create(
+ callee,
+ method,
+ parameter1,
+ parameter2,
+ parameter3,
+ parameter4);
+
+}
+
+template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5>
+PassOwnPtr<CCThread::Task> createCCThreadTask(
+ T* const callee,
+ void (T::*method)(MP1, MP2, MP3, MP4, MP5),
+ const P1& parameter1,
+ const P2& parameter2,
+ const P3& parameter3,
+ const P4& parameter4,
+ const P5& parameter5)
+{
+ return CCThreadTask5<T, P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5>::create(
+ callee,
+ method,
+ parameter1,
+ parameter2,
+ parameter3,
+ parameter4,
+ parameter5);
+
+}
+
+} // namespace WebCore
+
+#endif // CCThreadTask_h
diff --git a/cc/CCThreadTaskTest.cpp b/cc/CCThreadTaskTest.cpp
new file mode 100644
index 0000000..7a757ef
--- /dev/null
+++ b/cc/CCThreadTaskTest.cpp
@@ -0,0 +1,45 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCThreadTask.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace WTF;
+using namespace WebCore;
+
+namespace {
+
+class Mock {
+public:
+ MOCK_METHOD0(method0, void());
+ MOCK_METHOD1(method1, void(int a1));
+ MOCK_METHOD2(method2, void(int a1, int a2));
+ MOCK_METHOD3(method3, void(int a1, int a2, int a3));
+ MOCK_METHOD4(method4, void(int a1, int a2, int a3, int a4));
+ MOCK_METHOD5(method5, void(int a1, int a2, int a3, int a4, int a5));
+};
+
+TEST(CCThreadTaskTest, runnableMethods)
+{
+ Mock mock;
+ EXPECT_CALL(mock, method0()).Times(1);
+ EXPECT_CALL(mock, method1(9)).Times(1);
+ EXPECT_CALL(mock, method2(9, 8)).Times(1);
+ EXPECT_CALL(mock, method3(9, 8, 7)).Times(1);
+ EXPECT_CALL(mock, method4(9, 8, 7, 6)).Times(1);
+ EXPECT_CALL(mock, method5(9, 8, 7, 6, 5)).Times(1);
+
+ createCCThreadTask(&mock, &Mock::method0)->performTask();
+ createCCThreadTask(&mock, &Mock::method1, 9)->performTask();
+ createCCThreadTask(&mock, &Mock::method2, 9, 8)->performTask();
+ createCCThreadTask(&mock, &Mock::method3, 9, 8, 7)->performTask();
+ createCCThreadTask(&mock, &Mock::method4, 9, 8, 7, 6)->performTask();
+ createCCThreadTask(&mock, &Mock::method5, 9, 8, 7, 6, 5)->performTask();
+}
+
+} // namespace
diff --git a/cc/CCThreadedTest.cpp b/cc/CCThreadedTest.cpp
new file mode 100644
index 0000000..09526ac
--- /dev/null
+++ b/cc/CCThreadedTest.cpp
@@ -0,0 +1,622 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCThreadedTest.h"
+
+#include "CCActiveAnimation.h"
+#include "CCAnimationTestCommon.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCOcclusionTrackerTestCommon.h"
+#include "CCScopedThreadProxy.h"
+#include "CCSingleThreadProxy.h"
+#include "CCTextureUpdateQueue.h"
+#include "CCThreadTask.h"
+#include "CCTiledLayerTestCommon.h"
+#include "CCTimingFunction.h"
+#include "ContentLayerChromium.h"
+#include "FakeWebCompositorOutputSurface.h"
+#include "FakeWebGraphicsContext3D.h"
+#include "LayerChromium.h"
+#include <gmock/gmock.h>
+#include <public/Platform.h>
+#include <public/WebCompositor.h>
+#include <public/WebFilterOperation.h>
+#include <public/WebFilterOperations.h>
+#include <public/WebThread.h>
+#include <wtf/Locker.h>
+#include <wtf/MainThread.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+using namespace WebKit;
+using namespace WTF;
+
+namespace WebKitTests {
+
+PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> CompositorFakeWebGraphicsContext3DWithTextureTracking::create(Attributes attrs)
+{
+ return adoptPtr(new CompositorFakeWebGraphicsContext3DWithTextureTracking(attrs));
+}
+
+WebGLId CompositorFakeWebGraphicsContext3DWithTextureTracking::createTexture()
+{
+ WebGLId texture = m_textures.size() + 1;
+ m_textures.append(texture);
+ return texture;
+}
+
+void CompositorFakeWebGraphicsContext3DWithTextureTracking::deleteTexture(WebGLId texture)
+{
+ for (size_t i = 0; i < m_textures.size(); i++) {
+ if (m_textures[i] == texture) {
+ m_textures.remove(i);
+ break;
+ }
+ }
+}
+
+void CompositorFakeWebGraphicsContext3DWithTextureTracking::bindTexture(WGC3Denum /* target */, WebGLId texture)
+{
+ m_usedTextures.add(texture);
+}
+
+int CompositorFakeWebGraphicsContext3DWithTextureTracking::numTextures() const { return static_cast<int>(m_textures.size()); }
+int CompositorFakeWebGraphicsContext3DWithTextureTracking::texture(int i) const { return m_textures[i]; }
+void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetTextures() { m_textures.clear(); }
+
+int CompositorFakeWebGraphicsContext3DWithTextureTracking::numUsedTextures() const { return static_cast<int>(m_usedTextures.size()); }
+bool CompositorFakeWebGraphicsContext3DWithTextureTracking::usedTexture(int texture) const { return m_usedTextures.find(texture) != m_usedTextures.end(); }
+void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetUsedTextures() { m_usedTextures.clear(); }
+
+CompositorFakeWebGraphicsContext3DWithTextureTracking::CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs) : CompositorFakeWebGraphicsContext3D(attrs)
+{
+}
+
+PassOwnPtr<WebCompositorOutputSurface> TestHooks::createOutputSurface()
+{
+ return FakeWebCompositorOutputSurface::create(CompositorFakeWebGraphicsContext3DWithTextureTracking::create(WebGraphicsContext3D::Attributes()));
+}
+
+PassOwnPtr<MockLayerTreeHostImpl> MockLayerTreeHostImpl::create(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client)
+{
+ return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings, client));
+}
+
+void MockLayerTreeHostImpl::beginCommit()
+{
+ CCLayerTreeHostImpl::beginCommit();
+ m_testHooks->beginCommitOnCCThread(this);
+}
+
+void MockLayerTreeHostImpl::commitComplete()
+{
+ CCLayerTreeHostImpl::commitComplete();
+ m_testHooks->commitCompleteOnCCThread(this);
+}
+
+bool MockLayerTreeHostImpl::prepareToDraw(FrameData& frame)
+{
+ bool result = CCLayerTreeHostImpl::prepareToDraw(frame);
+ if (!m_testHooks->prepareToDrawOnCCThread(this))
+ result = false;
+ return result;
+}
+
+void MockLayerTreeHostImpl::drawLayers(const FrameData& frame)
+{
+ CCLayerTreeHostImpl::drawLayers(frame);
+ m_testHooks->drawLayersOnCCThread(this);
+}
+
+void MockLayerTreeHostImpl::animateLayers(double monotonicTime, double wallClockTime)
+{
+ m_testHooks->willAnimateLayers(this, monotonicTime);
+ CCLayerTreeHostImpl::animateLayers(monotonicTime, wallClockTime);
+ m_testHooks->animateLayers(this, monotonicTime);
+}
+
+double MockLayerTreeHostImpl::lowFrequencyAnimationInterval() const
+{
+ return 1.0 / 60;
+}
+
+MockLayerTreeHostImpl::MockLayerTreeHostImpl(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client)
+ : CCLayerTreeHostImpl(settings, client)
+ , m_testHooks(testHooks)
+{
+}
+
+// Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl.
+class MockLayerTreeHost : public WebCore::CCLayerTreeHost {
+public:
+ static PassOwnPtr<MockLayerTreeHost> create(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, PassRefPtr<WebCore::LayerChromium> rootLayer, const WebCore::CCLayerTreeSettings& settings)
+ {
+ OwnPtr<MockLayerTreeHost> layerTreeHost(adoptPtr(new MockLayerTreeHost(testHooks, client, settings)));
+ bool success = layerTreeHost->initialize();
+ EXPECT_TRUE(success);
+ layerTreeHost->setRootLayer(rootLayer);
+
+ // LayerTreeHostImpl won't draw if it has 1x1 viewport.
+ layerTreeHost->setViewportSize(IntSize(1, 1), IntSize(1, 1));
+
+ layerTreeHost->rootLayer()->setLayerAnimationDelegate(testHooks);
+
+ return layerTreeHost.release();
+ }
+
+ virtual PassOwnPtr<WebCore::CCLayerTreeHostImpl> createLayerTreeHostImpl(WebCore::CCLayerTreeHostImplClient* client)
+ {
+ return MockLayerTreeHostImpl::create(m_testHooks, settings(), client);
+ }
+
+ virtual void didAddAnimation() OVERRIDE
+ {
+ CCLayerTreeHost::didAddAnimation();
+ m_testHooks->didAddAnimation();
+ }
+
+private:
+ MockLayerTreeHost(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, const WebCore::CCLayerTreeSettings& settings)
+ : CCLayerTreeHost(client, settings)
+ , m_testHooks(testHooks)
+ {
+ }
+
+ TestHooks* m_testHooks;
+};
+
+// Implementation of CCLayerTreeHost callback interface.
+class MockLayerTreeHostClient : public MockCCLayerTreeHostClient {
+public:
+ static PassOwnPtr<MockLayerTreeHostClient> create(TestHooks* testHooks)
+ {
+ return adoptPtr(new MockLayerTreeHostClient(testHooks));
+ }
+
+ virtual void willBeginFrame() OVERRIDE
+ {
+ }
+
+ virtual void didBeginFrame() OVERRIDE
+ {
+ }
+
+ virtual void animate(double monotonicTime) OVERRIDE
+ {
+ m_testHooks->animate(monotonicTime);
+ }
+
+ virtual void layout() OVERRIDE
+ {
+ m_testHooks->layout();
+ }
+
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE
+ {
+ m_testHooks->applyScrollAndScale(scrollDelta, scale);
+ }
+
+ virtual PassOwnPtr<WebCompositorOutputSurface> createOutputSurface() OVERRIDE
+ {
+ return m_testHooks->createOutputSurface();
+ }
+
+ virtual void willCommit() OVERRIDE
+ {
+ }
+
+ virtual void didCommit() OVERRIDE
+ {
+ m_testHooks->didCommit();
+ }
+
+ virtual void didCommitAndDrawFrame() OVERRIDE
+ {
+ m_testHooks->didCommitAndDrawFrame();
+ }
+
+ virtual void didCompleteSwapBuffers() OVERRIDE
+ {
+ }
+
+ virtual void didRecreateOutputSurface(bool succeeded) OVERRIDE
+ {
+ m_testHooks->didRecreateOutputSurface(succeeded);
+ }
+
+ virtual void scheduleComposite() OVERRIDE
+ {
+ m_testHooks->scheduleComposite();
+ }
+
+private:
+ explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { }
+
+ TestHooks* m_testHooks;
+};
+
+class TimeoutTask : public WebThread::Task {
+public:
+ explicit TimeoutTask(CCThreadedTest* test)
+ : m_test(test)
+ {
+ }
+
+ void clearTest()
+ {
+ m_test = 0;
+ }
+
+ virtual ~TimeoutTask()
+ {
+ if (m_test)
+ m_test->clearTimeout();
+ }
+
+ virtual void run()
+ {
+ if (m_test)
+ m_test->timeout();
+ }
+
+private:
+ CCThreadedTest* m_test;
+};
+
+class BeginTask : public WebThread::Task {
+public:
+ explicit BeginTask(CCThreadedTest* test)
+ : m_test(test)
+ {
+ }
+
+ virtual ~BeginTask() { }
+ virtual void run()
+ {
+ m_test->doBeginTest();
+ }
+private:
+ CCThreadedTest* m_test;
+};
+
+class EndTestTask : public WebThread::Task {
+public:
+ explicit EndTestTask(CCThreadedTest* test)
+ : m_test(test)
+ {
+ }
+
+ virtual ~EndTestTask()
+ {
+ if (m_test)
+ m_test->clearEndTestTask();
+ }
+
+ void clearTest()
+ {
+ m_test = 0;
+ }
+
+ virtual void run()
+ {
+ if (m_test)
+ m_test->endTest();
+ }
+
+private:
+ CCThreadedTest* m_test;
+};
+
+CCThreadedTest::CCThreadedTest()
+ : m_beginning(false)
+ , m_endWhenBeginReturns(false)
+ , m_timedOut(false)
+ , m_finished(false)
+ , m_scheduled(false)
+ , m_started(false)
+ , m_endTestTask(0)
+{ }
+
+void CCThreadedTest::endTest()
+{
+ m_finished = true;
+
+ // If we are called from the CCThread, re-call endTest on the main thread.
+ if (!isMainThread())
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTest));
+ else {
+ // For the case where we endTest during beginTest(), set a flag to indicate that
+ // the test should end the second beginTest regains control.
+ if (m_beginning)
+ m_endWhenBeginReturns = true;
+ else
+ onEndTest(static_cast<void*>(this));
+ }
+}
+
+void CCThreadedTest::endTestAfterDelay(int delayMilliseconds)
+{
+ // If we are called from the CCThread, re-call endTest on the main thread.
+ if (!isMainThread())
+ m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTestAfterDelay, delayMilliseconds));
+ else {
+ m_endTestTask = new EndTestTask(this);
+ WebKit::Platform::current()->currentThread()->postDelayedTask(m_endTestTask, delayMilliseconds);
+ }
+}
+
+void CCThreadedTest::postSetNeedsAnimateToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimate, this);
+}
+
+void CCThreadedTest::postAddAnimationToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchAddAnimation, this);
+}
+
+void CCThreadedTest::postAddInstantAnimationToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchAddInstantAnimation, this);
+}
+
+void CCThreadedTest::postSetNeedsCommitToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchSetNeedsCommit, this);
+}
+
+void CCThreadedTest::postAcquireLayerTextures()
+{
+ callOnMainThread(CCThreadedTest::dispatchAcquireLayerTextures, this);
+}
+
+void CCThreadedTest::postSetNeedsRedrawToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchSetNeedsRedraw, this);
+}
+
+void CCThreadedTest::postSetNeedsAnimateAndCommitToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimateAndCommit, this);
+}
+
+void CCThreadedTest::postSetVisibleToMainThread(bool visible)
+{
+ callOnMainThread(visible ? CCThreadedTest::dispatchSetVisible : CCThreadedTest::dispatchSetInvisible, this);
+}
+
+void CCThreadedTest::postDidAddAnimationToMainThread()
+{
+ callOnMainThread(CCThreadedTest::dispatchDidAddAnimation, this);
+}
+
+void CCThreadedTest::doBeginTest()
+{
+ ASSERT(isMainThread());
+ m_client = MockLayerTreeHostClient::create(this);
+
+ RefPtr<LayerChromium> rootLayer = LayerChromium::create();
+ m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings);
+ ASSERT_TRUE(m_layerTreeHost);
+ rootLayer->setLayerTreeHost(m_layerTreeHost.get());
+ m_layerTreeHost->setSurfaceReady();
+
+ m_started = true;
+ m_beginning = true;
+ beginTest();
+ m_beginning = false;
+ if (m_endWhenBeginReturns)
+ onEndTest(static_cast<void*>(this));
+}
+
+void CCThreadedTest::timeout()
+{
+ m_timedOut = true;
+ endTest();
+}
+
+void CCThreadedTest::scheduleComposite()
+{
+ if (!m_started || m_scheduled || m_finished)
+ return;
+ m_scheduled = true;
+ callOnMainThread(&CCThreadedTest::dispatchComposite, this);
+}
+
+void CCThreadedTest::onEndTest(void* self)
+{
+ ASSERT(isMainThread());
+ WebKit::Platform::current()->currentThread()->exitRunLoop();
+}
+
+void CCThreadedTest::dispatchSetNeedsAnimate(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->setNeedsAnimate();
+}
+
+void CCThreadedTest::dispatchAddInstantAnimation(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer())
+ addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 0.5, false);
+}
+
+void CCThreadedTest::dispatchAddAnimation(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer())
+ addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 0.5, true);
+}
+
+void CCThreadedTest::dispatchSetNeedsAnimateAndCommit(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost) {
+ test->m_layerTreeHost->setNeedsAnimate();
+ test->m_layerTreeHost->setNeedsCommit();
+ }
+}
+
+void CCThreadedTest::dispatchSetNeedsCommit(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT_TRUE(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->setNeedsCommit();
+}
+
+void CCThreadedTest::dispatchAcquireLayerTextures(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT_TRUE(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->acquireLayerTextures();
+}
+
+void CCThreadedTest::dispatchSetNeedsRedraw(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT_TRUE(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->setNeedsRedraw();
+}
+
+void CCThreadedTest::dispatchSetVisible(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->setVisible(true);
+}
+
+void CCThreadedTest::dispatchSetInvisible(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->setVisible(false);
+}
+
+void CCThreadedTest::dispatchComposite(void* self)
+{
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(isMainThread());
+ ASSERT(test);
+ test->m_scheduled = false;
+ if (test->m_layerTreeHost && !test->m_finished)
+ test->m_layerTreeHost->composite();
+}
+
+void CCThreadedTest::dispatchDidAddAnimation(void* self)
+{
+ ASSERT(isMainThread());
+
+ CCThreadedTest* test = static_cast<CCThreadedTest*>(self);
+ ASSERT(test);
+ if (test->m_finished)
+ return;
+
+ if (test->m_layerTreeHost)
+ test->m_layerTreeHost->didAddAnimation();
+}
+
+void CCThreadedTest::runTest(bool threaded)
+{
+ // For these tests, we will enable threaded animations.
+ WebCompositor::setAcceleratedAnimationEnabled(true);
+
+ if (threaded) {
+ m_webThread = adoptPtr(WebKit::Platform::current()->createThread("CCThreadedTest"));
+ WebCompositor::initialize(m_webThread.get());
+ } else
+ WebCompositor::initialize(0);
+
+ ASSERT(CCProxy::isMainThread());
+ m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread());
+
+ m_beginTask = new BeginTask(this);
+ WebKit::Platform::current()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task
+ m_timeoutTask = new TimeoutTask(this);
+ WebKit::Platform::current()->currentThread()->postDelayedTask(m_timeoutTask, 5000);
+ WebKit::Platform::current()->currentThread()->enterRunLoop();
+
+ if (m_layerTreeHost && m_layerTreeHost->rootLayer())
+ m_layerTreeHost->rootLayer()->setLayerTreeHost(0);
+ m_layerTreeHost.clear();
+
+ if (m_timeoutTask)
+ m_timeoutTask->clearTest();
+
+ if (m_endTestTask)
+ m_endTestTask->clearTest();
+
+ ASSERT_FALSE(m_layerTreeHost.get());
+ m_client.clear();
+ if (m_timedOut) {
+ FAIL() << "Test timed out";
+ WebCompositor::shutdown();
+ return;
+ }
+ afterTest();
+ WebCompositor::shutdown();
+}
+
+} // namespace WebKitTests
diff --git a/cc/CCThreadedTest.h b/cc/CCThreadedTest.h
new file mode 100644
index 0000000..6971c42
--- /dev/null
+++ b/cc/CCThreadedTest.h
@@ -0,0 +1,207 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCThreadedTest_h
+#define CCThreadedTest_h
+
+#include "CCLayerTreeHost.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCScopedThreadProxy.h"
+#include "CompositorFakeWebGraphicsContext3D.h"
+#include <gtest/gtest.h>
+#include <public/WebAnimationDelegate.h>
+
+namespace WebCore {
+class CCLayerImpl;
+class CCLayerTreeHost;
+class CCLayerTreeHostClient;
+class CCLayerTreeHostImpl;
+class GraphicsContext3D;
+}
+
+namespace WebKit {
+class WebThread;
+}
+
+namespace WebKitTests {
+
+// Used by test stubs to notify the test when something interesting happens.
+class TestHooks : public WebKit::WebAnimationDelegate {
+public:
+ virtual void beginCommitOnCCThread(WebCore::CCLayerTreeHostImpl*) { }
+ virtual void commitCompleteOnCCThread(WebCore::CCLayerTreeHostImpl*) { }
+ virtual bool prepareToDrawOnCCThread(WebCore::CCLayerTreeHostImpl*) { return true; }
+ virtual void drawLayersOnCCThread(WebCore::CCLayerTreeHostImpl*) { }
+ virtual void animateLayers(WebCore::CCLayerTreeHostImpl*, double monotonicTime) { }
+ virtual void willAnimateLayers(WebCore::CCLayerTreeHostImpl*, double monotonicTime) { }
+ virtual void applyScrollAndScale(const WebCore::IntSize&, float) { }
+ virtual void animate(double monotonicTime) { }
+ virtual void layout() { }
+ virtual void didRecreateOutputSurface(bool succeeded) { }
+ virtual void didAddAnimation() { }
+ virtual void didCommit() { }
+ virtual void didCommitAndDrawFrame() { }
+ virtual void scheduleComposite() { }
+
+ // Implementation of WebAnimationDelegate
+ virtual void notifyAnimationStarted(double time) OVERRIDE { }
+ virtual void notifyAnimationFinished(double time) OVERRIDE { }
+
+ virtual PassOwnPtr<WebKit::WebCompositorOutputSurface> createOutputSurface();
+};
+
+class TimeoutTask;
+class BeginTask;
+class EndTestTask;
+
+class MockCCLayerTreeHostClient : public WebCore::CCLayerTreeHostClient {
+};
+
+// The CCThreadedTests runs with the main loop running. It instantiates a single MockLayerTreeHost and associated
+// MockLayerTreeHostImpl/MockLayerTreeHostClient.
+//
+// beginTest() is called once the main message loop is running and the layer tree host is initialized.
+//
+// Key stages of the drawing loop, e.g. drawing or commiting, redirect to CCThreadedTest methods of similar names.
+// To track the commit process, override these functions.
+//
+// The test continues until someone calls endTest. endTest can be called on any thread, but be aware that
+// ending the test is an asynchronous process.
+class CCThreadedTest : public testing::Test, public TestHooks {
+public:
+ virtual void afterTest() = 0;
+ virtual void beginTest() = 0;
+
+ void endTest();
+ void endTestAfterDelay(int delayMilliseconds);
+
+ void postSetNeedsAnimateToMainThread();
+ void postAddAnimationToMainThread();
+ void postAddInstantAnimationToMainThread();
+ void postSetNeedsCommitToMainThread();
+ void postAcquireLayerTextures();
+ void postSetNeedsRedrawToMainThread();
+ void postSetNeedsAnimateAndCommitToMainThread();
+ void postSetVisibleToMainThread(bool visible);
+ void postDidAddAnimationToMainThread();
+
+ void doBeginTest();
+ void timeout();
+
+ void clearTimeout() { m_timeoutTask = 0; }
+ void clearEndTestTask() { m_endTestTask = 0; }
+
+ WebCore::CCLayerTreeHost* layerTreeHost() { return m_layerTreeHost.get(); }
+
+protected:
+ CCThreadedTest();
+
+ virtual void scheduleComposite();
+
+ static void onEndTest(void* self);
+
+ static void dispatchSetNeedsAnimate(void* self);
+ static void dispatchAddInstantAnimation(void* self);
+ static void dispatchAddAnimation(void* self);
+ static void dispatchSetNeedsAnimateAndCommit(void* self);
+ static void dispatchSetNeedsCommit(void* self);
+ static void dispatchAcquireLayerTextures(void* self);
+ static void dispatchSetNeedsRedraw(void* self);
+ static void dispatchSetVisible(void* self);
+ static void dispatchSetInvisible(void* self);
+ static void dispatchComposite(void* self);
+ static void dispatchDidAddAnimation(void* self);
+
+ virtual void runTest(bool threaded);
+ WebKit::WebThread* webThread() const { return m_webThread.get(); }
+
+ WebCore::CCLayerTreeSettings m_settings;
+ OwnPtr<MockCCLayerTreeHostClient> m_client;
+ OwnPtr<WebCore::CCLayerTreeHost> m_layerTreeHost;
+
+private:
+ bool m_beginning;
+ bool m_endWhenBeginReturns;
+ bool m_timedOut;
+ bool m_finished;
+ bool m_scheduled;
+ bool m_started;
+
+ OwnPtr<WebKit::WebThread> m_webThread;
+ RefPtr<WebCore::CCScopedThreadProxy> m_mainThreadProxy;
+ TimeoutTask* m_timeoutTask;
+ BeginTask* m_beginTask;
+ EndTestTask* m_endTestTask;
+};
+
+class CCThreadedTestThreadOnly : public CCThreadedTest {
+public:
+ void runTestThreaded()
+ {
+ CCThreadedTest::runTest(true);
+ }
+};
+
+// Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks.
+class MockLayerTreeHostImpl : public WebCore::CCLayerTreeHostImpl {
+public:
+ static PassOwnPtr<MockLayerTreeHostImpl> create(TestHooks*, const WebCore::CCLayerTreeSettings&, WebCore::CCLayerTreeHostImplClient*);
+
+ virtual void beginCommit();
+ virtual void commitComplete();
+ virtual bool prepareToDraw(FrameData&);
+ virtual void drawLayers(const FrameData&);
+
+ // Make these public.
+ typedef Vector<WebCore::CCLayerImpl*> CCLayerList;
+ using CCLayerTreeHostImpl::calculateRenderSurfaceLayerList;
+
+protected:
+ virtual void animateLayers(double monotonicTime, double wallClockTime);
+ virtual double lowFrequencyAnimationInterval() const;
+
+private:
+ MockLayerTreeHostImpl(TestHooks*, const WebCore::CCLayerTreeSettings&, WebCore::CCLayerTreeHostImplClient*);
+
+ TestHooks* m_testHooks;
+};
+
+class CompositorFakeWebGraphicsContext3DWithTextureTracking : public WebKit::CompositorFakeWebGraphicsContext3D {
+public:
+ static PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> create(Attributes);
+
+ virtual WebKit::WebGLId createTexture();
+
+ virtual void deleteTexture(WebKit::WebGLId texture);
+
+ virtual void bindTexture(WebKit::WGC3Denum target, WebKit::WebGLId texture);
+
+ int numTextures() const;
+ int texture(int texture) const;
+ void resetTextures();
+
+ int numUsedTextures() const;
+ bool usedTexture(int texture) const;
+ void resetUsedTextures();
+
+private:
+ explicit CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs);
+
+ Vector<WebKit::WebGLId> m_textures;
+ HashSet<WebKit::WebGLId, DefaultHash<WebKit::WebGLId>::Hash, WTF::UnsignedWithZeroKeyHashTraits<WebKit::WebGLId> > m_usedTextures;
+};
+
+} // namespace WebKitTests
+
+#define SINGLE_AND_MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \
+ TEST_F(TEST_FIXTURE_NAME, runSingleThread) \
+ { \
+ runTest(false); \
+ } \
+ TEST_F(TEST_FIXTURE_NAME, runMultiThread) \
+ { \
+ runTest(true); \
+ }
+
+#endif // CCThreadedTest_h
diff --git a/cc/CCTileDrawQuad.cpp b/cc/CCTileDrawQuad.cpp
new file mode 100644
index 0000000..6d13788
--- /dev/null
+++ b/cc/CCTileDrawQuad.cpp
@@ -0,0 +1,39 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTileDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCTileDrawQuad> CCTileDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const IntRect& opaqueRect, unsigned resourceId, const IntPoint& textureOffset, const IntSize& textureSize, GC3Dint textureFilter, bool swizzleContents, bool leftEdgeAA, bool topEdgeAA, bool rightEdgeAA, bool bottomEdgeAA)
+{
+ return adoptPtr(new CCTileDrawQuad(sharedQuadState, quadRect, opaqueRect, resourceId, textureOffset, textureSize, textureFilter, swizzleContents, leftEdgeAA, topEdgeAA, rightEdgeAA, bottomEdgeAA));
+}
+
+CCTileDrawQuad::CCTileDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const IntRect& opaqueRect, unsigned resourceId, const IntPoint& textureOffset, const IntSize& textureSize, GC3Dint textureFilter, bool swizzleContents, bool leftEdgeAA, bool topEdgeAA, bool rightEdgeAA, bool bottomEdgeAA)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::TiledContent, quadRect)
+ , m_resourceId(resourceId)
+ , m_textureOffset(textureOffset)
+ , m_textureSize(textureSize)
+ , m_textureFilter(textureFilter)
+ , m_swizzleContents(swizzleContents)
+ , m_leftEdgeAA(leftEdgeAA)
+ , m_topEdgeAA(topEdgeAA)
+ , m_rightEdgeAA(rightEdgeAA)
+ , m_bottomEdgeAA(bottomEdgeAA)
+{
+ if (isAntialiased())
+ m_needsBlending = true;
+ m_opaqueRect = opaqueRect;
+}
+
+const CCTileDrawQuad* CCTileDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::TiledContent);
+ return static_cast<const CCTileDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCTileDrawQuad.h b/cc/CCTileDrawQuad.h
new file mode 100644
index 0000000..9a9969c
--- /dev/null
+++ b/cc/CCTileDrawQuad.h
@@ -0,0 +1,54 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTileDrawQuad_h
+#define CCTileDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "GraphicsTypes3D.h"
+#include "IntPoint.h"
+#include "IntSize.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+#pragma pack(push, 4)
+
+class CCTileDrawQuad : public CCDrawQuad {
+public:
+ static PassOwnPtr<CCTileDrawQuad> create(const CCSharedQuadState*, const IntRect& quadRect, const IntRect& opaqueRect, unsigned resourceId, const IntPoint& textureOffset, const IntSize& textureSize, GC3Dint textureFilter, bool swizzleContents, bool leftEdgeAA, bool topEdgeAA, bool rightEdgeAA, bool bottomEdgeAA);
+
+ unsigned resourceId() const { return m_resourceId; }
+ IntPoint textureOffset() const { return m_textureOffset; }
+ IntSize textureSize() const { return m_textureSize; }
+ GC3Dint textureFilter() const { return m_textureFilter; }
+ bool swizzleContents() const { return m_swizzleContents; }
+
+ bool leftEdgeAA() const { return m_leftEdgeAA; }
+ bool topEdgeAA() const { return m_topEdgeAA; }
+ bool rightEdgeAA() const { return m_rightEdgeAA; }
+ bool bottomEdgeAA() const { return m_bottomEdgeAA; }
+
+ bool isAntialiased() const { return leftEdgeAA() || topEdgeAA() || rightEdgeAA() || bottomEdgeAA(); }
+
+ static const CCTileDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCTileDrawQuad(const CCSharedQuadState*, const IntRect& quadRect, const IntRect& opaqueRect, unsigned resourceId, const IntPoint& textureOffset, const IntSize& textureSize, GC3Dint textureFilter, bool swizzleContents, bool leftEdgeAA, bool topEdgeAA, bool rightEdgeAA, bool bottomEdgeAA);
+
+ unsigned m_resourceId;
+ IntPoint m_textureOffset;
+ IntSize m_textureSize;
+ GC3Dint m_textureFilter;
+ bool m_swizzleContents;
+ bool m_leftEdgeAA;
+ bool m_topEdgeAA;
+ bool m_rightEdgeAA;
+ bool m_bottomEdgeAA;
+};
+
+#pragma pack(pop)
+
+}
+
+#endif
diff --git a/cc/CCTiledLayerImpl.cpp b/cc/CCTiledLayerImpl.cpp
new file mode 100644
index 0000000..a5d3cea
--- /dev/null
+++ b/cc/CCTiledLayerImpl.cpp
@@ -0,0 +1,219 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCTiledLayerImpl.h"
+
+#include "CCCheckerboardDrawQuad.h"
+#include "CCDebugBorderDrawQuad.h"
+#include "CCLayerTilingData.h"
+#include "CCMathUtil.h"
+#include "CCQuadSink.h"
+#include "CCSolidColorDrawQuad.h"
+#include "CCTileDrawQuad.h"
+#include "FloatQuad.h"
+#include "GraphicsContext3D.h"
+#include "SkColor.h"
+#include "TextStream.h"
+#include <wtf/text/WTFString.h>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+static const int debugTileBorderWidth = 1;
+static const int debugTileBorderAlpha = 100;
+static const int debugTileBorderColorRed = 80;
+static const int debugTileBorderColorGreen = 200;
+static const int debugTileBorderColorBlue = 200;
+static const int debugTileBorderMissingTileColorRed = 255;
+static const int debugTileBorderMissingTileColorGreen = 0;
+static const int debugTileBorderMissingTileColorBlue = 0;
+
+class DrawableTile : public CCLayerTilingData::Tile {
+ WTF_MAKE_NONCOPYABLE(DrawableTile);
+public:
+ static PassOwnPtr<DrawableTile> create() { return adoptPtr(new DrawableTile()); }
+
+ CCResourceProvider::ResourceId resourceId() const { return m_resourceId; }
+ void setResourceId(CCResourceProvider::ResourceId resourceId) { m_resourceId = resourceId; }
+
+private:
+ DrawableTile() : m_resourceId(0) { }
+
+ CCResourceProvider::ResourceId m_resourceId;
+};
+
+CCTiledLayerImpl::CCTiledLayerImpl(int id)
+ : CCLayerImpl(id)
+ , m_skipsDraw(true)
+ , m_contentsSwizzled(false)
+{
+}
+
+CCTiledLayerImpl::~CCTiledLayerImpl()
+{
+}
+
+CCResourceProvider::ResourceId CCTiledLayerImpl::contentsResourceId() const
+{
+ // This function is only valid for single texture layers, e.g. masks.
+ ASSERT(m_tiler);
+ ASSERT(m_tiler->numTilesX() == 1);
+ ASSERT(m_tiler->numTilesY() == 1);
+
+ DrawableTile* tile = tileAt(0, 0);
+ CCResourceProvider::ResourceId resourceId = tile ? tile->resourceId() : 0;
+ ASSERT(resourceId);
+
+ return resourceId;
+}
+
+void CCTiledLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ CCLayerImpl::dumpLayerProperties(ts, indent);
+ writeIndent(ts, indent);
+ ts << "skipsDraw: " << (!m_tiler || m_skipsDraw) << "\n";
+}
+
+bool CCTiledLayerImpl::hasTileAt(int i, int j) const
+{
+ return m_tiler->tileAt(i, j);
+}
+
+bool CCTiledLayerImpl::hasTextureIdForTileAt(int i, int j) const
+{
+ return hasTileAt(i, j) && tileAt(i, j)->resourceId();
+}
+
+DrawableTile* CCTiledLayerImpl::tileAt(int i, int j) const
+{
+ return static_cast<DrawableTile*>(m_tiler->tileAt(i, j));
+}
+
+DrawableTile* CCTiledLayerImpl::createTile(int i, int j)
+{
+ OwnPtr<DrawableTile> tile(DrawableTile::create());
+ DrawableTile* addedTile = tile.get();
+ m_tiler->addTile(tile.release(), i, j);
+ return addedTile;
+}
+
+void CCTiledLayerImpl::appendQuads(CCQuadSink& quadSink, bool& hadMissingTiles)
+{
+ const IntRect& contentRect = visibleContentRect();
+
+ if (!m_tiler || m_tiler->hasEmptyBounds() || contentRect.isEmpty())
+ return;
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+
+ if (hasDebugBorders()) {
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ DrawableTile* tile = tileAt(i, j);
+ IntRect tileRect = m_tiler->tileBounds(i, j);
+ SkColor borderColor;
+
+ if (m_skipsDraw || !tile || !tile->resourceId())
+ borderColor = SkColorSetARGB(debugTileBorderAlpha, debugTileBorderMissingTileColorRed, debugTileBorderMissingTileColorGreen, debugTileBorderMissingTileColorBlue);
+ else
+ borderColor = SkColorSetARGB(debugTileBorderAlpha, debugTileBorderColorRed, debugTileBorderColorGreen, debugTileBorderColorBlue);
+ quadSink.append(CCDebugBorderDrawQuad::create(sharedQuadState, tileRect, borderColor, debugTileBorderWidth));
+ }
+ }
+ }
+
+ if (m_skipsDraw)
+ return;
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ DrawableTile* tile = tileAt(i, j);
+ IntRect tileRect = m_tiler->tileBounds(i, j);
+ IntRect displayRect = tileRect;
+ tileRect.intersect(contentRect);
+
+ // Skip empty tiles.
+ if (tileRect.isEmpty())
+ continue;
+
+ if (!tile || !tile->resourceId()) {
+ if (drawCheckerboardForMissingTiles())
+ hadMissingTiles |= quadSink.append(CCCheckerboardDrawQuad::create(sharedQuadState, tileRect));
+ else
+ hadMissingTiles |= quadSink.append(CCSolidColorDrawQuad::create(sharedQuadState, tileRect, backgroundColor()));
+ continue;
+ }
+
+ IntRect tileOpaqueRect = tile->opaqueRect();
+ tileOpaqueRect.intersect(contentRect);
+
+ // Keep track of how the top left has moved, so the texture can be
+ // offset the same amount.
+ IntSize displayOffset = tileRect.minXMinYCorner() - displayRect.minXMinYCorner();
+ IntPoint textureOffset = m_tiler->textureOffset(i, j) + displayOffset;
+ float tileWidth = static_cast<float>(m_tiler->tileSize().width());
+ float tileHeight = static_cast<float>(m_tiler->tileSize().height());
+ IntSize textureSize(tileWidth, tileHeight);
+
+ bool clipped = false;
+ FloatQuad visibleContentInTargetQuad = CCMathUtil::mapQuad(drawTransform(), FloatQuad(visibleContentRect()), clipped);
+ bool isAxisAlignedInTarget = !clipped && visibleContentInTargetQuad.isRectilinear();
+ bool useAA = m_tiler->hasBorderTexels() && !isAxisAlignedInTarget;
+
+ bool leftEdgeAA = !i && useAA;
+ bool topEdgeAA = !j && useAA;
+ bool rightEdgeAA = i == m_tiler->numTilesX() - 1 && useAA;
+ bool bottomEdgeAA = j == m_tiler->numTilesY() - 1 && useAA;
+
+ const GC3Dint textureFilter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
+ quadSink.append(CCTileDrawQuad::create(sharedQuadState, tileRect, tileOpaqueRect, tile->resourceId(), textureOffset, textureSize, textureFilter, contentsSwizzled(), leftEdgeAA, topEdgeAA, rightEdgeAA, bottomEdgeAA));
+ }
+ }
+}
+
+void CCTiledLayerImpl::setTilingData(const CCLayerTilingData& tiler)
+{
+ if (m_tiler)
+ m_tiler->reset();
+ else
+ m_tiler = CCLayerTilingData::create(tiler.tileSize(), tiler.hasBorderTexels() ? CCLayerTilingData::HasBorderTexels : CCLayerTilingData::NoBorderTexels);
+ *m_tiler = tiler;
+}
+
+void CCTiledLayerImpl::pushTileProperties(int i, int j, CCResourceProvider::ResourceId resourceId, const IntRect& opaqueRect)
+{
+ DrawableTile* tile = tileAt(i, j);
+ if (!tile)
+ tile = createTile(i, j);
+ tile->setResourceId(resourceId);
+ tile->setOpaqueRect(opaqueRect);
+}
+
+Region CCTiledLayerImpl::visibleContentOpaqueRegion() const
+{
+ if (m_skipsDraw)
+ return Region();
+ if (opaque())
+ return visibleContentRect();
+ return m_tiler->opaqueRegionInContentRect(visibleContentRect());
+}
+
+void CCTiledLayerImpl::didLoseContext()
+{
+ m_tiler->reset();
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCTiledLayerImpl.h b/cc/CCTiledLayerImpl.h
new file mode 100644
index 0000000..8ddc54d
--- /dev/null
+++ b/cc/CCTiledLayerImpl.h
@@ -0,0 +1,61 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTiledLayerImpl_h
+#define CCTiledLayerImpl_h
+
+#include "CCLayerImpl.h"
+#include <public/WebTransformationMatrix.h>
+
+namespace WebCore {
+
+class CCLayerTilingData;
+class DrawableTile;
+
+class CCTiledLayerImpl : public CCLayerImpl {
+public:
+ static PassOwnPtr<CCTiledLayerImpl> create(int id)
+ {
+ return adoptPtr(new CCTiledLayerImpl(id));
+ }
+ virtual ~CCTiledLayerImpl();
+
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+
+ virtual CCResourceProvider::ResourceId contentsResourceId() const OVERRIDE;
+
+ virtual void dumpLayerProperties(TextStream&, int indent) const OVERRIDE;
+
+ void setSkipsDraw(bool skipsDraw) { m_skipsDraw = skipsDraw; }
+ void setTilingData(const CCLayerTilingData& tiler);
+ void pushTileProperties(int, int, CCResourceProvider::ResourceId, const IntRect& opaqueRect);
+
+ void setContentsSwizzled(bool contentsSwizzled) { m_contentsSwizzled = contentsSwizzled; }
+ bool contentsSwizzled() const { return m_contentsSwizzled; }
+
+ virtual Region visibleContentOpaqueRegion() const OVERRIDE;
+ virtual void didLoseContext() OVERRIDE;
+
+protected:
+ explicit CCTiledLayerImpl(int id);
+ // Exposed for testing.
+ bool hasTileAt(int, int) const;
+ bool hasTextureIdForTileAt(int, int) const;
+
+private:
+
+ virtual const char* layerTypeAsString() const OVERRIDE { return "ContentLayer"; }
+
+ DrawableTile* tileAt(int, int) const;
+ DrawableTile* createTile(int, int);
+
+ bool m_skipsDraw;
+ bool m_contentsSwizzled;
+
+ OwnPtr<CCLayerTilingData> m_tiler;
+};
+
+}
+
+#endif // CCTiledLayerImpl_h
diff --git a/cc/CCTiledLayerImplTest.cpp b/cc/CCTiledLayerImplTest.cpp
new file mode 100644
index 0000000..d67907a
--- /dev/null
+++ b/cc/CCTiledLayerImplTest.cpp
@@ -0,0 +1,243 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTiledLayerImpl.h"
+
+#include "CCLayerTestCommon.h"
+#include "CCLayerTilingData.h"
+#include "CCSingleThreadProxy.h"
+#include "CCTileDrawQuad.h"
+#include "MockCCQuadCuller.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace CCLayerTestCommon;
+
+namespace {
+
+// Create a default tiled layer with textures for all tiles and a default
+// visibility of the entire layer size.
+static PassOwnPtr<CCTiledLayerImpl> createLayer(const IntSize& tileSize, const IntSize& layerSize, CCLayerTilingData::BorderTexelOption borderTexels)
+{
+ OwnPtr<CCTiledLayerImpl> layer = CCTiledLayerImpl::create(1);
+ OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(tileSize, borderTexels);
+ tiler->setBounds(layerSize);
+ layer->setTilingData(*tiler);
+ layer->setSkipsDraw(false);
+ layer->setVisibleContentRect(IntRect(IntPoint(), layerSize));
+ layer->setDrawOpacity(1);
+ layer->setBounds(layerSize);
+ layer->setContentBounds(layerSize);
+ layer->createRenderSurface();
+ layer->setRenderTarget(layer.get());
+
+ CCResourceProvider::ResourceId resourceId = 1;
+ for (int i = 0; i < tiler->numTilesX(); ++i)
+ for (int j = 0; j < tiler->numTilesY(); ++j)
+ layer->pushTileProperties(i, j, resourceId++, IntRect(0, 0, 1, 1));
+
+ return layer.release();
+}
+
+TEST(CCTiledLayerImplTest, emptyQuadList)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ const IntSize tileSize(90, 90);
+ const int numTilesX = 8;
+ const int numTilesY = 4;
+ const IntSize layerSize(tileSize.width() * numTilesX, tileSize.height() * numTilesY);
+
+ // Verify default layer does creates quads
+ {
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels);
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ const unsigned numTiles = numTilesX * numTilesY;
+ EXPECT_EQ(quadCuller.quadList().size(), numTiles);
+ }
+
+ // Layer with empty visible layer rect produces no quads
+ {
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels);
+ layer->setVisibleContentRect(IntRect());
+
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ EXPECT_EQ(quadCuller.quadList().size(), 0u);
+ }
+
+ // Layer with non-intersecting visible layer rect produces no quads
+ {
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels);
+
+ IntRect outsideBounds(IntPoint(-100, -100), IntSize(50, 50));
+ layer->setVisibleContentRect(outsideBounds);
+
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ EXPECT_EQ(quadCuller.quadList().size(), 0u);
+ }
+
+ // Layer with skips draw produces no quads
+ {
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels);
+ layer->setSkipsDraw(true);
+
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ EXPECT_EQ(quadCuller.quadList().size(), 0u);
+ }
+}
+
+TEST(CCTiledLayerImplTest, checkerboarding)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ const IntSize tileSize(10, 10);
+ const int numTilesX = 2;
+ const int numTilesY = 2;
+ const IntSize layerSize(tileSize.width() * numTilesX, tileSize.height() * numTilesY);
+
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels);
+
+ // No checkerboarding
+ {
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ EXPECT_EQ(quadCuller.quadList().size(), 4u);
+ EXPECT_FALSE(hadMissingTiles);
+
+ for (size_t i = 0; i < quadCuller.quadList().size(); ++i)
+ EXPECT_EQ(quadCuller.quadList()[i]->material(), CCDrawQuad::TiledContent);
+ }
+
+ for (int i = 0; i < numTilesX; ++i)
+ for (int j = 0; j < numTilesY; ++j)
+ layer->pushTileProperties(i, j, 0, IntRect());
+
+ // All checkerboarding
+ {
+ MockCCQuadCuller quadCuller;
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+ EXPECT_TRUE(hadMissingTiles);
+ EXPECT_EQ(quadCuller.quadList().size(), 4u);
+ for (size_t i = 0; i < quadCuller.quadList().size(); ++i)
+ EXPECT_NE(quadCuller.quadList()[i]->material(), CCDrawQuad::TiledContent);
+ }
+}
+
+static void getQuads(CCQuadList& quads, CCSharedQuadStateList& sharedStates, IntSize tileSize, const IntSize& layerSize, CCLayerTilingData::BorderTexelOption borderTexelOption, const IntRect& visibleContentRect)
+{
+ OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, borderTexelOption);
+ layer->setVisibleContentRect(visibleContentRect);
+ layer->setBounds(layerSize);
+
+ MockCCQuadCuller quadCuller(quads, sharedStates);
+ bool hadMissingTiles = false;
+ layer->appendQuads(quadCuller, hadMissingTiles);
+}
+
+// Test with both border texels and without.
+#define WITH_AND_WITHOUT_BORDER_TEST(testFixtureName) \
+ TEST(CCTiledLayerImplTest, testFixtureName##NoBorders) \
+ { \
+ testFixtureName(CCLayerTilingData::NoBorderTexels); \
+ } \
+ TEST(CCTiledLayerImplTest, testFixtureName##HasBorders) \
+ { \
+ testFixtureName(CCLayerTilingData::HasBorderTexels);\
+ }
+
+static void coverageVisibleRectOnTileBoundaries(CCLayerTilingData::BorderTexelOption borders)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ IntSize layerSize(1000, 1000);
+ CCQuadList quads;
+ CCSharedQuadStateList sharedStates;
+ getQuads(quads, sharedStates, IntSize(100, 100), layerSize, borders, IntRect(IntPoint(), layerSize));
+ verifyQuadsExactlyCoverRect(quads, IntRect(IntPoint(), layerSize));
+}
+WITH_AND_WITHOUT_BORDER_TEST(coverageVisibleRectOnTileBoundaries);
+
+static void coverageVisibleRectIntersectsTiles(CCLayerTilingData::BorderTexelOption borders)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ // This rect intersects the middle 3x3 of the 5x5 tiles.
+ IntPoint topLeft(65, 73);
+ IntPoint bottomRight(182, 198);
+ IntRect visibleContentRect(topLeft, bottomRight - topLeft);
+
+ IntSize layerSize(250, 250);
+ CCQuadList quads;
+ CCSharedQuadStateList sharedStates;
+ getQuads(quads, sharedStates, IntSize(50, 50), IntSize(250, 250), CCLayerTilingData::NoBorderTexels, visibleContentRect);
+ verifyQuadsExactlyCoverRect(quads, visibleContentRect);
+}
+WITH_AND_WITHOUT_BORDER_TEST(coverageVisibleRectIntersectsTiles);
+
+static void coverageVisibleRectIntersectsBounds(CCLayerTilingData::BorderTexelOption borders)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ IntSize layerSize(220, 210);
+ IntRect visibleContentRect(IntPoint(), layerSize);
+ CCQuadList quads;
+ CCSharedQuadStateList sharedStates;
+ getQuads(quads, sharedStates, IntSize(100, 100), layerSize, CCLayerTilingData::NoBorderTexels, visibleContentRect);
+ verifyQuadsExactlyCoverRect(quads, visibleContentRect);
+}
+WITH_AND_WITHOUT_BORDER_TEST(coverageVisibleRectIntersectsBounds);
+
+TEST(CCTiledLayerImplTest, textureInfoForLayerNoBorders)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ IntSize tileSize(50, 50);
+ IntSize layerSize(250, 250);
+ CCQuadList quads;
+ CCSharedQuadStateList sharedStates;
+ getQuads(quads, sharedStates, tileSize, layerSize, CCLayerTilingData::NoBorderTexels, IntRect(IntPoint(), layerSize));
+
+ for (size_t i = 0; i < quads.size(); ++i) {
+ ASSERT_EQ(quads[i]->material(), CCDrawQuad::TiledContent) << quadString << i;
+ CCTileDrawQuad* quad = static_cast<CCTileDrawQuad*>(quads[i].get());
+
+ EXPECT_NE(quad->resourceId(), 0u) << quadString << i;
+ EXPECT_EQ(quad->textureOffset(), IntPoint()) << quadString << i;
+ EXPECT_EQ(quad->textureSize(), tileSize) << quadString << i;
+ EXPECT_EQ(IntRect(0, 0, 1, 1), quad->opaqueRect()) << quadString << i;
+ }
+}
+
+TEST(CCTiledLayerImplTest, tileOpaqueRectForLayerNoBorders)
+{
+ DebugScopedSetImplThread scopedImplThread;
+
+ IntSize tileSize(50, 50);
+ IntSize layerSize(250, 250);
+ CCQuadList quads;
+ CCSharedQuadStateList sharedStates;
+ getQuads(quads, sharedStates, tileSize, layerSize, CCLayerTilingData::NoBorderTexels, IntRect(IntPoint(), layerSize));
+
+ for (size_t i = 0; i < quads.size(); ++i) {
+ ASSERT_EQ(quads[i]->material(), CCDrawQuad::TiledContent) << quadString << i;
+ CCTileDrawQuad* quad = static_cast<CCTileDrawQuad*>(quads[i].get());
+
+ EXPECT_EQ(IntRect(0, 0, 1, 1), quad->opaqueRect()) << quadString << i;
+ }
+}
+
+} // namespace
diff --git a/cc/CCTimeSource.h b/cc/CCTimeSource.h
new file mode 100644
index 0000000..e74c508
--- /dev/null
+++ b/cc/CCTimeSource.h
@@ -0,0 +1,39 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTimeSource_h
+#define CCTimeSource_h
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class CCThread;
+
+class CCTimeSourceClient {
+public:
+ virtual void onTimerTick() = 0;
+
+protected:
+ virtual ~CCTimeSourceClient() { }
+};
+
+// An generic interface for getting a reliably-ticking timesource of
+// a specified rate.
+//
+// Be sure to call setActive(false) before releasing your reference to the
+// timer, or it will keep on ticking!
+class CCTimeSource : public RefCounted<CCTimeSource> {
+public:
+ virtual ~CCTimeSource() { }
+ virtual void setClient(CCTimeSourceClient*) = 0;
+ virtual void setActive(bool) = 0;
+ virtual bool active() const = 0;
+ virtual void setTimebaseAndInterval(double timebase, double intervalSeconds) = 0;
+ virtual double lastTickTime() = 0;
+ virtual double nextTickTimeIfActivated() = 0;
+};
+
+}
+#endif // CCSmoothedTimer_h
diff --git a/cc/CCTimer.cpp b/cc/CCTimer.cpp
new file mode 100644
index 0000000..52222b7
--- /dev/null
+++ b/cc/CCTimer.cpp
@@ -0,0 +1,79 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTimer.h"
+
+#include "CCThread.h"
+
+namespace WebCore {
+
+class CCTimerTask : public CCThread::Task {
+public:
+ explicit CCTimerTask(CCTimer* timer)
+ : CCThread::Task(0)
+ , m_timer(timer)
+ {
+ }
+
+ ~CCTimerTask()
+ {
+ if (!m_timer)
+ return;
+
+ ASSERT(m_timer->m_task == this);
+ m_timer->stop();
+ }
+
+ void performTask()
+ {
+ if (!m_timer)
+ return;
+
+ CCTimerClient* client = m_timer->m_client;
+
+ m_timer->stop();
+ if (client)
+ client->onTimerFired();
+ }
+
+private:
+ friend class CCTimer;
+
+ CCTimer* m_timer; // null if cancelled
+};
+
+CCTimer::CCTimer(CCThread* thread, CCTimerClient* client)
+ : m_client(client)
+ , m_thread(thread)
+ , m_task(0)
+{
+}
+
+CCTimer::~CCTimer()
+{
+ stop();
+}
+
+void CCTimer::startOneShot(double intervalSeconds)
+{
+ stop();
+
+ m_task = new CCTimerTask(this);
+
+ // The thread expects delays in milliseconds.
+ m_thread->postDelayedTask(adoptPtr(m_task), intervalSeconds * 1000.0);
+}
+
+void CCTimer::stop()
+{
+ if (!m_task)
+ return;
+
+ m_task->m_timer = 0;
+ m_task = 0;
+}
+
+} // namespace WebCore
diff --git a/cc/CCTimer.h b/cc/CCTimer.h
new file mode 100644
index 0000000..7cf3340
--- /dev/null
+++ b/cc/CCTimer.h
@@ -0,0 +1,42 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTimer_h
+#define CCTimer_h
+
+
+namespace WebCore {
+
+class CCThread;
+class CCTimerTask;
+
+class CCTimerClient {
+public:
+ virtual ~CCTimerClient() { }
+
+ virtual void onTimerFired() = 0;
+};
+
+class CCTimer {
+public:
+ CCTimer(CCThread*, CCTimerClient*);
+ ~CCTimer();
+
+ // If a previous task is pending, it will be replaced with the new one.
+ void startOneShot(double intervalSeconds);
+ void stop();
+
+ bool isActive() const { return m_task; }
+
+private:
+ friend class CCTimerTask;
+
+ CCTimerClient* m_client;
+ CCThread* m_thread;
+ CCTimerTask* m_task; // weak pointer
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/CCTimerTest.cpp b/cc/CCTimerTest.cpp
new file mode 100644
index 0000000..fe65d6e
--- /dev/null
+++ b/cc/CCTimerTest.cpp
@@ -0,0 +1,63 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTimer.h"
+
+#include "CCSchedulerTestCommon.h"
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace WebKitTests;
+
+namespace {
+
+class CCTimerTest : public testing::Test, public CCTimerClient {
+public:
+ CCTimerTest() : m_flag(false) { }
+
+ void onTimerFired() { m_flag = true; }
+
+protected:
+ FakeCCThread m_thread;
+ bool m_flag;
+};
+
+TEST_F(CCTimerTest, OneShot)
+{
+ CCTimer timer(&m_thread, this);
+ timer.startOneShot(0.001);
+ EXPECT_TRUE(timer.isActive());
+ m_thread.runPendingTask();
+ EXPECT_FALSE(timer.isActive());
+ EXPECT_TRUE(m_flag);
+ EXPECT_FALSE(m_thread.hasPendingTask());
+}
+
+TEST_F(CCTimerTest, StopManually)
+{
+ CCTimer timer(&m_thread, this);
+ timer.startOneShot(0.001);
+ EXPECT_TRUE(timer.isActive());
+ timer.stop();
+ EXPECT_FALSE(timer.isActive());
+
+ m_thread.runPendingTask();
+ EXPECT_FALSE(m_flag);
+ EXPECT_FALSE(m_thread.hasPendingTask());
+}
+
+TEST_F(CCTimerTest, StopByScope)
+{
+ {
+ CCTimer timer(&m_thread, this);
+ timer.startOneShot(0.001);
+ }
+
+ m_thread.runPendingTask();
+ EXPECT_FALSE(m_flag);
+}
+
+}
diff --git a/cc/CCTimingFunction.cpp b/cc/CCTimingFunction.cpp
new file mode 100644
index 0000000..6446948
--- /dev/null
+++ b/cc/CCTimingFunction.cpp
@@ -0,0 +1,76 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTimingFunction.h"
+
+#include <wtf/OwnPtr.h>
+
+namespace {
+const double epsilon = 1e-6;
+} // namespace
+
+namespace WebCore {
+
+CCTimingFunction::CCTimingFunction()
+{
+}
+
+CCTimingFunction::~CCTimingFunction()
+{
+}
+
+double CCTimingFunction::duration() const
+{
+ return 1.0;
+}
+
+PassOwnPtr<CCCubicBezierTimingFunction> CCCubicBezierTimingFunction::create(double x1, double y1, double x2, double y2)
+{
+ return adoptPtr(new CCCubicBezierTimingFunction(x1, y1, x2, y2));
+}
+
+CCCubicBezierTimingFunction::CCCubicBezierTimingFunction(double x1, double y1, double x2, double y2)
+ : m_curve(x1, y1, x2, y2)
+{
+}
+
+CCCubicBezierTimingFunction::~CCCubicBezierTimingFunction()
+{
+}
+
+float CCCubicBezierTimingFunction::getValue(double x) const
+{
+ UnitBezier temp(m_curve);
+ return static_cast<float>(temp.solve(x, epsilon));
+}
+
+PassOwnPtr<CCAnimationCurve> CCCubicBezierTimingFunction::clone() const
+{
+ return adoptPtr(new CCCubicBezierTimingFunction(*this));
+}
+
+// These numbers come from http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag.
+PassOwnPtr<CCTimingFunction> CCEaseTimingFunction::create()
+{
+ return CCCubicBezierTimingFunction::create(0.25, 0.1, 0.25, 1);
+}
+
+PassOwnPtr<CCTimingFunction> CCEaseInTimingFunction::create()
+{
+ return CCCubicBezierTimingFunction::create(0.42, 0, 1.0, 1);
+}
+
+PassOwnPtr<CCTimingFunction> CCEaseOutTimingFunction::create()
+{
+ return CCCubicBezierTimingFunction::create(0, 0, 0.58, 1);
+}
+
+PassOwnPtr<CCTimingFunction> CCEaseInOutTimingFunction::create()
+{
+ return CCCubicBezierTimingFunction::create(0.42, 0, 0.58, 1);
+}
+
+} // namespace WebCore
diff --git a/cc/CCTimingFunction.h b/cc/CCTimingFunction.h
new file mode 100644
index 0000000..30bba72
--- /dev/null
+++ b/cc/CCTimingFunction.h
@@ -0,0 +1,64 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTimingFunction_h
+#define CCTimingFunction_h
+
+#include "CCAnimationCurve.h"
+#include "UnitBezier.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+// See http://www.w3.org/TR/css3-transitions/.
+class CCTimingFunction : public CCFloatAnimationCurve {
+public:
+ virtual ~CCTimingFunction();
+
+ // Partial implementation of CCFloatAnimationCurve.
+ virtual double duration() const OVERRIDE;
+
+protected:
+ CCTimingFunction();
+};
+
+class CCCubicBezierTimingFunction : public CCTimingFunction {
+public:
+ static PassOwnPtr<CCCubicBezierTimingFunction> create(double x1, double y1, double x2, double y2);
+ virtual ~CCCubicBezierTimingFunction();
+
+ // Partial implementation of CCFloatAnimationCurve.
+ virtual float getValue(double time) const OVERRIDE;
+ virtual PassOwnPtr<CCAnimationCurve> clone() const OVERRIDE;
+
+protected:
+ CCCubicBezierTimingFunction(double x1, double y1, double x2, double y2);
+
+ UnitBezier m_curve;
+};
+
+class CCEaseTimingFunction {
+public:
+ static PassOwnPtr<CCTimingFunction> create();
+};
+
+class CCEaseInTimingFunction {
+public:
+ static PassOwnPtr<CCTimingFunction> create();
+};
+
+class CCEaseOutTimingFunction {
+public:
+ static PassOwnPtr<CCTimingFunction> create();
+};
+
+class CCEaseInOutTimingFunction {
+public:
+ static PassOwnPtr<CCTimingFunction> create();
+};
+
+} // namespace WebCore
+
+#endif // CCTimingFunction_h
+
diff --git a/cc/CCVideoLayerImpl.cpp b/cc/CCVideoLayerImpl.cpp
new file mode 100644
index 0000000..3643c1b
--- /dev/null
+++ b/cc/CCVideoLayerImpl.cpp
@@ -0,0 +1,388 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCVideoLayerImpl.h"
+
+#include "CCIOSurfaceDrawQuad.h"
+#include "CCLayerTreeHostImpl.h"
+#include "CCProxy.h"
+#include "CCQuadSink.h"
+#include "CCResourceProvider.h"
+#include "CCStreamVideoDrawQuad.h"
+#include "CCTextureDrawQuad.h"
+#include "CCYUVVideoDrawQuad.h"
+#include "Extensions3DChromium.h"
+#include "GraphicsContext3D.h"
+#include "NotImplemented.h"
+#include "TextStream.h"
+#include <public/WebVideoFrame.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+CCVideoLayerImpl::CCVideoLayerImpl(int id, WebKit::WebVideoFrameProvider* provider)
+ : CCLayerImpl(id)
+ , m_provider(provider)
+ , m_frame(0)
+ , m_externalTextureResource(0)
+{
+ // This matrix is the default transformation for stream textures, and flips on the Y axis.
+ m_streamTextureMatrix = WebKit::WebTransformationMatrix(
+ 1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 1, 0, 1);
+
+ // This only happens during a commit on the compositor thread while the main
+ // thread is blocked. That makes this a thread-safe call to set the video
+ // frame provider client that does not require a lock. The same is true of
+ // the call in the destructor.
+ ASSERT(CCProxy::isMainThreadBlocked());
+ m_provider->setVideoFrameProviderClient(this);
+}
+
+CCVideoLayerImpl::~CCVideoLayerImpl()
+{
+ // See comment in constructor for why this doesn't need a lock.
+ ASSERT(CCProxy::isMainThreadBlocked());
+ if (m_provider) {
+ m_provider->setVideoFrameProviderClient(0);
+ m_provider = 0;
+ }
+ freePlaneData(layerTreeHostImpl()->resourceProvider());
+
+#if !ASSERT_DISABLED
+ for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ ASSERT(!m_framePlanes[i].resourceId);
+ ASSERT(!m_externalTextureResource);
+#endif
+}
+
+void CCVideoLayerImpl::stopUsingProvider()
+{
+ // Block the provider from shutting down until this client is done
+ // using the frame.
+ MutexLocker locker(m_providerMutex);
+ ASSERT(!m_frame);
+ m_provider = 0;
+}
+
+// Convert WebKit::WebVideoFrame::Format to GraphicsContext3D's format enum values.
+static GC3Denum convertVFCFormatToGC3DFormat(const WebKit::WebVideoFrame& frame)
+{
+ switch (frame.format()) {
+ case WebKit::WebVideoFrame::FormatYV12:
+ case WebKit::WebVideoFrame::FormatYV16:
+ return GraphicsContext3D::LUMINANCE;
+ case WebKit::WebVideoFrame::FormatNativeTexture:
+ return frame.textureTarget();
+ case WebKit::WebVideoFrame::FormatInvalid:
+ case WebKit::WebVideoFrame::FormatRGB32:
+ case WebKit::WebVideoFrame::FormatEmpty:
+ case WebKit::WebVideoFrame::FormatI420:
+ notImplemented();
+ }
+ return GraphicsContext3D::INVALID_VALUE;
+}
+
+void CCVideoLayerImpl::willDraw(CCResourceProvider* resourceProvider)
+{
+ ASSERT(CCProxy::isImplThread());
+ CCLayerImpl::willDraw(resourceProvider);
+
+ // Explicitly lock and unlock the provider mutex so it can be held from
+ // willDraw to didDraw. Since the compositor thread is in the middle of
+ // drawing, the layer will not be destroyed before didDraw is called.
+ // Therefore, the only thing that will prevent this lock from being released
+ // is the GPU process locking it. As the GPU process can't cause the
+ // destruction of the provider (calling stopUsingProvider), holding this
+ // lock should not cause a deadlock.
+ m_providerMutex.lock();
+
+ willDrawInternal(resourceProvider);
+ freeUnusedPlaneData(resourceProvider);
+
+ if (!m_frame)
+ m_providerMutex.unlock();
+}
+
+void CCVideoLayerImpl::willDrawInternal(CCResourceProvider* resourceProvider)
+{
+ ASSERT(CCProxy::isImplThread());
+ ASSERT(!m_externalTextureResource);
+
+ if (!m_provider) {
+ m_frame = 0;
+ return;
+ }
+
+ m_frame = m_provider->getCurrentFrame();
+
+ if (!m_frame)
+ return;
+
+ m_format = convertVFCFormatToGC3DFormat(*m_frame);
+
+ if (m_format == GraphicsContext3D::INVALID_VALUE) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (m_frame->planes() > WebKit::WebVideoFrame::maxPlanes) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (!allocatePlaneData(resourceProvider)) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (!copyPlaneData(resourceProvider)) {
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+ return;
+ }
+
+ if (m_format == GraphicsContext3D::TEXTURE_2D)
+ m_externalTextureResource = resourceProvider->createResourceFromExternalTexture(m_frame->textureId());
+}
+
+void CCVideoLayerImpl::appendQuads(CCQuadSink& quadSink, bool&)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ if (!m_frame)
+ return;
+
+ CCSharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
+ appendDebugBorderQuad(quadSink, sharedQuadState);
+
+ // FIXME: When we pass quads out of process, we need to double-buffer, or
+ // otherwise synchonize use of all textures in the quad.
+
+ IntRect quadRect(IntPoint(), contentBounds());
+
+ switch (m_format) {
+ case GraphicsContext3D::LUMINANCE: {
+ // YUV software decoder.
+ const FramePlane& yPlane = m_framePlanes[WebKit::WebVideoFrame::yPlane];
+ const FramePlane& uPlane = m_framePlanes[WebKit::WebVideoFrame::uPlane];
+ const FramePlane& vPlane = m_framePlanes[WebKit::WebVideoFrame::vPlane];
+ OwnPtr<CCYUVVideoDrawQuad> yuvVideoQuad = CCYUVVideoDrawQuad::create(sharedQuadState, quadRect, yPlane, uPlane, vPlane);
+ quadSink.append(yuvVideoQuad.release());
+ break;
+ }
+ case GraphicsContext3D::RGBA: {
+ // RGBA software decoder.
+ const FramePlane& plane = m_framePlanes[WebKit::WebVideoFrame::rgbPlane];
+ float widthScaleFactor = static_cast<float>(plane.visibleSize.width()) / plane.size.width();
+
+ bool premultipliedAlpha = true;
+ FloatRect uvRect(0, 0, widthScaleFactor, 1);
+ bool flipped = false;
+ OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, plane.resourceId, premultipliedAlpha, uvRect, flipped);
+ quadSink.append(textureQuad.release());
+ break;
+ }
+ case GraphicsContext3D::TEXTURE_2D: {
+ // NativeTexture hardware decoder.
+ bool premultipliedAlpha = true;
+ FloatRect uvRect(0, 0, 1, 1);
+ bool flipped = false;
+ OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, m_externalTextureResource, premultipliedAlpha, uvRect, flipped);
+ quadSink.append(textureQuad.release());
+ break;
+ }
+ case Extensions3D::TEXTURE_RECTANGLE_ARB: {
+ IntSize textureSize(m_frame->width(), m_frame->height());
+ OwnPtr<CCIOSurfaceDrawQuad> ioSurfaceQuad = CCIOSurfaceDrawQuad::create(sharedQuadState, quadRect, textureSize, m_frame->textureId(), CCIOSurfaceDrawQuad::Unflipped);
+ quadSink.append(ioSurfaceQuad.release());
+ break;
+ }
+ case Extensions3DChromium::GL_TEXTURE_EXTERNAL_OES: {
+ // StreamTexture hardware decoder.
+ OwnPtr<CCStreamVideoDrawQuad> streamVideoQuad = CCStreamVideoDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), m_streamTextureMatrix);
+ quadSink.append(streamVideoQuad.release());
+ break;
+ }
+ default:
+ CRASH(); // Someone updated convertVFCFormatToGC3DFormat above but update this!
+ }
+}
+
+void CCVideoLayerImpl::didDraw(CCResourceProvider* resourceProvider)
+{
+ ASSERT(CCProxy::isImplThread());
+ CCLayerImpl::didDraw(resourceProvider);
+
+ if (!m_frame)
+ return;
+
+ if (m_format == GraphicsContext3D::TEXTURE_2D) {
+ ASSERT(m_externalTextureResource);
+ // FIXME: the following assert will not be true when sending resources to a
+ // parent compositor. We will probably need to hold on to m_frame for
+ // longer, and have several "current frames" in the pipeline.
+ ASSERT(!resourceProvider->inUseByConsumer(m_externalTextureResource));
+ resourceProvider->deleteResource(m_externalTextureResource);
+ m_externalTextureResource = 0;
+ }
+
+ m_provider->putCurrentFrame(m_frame);
+ m_frame = 0;
+
+ m_providerMutex.unlock();
+}
+
+static int videoFrameDimension(int originalDimension, unsigned plane, int format)
+{
+ if (format == WebKit::WebVideoFrame::FormatYV12 && plane != WebKit::WebVideoFrame::yPlane)
+ return originalDimension / 2;
+ return originalDimension;
+}
+
+static bool hasPaddingBytes(const WebKit::WebVideoFrame& frame, unsigned plane)
+{
+ return frame.stride(plane) > videoFrameDimension(frame.width(), plane, frame.format());
+}
+
+IntSize CCVideoLayerImpl::computeVisibleSize(const WebKit::WebVideoFrame& frame, unsigned plane)
+{
+ int visibleWidth = videoFrameDimension(frame.width(), plane, frame.format());
+ int originalWidth = visibleWidth;
+ int visibleHeight = videoFrameDimension(frame.height(), plane, frame.format());
+
+ // When there are dead pixels at the edge of the texture, decrease
+ // the frame width by 1 to prevent the rightmost pixels from
+ // interpolating with the dead pixels.
+ if (hasPaddingBytes(frame, plane))
+ --visibleWidth;
+
+ // In YV12, every 2x2 square of Y values corresponds to one U and
+ // one V value. If we decrease the width of the UV plane, we must decrease the
+ // width of the Y texture by 2 for proper alignment. This must happen
+ // always, even if Y's texture does not have padding bytes.
+ if (plane == WebKit::WebVideoFrame::yPlane && frame.format() == WebKit::WebVideoFrame::FormatYV12) {
+ if (hasPaddingBytes(frame, WebKit::WebVideoFrame::uPlane))
+ visibleWidth = originalWidth - 2;
+ }
+
+ return IntSize(visibleWidth, visibleHeight);
+}
+
+bool CCVideoLayerImpl::FramePlane::allocateData(CCResourceProvider* resourceProvider)
+{
+ if (resourceId)
+ return true;
+
+ resourceId = resourceProvider->createResource(CCRenderer::ImplPool, size, format, CCResourceProvider::TextureUsageAny);
+ return resourceId;
+}
+
+void CCVideoLayerImpl::FramePlane::freeData(CCResourceProvider* resourceProvider)
+{
+ if (!resourceId)
+ return;
+
+ resourceProvider->deleteResource(resourceId);
+ resourceId = 0;
+}
+
+bool CCVideoLayerImpl::allocatePlaneData(CCResourceProvider* resourceProvider)
+{
+ int maxTextureSize = resourceProvider->maxTextureSize();
+ for (unsigned planeIndex = 0; planeIndex < m_frame->planes(); ++planeIndex) {
+ CCVideoLayerImpl::FramePlane& plane = m_framePlanes[planeIndex];
+
+ IntSize requiredTextureSize(m_frame->stride(planeIndex), videoFrameDimension(m_frame->height(), planeIndex, m_frame->format()));
+ // FIXME: Remove the test against maxTextureSize when tiled layers are implemented.
+ if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize)
+ return false;
+
+ if (plane.size != requiredTextureSize || plane.format != m_format) {
+ plane.freeData(resourceProvider);
+ plane.size = requiredTextureSize;
+ plane.format = m_format;
+ }
+
+ if (!plane.resourceId) {
+ if (!plane.allocateData(resourceProvider))
+ return false;
+ plane.visibleSize = computeVisibleSize(*m_frame, planeIndex);
+ }
+ }
+ return true;
+}
+
+bool CCVideoLayerImpl::copyPlaneData(CCResourceProvider* resourceProvider)
+{
+ size_t softwarePlaneCount = m_frame->planes();
+ if (!softwarePlaneCount)
+ return true;
+
+ for (size_t softwarePlaneIndex = 0; softwarePlaneIndex < softwarePlaneCount; ++softwarePlaneIndex) {
+ CCVideoLayerImpl::FramePlane& plane = m_framePlanes[softwarePlaneIndex];
+ const uint8_t* softwarePlanePixels = static_cast<const uint8_t*>(m_frame->data(softwarePlaneIndex));
+ IntRect planeRect(IntPoint(), plane.size);
+ resourceProvider->upload(plane.resourceId, softwarePlanePixels, planeRect, planeRect, IntSize());
+ }
+ return true;
+}
+
+void CCVideoLayerImpl::freePlaneData(CCResourceProvider* resourceProvider)
+{
+ for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ m_framePlanes[i].freeData(resourceProvider);
+}
+
+void CCVideoLayerImpl::freeUnusedPlaneData(CCResourceProvider* resourceProvider)
+{
+ unsigned firstUnusedPlane = m_frame ? m_frame->planes() : 0;
+ for (unsigned i = firstUnusedPlane; i < WebKit::WebVideoFrame::maxPlanes; ++i)
+ m_framePlanes[i].freeData(resourceProvider);
+}
+
+void CCVideoLayerImpl::didReceiveFrame()
+{
+ setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::didUpdateMatrix(const float matrix[16])
+{
+ m_streamTextureMatrix = WebKit::WebTransformationMatrix(
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]);
+ setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::didLoseContext()
+{
+ freePlaneData(layerTreeHostImpl()->resourceProvider());
+}
+
+void CCVideoLayerImpl::setNeedsRedraw()
+{
+ layerTreeHostImpl()->setNeedsRedraw();
+}
+
+void CCVideoLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
+{
+ writeIndent(ts, indent);
+ ts << "video layer\n";
+ CCLayerImpl::dumpLayerProperties(ts, indent);
+}
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CCVideoLayerImpl.h b/cc/CCVideoLayerImpl.h
new file mode 100644
index 0000000..17fc030
--- /dev/null
+++ b/cc/CCVideoLayerImpl.h
@@ -0,0 +1,89 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCVideoLayerImpl_h
+#define CCVideoLayerImpl_h
+
+#include "CCLayerImpl.h"
+#include "GraphicsContext3D.h"
+#include "IntSize.h"
+#include <public/WebTransformationMatrix.h>
+#include <public/WebVideoFrameProvider.h>
+
+namespace WebKit {
+class WebVideoFrame;
+}
+
+namespace WebCore {
+
+class CCLayerTreeHostImpl;
+class CCVideoLayerImpl;
+
+class CCVideoLayerImpl : public CCLayerImpl
+ , public WebKit::WebVideoFrameProvider::Client {
+public:
+ static PassOwnPtr<CCVideoLayerImpl> create(int id, WebKit::WebVideoFrameProvider* provider)
+ {
+ return adoptPtr(new CCVideoLayerImpl(id, provider));
+ }
+ virtual ~CCVideoLayerImpl();
+
+ virtual void willDraw(CCResourceProvider*) OVERRIDE;
+ virtual void appendQuads(CCQuadSink&, bool& hadMissingTiles) OVERRIDE;
+ virtual void didDraw(CCResourceProvider*) OVERRIDE;
+
+ virtual void dumpLayerProperties(TextStream&, int indent) const OVERRIDE;
+
+ Mutex& providerMutex() { return m_providerMutex; }
+
+ // WebKit::WebVideoFrameProvider::Client implementation.
+ virtual void stopUsingProvider(); // Callable on any thread.
+ virtual void didReceiveFrame(); // Callable on impl thread.
+ virtual void didUpdateMatrix(const float*); // Callable on impl thread.
+
+ virtual void didLoseContext() OVERRIDE;
+
+ void setNeedsRedraw();
+
+ struct FramePlane {
+ CCResourceProvider::ResourceId resourceId;
+ IntSize size;
+ GC3Denum format;
+ IntSize visibleSize;
+
+ FramePlane() : resourceId(0) { }
+
+ bool allocateData(CCResourceProvider*);
+ void freeData(CCResourceProvider*);
+ };
+
+private:
+ CCVideoLayerImpl(int, WebKit::WebVideoFrameProvider*);
+
+ static IntSize computeVisibleSize(const WebKit::WebVideoFrame&, unsigned plane);
+ virtual const char* layerTypeAsString() const OVERRIDE { return "VideoLayer"; }
+
+ void willDrawInternal(CCResourceProvider*);
+ bool allocatePlaneData(CCResourceProvider*);
+ bool copyPlaneData(CCResourceProvider*);
+ void freePlaneData(CCResourceProvider*);
+ void freeUnusedPlaneData(CCResourceProvider*);
+
+ // Guards the destruction of m_provider and the frame that it provides
+ Mutex m_providerMutex;
+ WebKit::WebVideoFrameProvider* m_provider;
+
+ WebKit::WebTransformationMatrix m_streamTextureMatrix;
+
+ WebKit::WebVideoFrame* m_frame;
+ GC3Denum m_format;
+ CCResourceProvider::ResourceId m_externalTextureResource;
+
+ // Each index in this array corresponds to a plane in WebKit::WebVideoFrame.
+ FramePlane m_framePlanes[WebKit::WebVideoFrame::maxPlanes];
+};
+
+}
+
+#endif // CCVideoLayerImpl_h
diff --git a/cc/CCYUVVideoDrawQuad.cpp b/cc/CCYUVVideoDrawQuad.cpp
new file mode 100644
index 0000000..66e0fc1
--- /dev/null
+++ b/cc/CCYUVVideoDrawQuad.cpp
@@ -0,0 +1,30 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCYUVVideoDrawQuad.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCYUVVideoDrawQuad> CCYUVVideoDrawQuad::create(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const CCVideoLayerImpl::FramePlane& yPlane, const CCVideoLayerImpl::FramePlane& uPlane, const CCVideoLayerImpl::FramePlane& vPlane)
+{
+ return adoptPtr(new CCYUVVideoDrawQuad(sharedQuadState, quadRect, yPlane, uPlane, vPlane));
+}
+
+CCYUVVideoDrawQuad::CCYUVVideoDrawQuad(const CCSharedQuadState* sharedQuadState, const IntRect& quadRect, const CCVideoLayerImpl::FramePlane& yPlane, const CCVideoLayerImpl::FramePlane& uPlane, const CCVideoLayerImpl::FramePlane& vPlane)
+ : CCDrawQuad(sharedQuadState, CCDrawQuad::YUVVideoContent, quadRect)
+ , m_yPlane(yPlane)
+ , m_uPlane(uPlane)
+ , m_vPlane(vPlane)
+{
+}
+
+const CCYUVVideoDrawQuad* CCYUVVideoDrawQuad::materialCast(const CCDrawQuad* quad)
+{
+ ASSERT(quad->material() == CCDrawQuad::YUVVideoContent);
+ return static_cast<const CCYUVVideoDrawQuad*>(quad);
+}
+
+}
diff --git a/cc/CCYUVVideoDrawQuad.h b/cc/CCYUVVideoDrawQuad.h
new file mode 100644
index 0000000..240922f
--- /dev/null
+++ b/cc/CCYUVVideoDrawQuad.h
@@ -0,0 +1,34 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCYUVVideoDrawQuad_h
+#define CCYUVVideoDrawQuad_h
+
+#include "CCDrawQuad.h"
+#include "CCVideoLayerImpl.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCYUVVideoDrawQuad : public CCDrawQuad {
+ WTF_MAKE_NONCOPYABLE(CCYUVVideoDrawQuad);
+public:
+ static PassOwnPtr<CCYUVVideoDrawQuad> create(const CCSharedQuadState*, const IntRect&, const CCVideoLayerImpl::FramePlane& yPlane, const CCVideoLayerImpl::FramePlane& uPlane, const CCVideoLayerImpl::FramePlane& vPlane);
+
+ const CCVideoLayerImpl::FramePlane& yPlane() const { return m_yPlane; }
+ const CCVideoLayerImpl::FramePlane& uPlane() const { return m_uPlane; }
+ const CCVideoLayerImpl::FramePlane& vPlane() const { return m_vPlane; }
+
+ static const CCYUVVideoDrawQuad* materialCast(const CCDrawQuad*);
+private:
+ CCYUVVideoDrawQuad(const CCSharedQuadState*, const IntRect&, const CCVideoLayerImpl::FramePlane& yPlane, const CCVideoLayerImpl::FramePlane& uPlane, const CCVideoLayerImpl::FramePlane& vPlane);
+
+ CCVideoLayerImpl::FramePlane m_yPlane;
+ CCVideoLayerImpl::FramePlane m_uPlane;
+ CCVideoLayerImpl::FramePlane m_vPlane;
+};
+
+}
+
+#endif
diff --git a/cc/CanvasLayerTextureUpdater.cpp b/cc/CanvasLayerTextureUpdater.cpp
new file mode 100644
index 0000000..dd63b97
--- /dev/null
+++ b/cc/CanvasLayerTextureUpdater.cpp
@@ -0,0 +1,70 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CanvasLayerTextureUpdater.h"
+
+#include "CCRenderingStats.h"
+#include "FloatRect.h"
+#include "LayerPainterChromium.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRect.h"
+#include "SkiaUtils.h"
+#include "TraceEvent.h"
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+CanvasLayerTextureUpdater::CanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium> painter)
+ : m_painter(painter)
+{
+}
+
+CanvasLayerTextureUpdater::~CanvasLayerTextureUpdater()
+{
+}
+
+void CanvasLayerTextureUpdater::paintContents(SkCanvas* canvas, const IntRect& contentRect, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats& stats)
+{
+ TRACE_EVENT0("cc", "CanvasLayerTextureUpdater::paintContents");
+ canvas->save();
+ canvas->translate(WebCoreFloatToSkScalar(-contentRect.x()), WebCoreFloatToSkScalar(-contentRect.y()));
+
+ IntRect layerRect = contentRect;
+
+ if (contentsWidthScale != 1 || contentsHeightScale != 1) {
+ canvas->scale(WebCoreFloatToSkScalar(contentsWidthScale), WebCoreFloatToSkScalar(contentsHeightScale));
+
+ FloatRect rect = contentRect;
+ rect.scale(1 / contentsWidthScale, 1 / contentsHeightScale);
+ layerRect = enclosingIntRect(rect);
+ }
+
+ SkPaint paint;
+ paint.setAntiAlias(false);
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ SkRect layerSkRect = SkRect::MakeXYWH(layerRect.x(), layerRect.y(), layerRect.width(), layerRect.height());
+ canvas->drawRect(layerSkRect, paint);
+ canvas->clipRect(layerSkRect);
+
+ FloatRect opaqueLayerRect;
+ double paintBeginTime = monotonicallyIncreasingTime();
+ m_painter->paint(canvas, layerRect, opaqueLayerRect);
+ stats.totalPaintTimeInSeconds += monotonicallyIncreasingTime() - paintBeginTime;
+ canvas->restore();
+
+ FloatRect opaqueContentRect = opaqueLayerRect;
+ opaqueContentRect.scale(contentsWidthScale, contentsHeightScale);
+ resultingOpaqueRect = enclosedIntRect(opaqueContentRect);
+
+ m_contentRect = contentRect;
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/CanvasLayerTextureUpdater.h b/cc/CanvasLayerTextureUpdater.h
new file mode 100644
index 0000000..051f9fc
--- /dev/null
+++ b/cc/CanvasLayerTextureUpdater.h
@@ -0,0 +1,39 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef CanvasLayerTextureUpdater_h
+#define CanvasLayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerTextureUpdater.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+class LayerPainterChromium;
+
+// Base class for BitmapCanvasLayerTextureUpdater and
+// SkPictureCanvasLayerTextureUpdater that reduces code duplication between
+// their respective paintContents implementations.
+class CanvasLayerTextureUpdater : public LayerTextureUpdater {
+public:
+ virtual ~CanvasLayerTextureUpdater();
+
+protected:
+ explicit CanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
+
+ void paintContents(SkCanvas*, const IntRect& contentRect, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats&);
+ const IntRect& contentRect() const { return m_contentRect; }
+
+private:
+ IntRect m_contentRect;
+ OwnPtr<LayerPainterChromium> m_painter;
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // CanvasLayerTextureUpdater_h
diff --git a/cc/ContentLayerChromium.cpp b/cc/ContentLayerChromium.cpp
new file mode 100644
index 0000000..7465ed4
--- /dev/null
+++ b/cc/ContentLayerChromium.cpp
@@ -0,0 +1,107 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ContentLayerChromium.h"
+
+#include "BitmapCanvasLayerTextureUpdater.h"
+#include "BitmapSkPictureCanvasLayerTextureUpdater.h"
+#include "CCLayerTreeHost.h"
+#include "CCSettings.h"
+#include "FrameBufferSkPictureCanvasLayerTextureUpdater.h"
+#include "LayerPainterChromium.h"
+#include <public/Platform.h>
+#include <wtf/CurrentTime.h>
+
+namespace WebCore {
+
+ContentLayerPainter::ContentLayerPainter(ContentLayerDelegate* delegate)
+ : m_delegate(delegate)
+{
+}
+
+PassOwnPtr<ContentLayerPainter> ContentLayerPainter::create(ContentLayerDelegate* delegate)
+{
+ return adoptPtr(new ContentLayerPainter(delegate));
+}
+
+void ContentLayerPainter::paint(SkCanvas* canvas, const IntRect& contentRect, FloatRect& opaque)
+{
+ double paintStart = currentTime();
+ m_delegate->paintContents(canvas, contentRect, opaque);
+ double paintEnd = currentTime();
+ double pixelsPerSec = (contentRect.width() * contentRect.height()) / (paintEnd - paintStart);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.AccelContentPaintDurationMS", (paintEnd - paintStart) * 1000, 0, 120, 30);
+ WebKit::Platform::current()->histogramCustomCounts("Renderer4.AccelContentPaintMegapixPerSecond", pixelsPerSec / 1000000, 10, 210, 30);
+}
+
+PassRefPtr<ContentLayerChromium> ContentLayerChromium::create(ContentLayerDelegate* delegate)
+{
+ return adoptRef(new ContentLayerChromium(delegate));
+}
+
+ContentLayerChromium::ContentLayerChromium(ContentLayerDelegate* delegate)
+ : TiledLayerChromium()
+ , m_delegate(delegate)
+{
+}
+
+ContentLayerChromium::~ContentLayerChromium()
+{
+}
+
+bool ContentLayerChromium::drawsContent() const
+{
+ return TiledLayerChromium::drawsContent() && m_delegate;
+}
+
+void ContentLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
+{
+ // Update the tile data before creating all the layer's tiles.
+ updateTileSizeAndTilingOption();
+
+ TiledLayerChromium::setTexturePriorities(priorityCalc);
+}
+
+void ContentLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+ createTextureUpdaterIfNeeded();
+ TiledLayerChromium::update(queue, occlusion, stats);
+ m_needsDisplay = false;
+}
+
+bool ContentLayerChromium::needMoreUpdates()
+{
+ return needsIdlePaint();
+}
+
+void ContentLayerChromium::createTextureUpdaterIfNeeded()
+{
+ if (m_textureUpdater)
+ return;
+ if (layerTreeHost()->settings().acceleratePainting)
+ m_textureUpdater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(ContentLayerPainter::create(m_delegate));
+ else if (CCSettings::perTilePaintingEnabled())
+ m_textureUpdater = BitmapSkPictureCanvasLayerTextureUpdater::create(ContentLayerPainter::create(m_delegate));
+ else
+ m_textureUpdater = BitmapCanvasLayerTextureUpdater::create(ContentLayerPainter::create(m_delegate));
+ m_textureUpdater->setOpaque(opaque());
+
+ GC3Denum textureFormat = layerTreeHost()->rendererCapabilities().bestTextureFormat;
+ setTextureFormat(textureFormat);
+ setSampledTexelFormat(textureUpdater()->sampledTexelFormat(textureFormat));
+}
+
+void ContentLayerChromium::setOpaque(bool opaque)
+{
+ LayerChromium::setOpaque(opaque);
+ if (m_textureUpdater)
+ m_textureUpdater->setOpaque(opaque);
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/ContentLayerChromium.h b/cc/ContentLayerChromium.h
new file mode 100644
index 0000000..fa68d11
--- /dev/null
+++ b/cc/ContentLayerChromium.h
@@ -0,0 +1,74 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef ContentLayerChromium_h
+#define ContentLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerPainterChromium.h"
+#include "TiledLayerChromium.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+class FloatRect;
+class IntRect;
+class LayerTextureUpdater;
+
+class ContentLayerDelegate {
+public:
+ virtual void paintContents(SkCanvas*, const IntRect& clip, FloatRect& opaque) = 0;
+
+protected:
+ virtual ~ContentLayerDelegate() { }
+};
+
+class ContentLayerPainter : public LayerPainterChromium {
+ WTF_MAKE_NONCOPYABLE(ContentLayerPainter);
+public:
+ static PassOwnPtr<ContentLayerPainter> create(ContentLayerDelegate*);
+
+ virtual void paint(SkCanvas*, const IntRect& contentRect, FloatRect& opaque) OVERRIDE;
+
+private:
+ explicit ContentLayerPainter(ContentLayerDelegate*);
+
+ ContentLayerDelegate* m_delegate;
+};
+
+// A layer that renders its contents into an SkCanvas.
+class ContentLayerChromium : public TiledLayerChromium {
+public:
+ static PassRefPtr<ContentLayerChromium> create(ContentLayerDelegate*);
+
+ virtual ~ContentLayerChromium();
+
+ void clearDelegate() { m_delegate = 0; }
+
+ virtual bool drawsContent() const OVERRIDE;
+ virtual void setTexturePriorities(const CCPriorityCalculator&) OVERRIDE;
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual bool needMoreUpdates() OVERRIDE;
+
+ virtual void setOpaque(bool) OVERRIDE;
+
+protected:
+ explicit ContentLayerChromium(ContentLayerDelegate*);
+
+
+private:
+ virtual LayerTextureUpdater* textureUpdater() const OVERRIDE { return m_textureUpdater.get(); }
+ virtual void createTextureUpdaterIfNeeded() OVERRIDE;
+
+ ContentLayerDelegate* m_delegate;
+ RefPtr<LayerTextureUpdater> m_textureUpdater;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/DEPS b/cc/DEPS
new file mode 100644
index 0000000..b2e3c85
--- /dev/null
+++ b/cc/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+third_party/khronos/GLES2/gl2.h",
+ "+third_party/khronos/GLES2/gl2ext.h",
+ "+third_party/WebKit/Source/WebCore/platform/graphics/Region.h",
+ "+third_party/WebKit/Source/WebCore/platform/graphics/gpu/TilingData.h",
+ "+third_party/WebKit/Source/WebCore/platform/graphics/UnitBezier.h",
+]
diff --git a/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.cpp b/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.cpp
new file mode 100644
index 0000000..c3b5dbb
--- /dev/null
+++ b/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.cpp
@@ -0,0 +1,114 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FrameBufferSkPictureCanvasLayerTextureUpdater.h"
+
+#include "CCProxy.h"
+#include "LayerPainterChromium.h"
+#include "SkCanvas.h"
+#include "SkGpuDevice.h"
+#include <public/WebGraphicsContext3D.h>
+#include <public/WebSharedGraphicsContext3D.h>
+
+using WebKit::WebGraphicsContext3D;
+using WebKit::WebSharedGraphicsContext3D;
+
+namespace WebCore {
+
+static PassOwnPtr<SkCanvas> createAcceleratedCanvas(GrContext* grContext,
+ IntSize canvasSize,
+ unsigned textureId)
+{
+ GrPlatformTextureDesc textureDesc;
+ textureDesc.fFlags = kRenderTarget_GrPlatformTextureFlag;
+ textureDesc.fWidth = canvasSize.width();
+ textureDesc.fHeight = canvasSize.height();
+ textureDesc.fConfig = kSkia8888_PM_GrPixelConfig;
+ textureDesc.fTextureHandle = textureId;
+ SkAutoTUnref<GrTexture> target(grContext->createPlatformTexture(textureDesc));
+ SkAutoTUnref<SkDevice> device(new SkGpuDevice(grContext, target.get()));
+ return adoptPtr(new SkCanvas(device.get()));
+}
+
+FrameBufferSkPictureCanvasLayerTextureUpdater::Texture::Texture(FrameBufferSkPictureCanvasLayerTextureUpdater* textureUpdater, PassOwnPtr<CCPrioritizedTexture> texture)
+ : LayerTextureUpdater::Texture(texture)
+ , m_textureUpdater(textureUpdater)
+{
+}
+
+FrameBufferSkPictureCanvasLayerTextureUpdater::Texture::~Texture()
+{
+}
+
+void FrameBufferSkPictureCanvasLayerTextureUpdater::Texture::updateRect(CCResourceProvider* resourceProvider, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ WebGraphicsContext3D* sharedContext = CCProxy::hasImplThread() ? WebSharedGraphicsContext3D::compositorThreadContext() : WebSharedGraphicsContext3D::mainThreadContext();
+ GrContext* sharedGrContext = CCProxy::hasImplThread() ? WebSharedGraphicsContext3D::compositorThreadGrContext() : WebSharedGraphicsContext3D::mainThreadGrContext();
+ if (!sharedContext || !sharedGrContext)
+ return;
+ textureUpdater()->updateTextureRect(sharedContext, sharedGrContext, resourceProvider, texture(), sourceRect, destOffset);
+}
+
+PassRefPtr<FrameBufferSkPictureCanvasLayerTextureUpdater> FrameBufferSkPictureCanvasLayerTextureUpdater::create(PassOwnPtr<LayerPainterChromium> painter)
+{
+ return adoptRef(new FrameBufferSkPictureCanvasLayerTextureUpdater(painter));
+}
+
+FrameBufferSkPictureCanvasLayerTextureUpdater::FrameBufferSkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium> painter)
+ : SkPictureCanvasLayerTextureUpdater(painter)
+{
+}
+
+FrameBufferSkPictureCanvasLayerTextureUpdater::~FrameBufferSkPictureCanvasLayerTextureUpdater()
+{
+}
+
+PassOwnPtr<LayerTextureUpdater::Texture> FrameBufferSkPictureCanvasLayerTextureUpdater::createTexture(CCPrioritizedTextureManager* manager)
+{
+ return adoptPtr(new Texture(this, CCPrioritizedTexture::create(manager)));
+}
+
+LayerTextureUpdater::SampledTexelFormat FrameBufferSkPictureCanvasLayerTextureUpdater::sampledTexelFormat(GC3Denum textureFormat)
+{
+ // Here we directly render to the texture, so the component order is always correct.
+ return LayerTextureUpdater::SampledTexelFormatRGBA;
+}
+
+void FrameBufferSkPictureCanvasLayerTextureUpdater::updateTextureRect(WebGraphicsContext3D* context, GrContext* grContext, CCResourceProvider* resourceProvider, CCPrioritizedTexture* texture, const IntRect& sourceRect, const IntSize& destOffset)
+{
+ // Make sure ganesh uses the correct GL context.
+ context->makeContextCurrent();
+
+ texture->acquireBackingTexture(resourceProvider);
+ CCResourceProvider::ScopedWriteLockGL lock(resourceProvider, texture->resourceId());
+ // Create an accelerated canvas to draw on.
+ OwnPtr<SkCanvas> canvas = createAcceleratedCanvas(grContext, texture->size(), lock.textureId());
+
+ // The compositor expects the textures to be upside-down so it can flip
+ // the final composited image. Ganesh renders the image upright so we
+ // need to do a y-flip.
+ canvas->translate(0.0, texture->size().height());
+ canvas->scale(1.0, -1.0);
+ // Clip to the destination on the texture that must be updated.
+ canvas->clipRect(SkRect::MakeXYWH(destOffset.width(), destOffset.height(), sourceRect.width(), sourceRect.height()));
+ // Translate the origin of contentRect to that of destRect.
+ // Note that destRect is defined relative to sourceRect.
+ canvas->translate(contentRect().x() - sourceRect.x() + destOffset.width(),
+ contentRect().y() - sourceRect.y() + destOffset.height());
+ drawPicture(canvas.get());
+
+ // Flush ganesh context so that all the rendered stuff appears on the texture.
+ grContext->flush();
+
+ // Flush the GL context so rendering results from this context are visible in the compositor's context.
+ context->flush();
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.h b/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.h
new file mode 100644
index 0000000..5a9452f
--- /dev/null
+++ b/cc/FrameBufferSkPictureCanvasLayerTextureUpdater.h
@@ -0,0 +1,52 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef FrameBufferSkPictureCanvasLayerTextureUpdater_h
+#define FrameBufferSkPictureCanvasLayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "SkPictureCanvasLayerTextureUpdater.h"
+
+class GrContext;
+
+namespace WebKit {
+class WebGraphicsContext3D;
+class WebSharedGraphicsContext3D;
+}
+
+namespace WebCore {
+
+// This class records the contentRect into an SkPicture, then uses accelerated
+// drawing to update the texture. The accelerated drawing goes to an
+// intermediate framebuffer and then is copied to the destination texture once done.
+class FrameBufferSkPictureCanvasLayerTextureUpdater : public SkPictureCanvasLayerTextureUpdater {
+public:
+ class Texture : public LayerTextureUpdater::Texture {
+ public:
+ Texture(FrameBufferSkPictureCanvasLayerTextureUpdater*, PassOwnPtr<CCPrioritizedTexture>);
+ virtual ~Texture();
+
+ virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) OVERRIDE;
+
+ private:
+ FrameBufferSkPictureCanvasLayerTextureUpdater* textureUpdater() { return m_textureUpdater; }
+
+ FrameBufferSkPictureCanvasLayerTextureUpdater* m_textureUpdater;
+ };
+
+ static PassRefPtr<FrameBufferSkPictureCanvasLayerTextureUpdater> create(PassOwnPtr<LayerPainterChromium>);
+ virtual ~FrameBufferSkPictureCanvasLayerTextureUpdater();
+
+ virtual PassOwnPtr<LayerTextureUpdater::Texture> createTexture(CCPrioritizedTextureManager*) OVERRIDE;
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum textureFormat) OVERRIDE;
+ void updateTextureRect(WebKit::WebGraphicsContext3D*, GrContext*, CCResourceProvider*, CCPrioritizedTexture*, const IntRect& sourceRect, const IntSize& destOffset);
+
+private:
+ explicit FrameBufferSkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
+};
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // FrameBufferSkPictureCanvasLayerTextureUpdater_h
diff --git a/cc/GeometryBinding.cpp b/cc/GeometryBinding.cpp
new file mode 100644
index 0000000..65ee243
--- /dev/null
+++ b/cc/GeometryBinding.cpp
@@ -0,0 +1,61 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GeometryBinding.h"
+
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "GraphicsContext3D.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+GeometryBinding::GeometryBinding(WebKit::WebGraphicsContext3D* context, const FloatRect& quadVertexRect)
+ : m_context(context)
+ , m_quadVerticesVbo(0)
+ , m_quadElementsVbo(0)
+ , m_initialized(false)
+{
+ // Vertex positions and texture coordinates for the 4 corners of a 1x1 quad.
+ float vertices[] = { quadVertexRect.x(), quadVertexRect.maxY(), 0.0f, 0.0f, 1.0f,
+ quadVertexRect.x(), quadVertexRect.y(), 0.0f, 0.0f, 0.0f,
+ quadVertexRect.maxX(), quadVertexRect.y(), 0.0f, 1.0f, 0.0f,
+ quadVertexRect.maxX(), quadVertexRect.maxY(), 0.0f, 1.0f, 1.0f };
+ uint16_t indices[] = { 0, 1, 2, 0, 2, 3, // The two triangles that make up the layer quad.
+ 0, 1, 2, 3}; // A line path for drawing the layer border.
+
+ GLC(m_context, m_quadVerticesVbo = m_context->createBuffer());
+ GLC(m_context, m_quadElementsVbo = m_context->createBuffer());
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_quadVerticesVbo));
+ GLC(m_context, m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(vertices), vertices, GraphicsContext3D::STATIC_DRAW));
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, m_quadElementsVbo));
+ GLC(m_context, m_context->bufferData(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GraphicsContext3D::STATIC_DRAW));
+
+ m_initialized = true;
+}
+
+GeometryBinding::~GeometryBinding()
+{
+ GLC(m_context, m_context->deleteBuffer(m_quadVerticesVbo));
+ GLC(m_context, m_context->deleteBuffer(m_quadElementsVbo));
+}
+
+void GeometryBinding::prepareForDraw()
+{
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, quadVerticesVbo()));
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, quadElementsVbo()));
+ unsigned offset = 0;
+ GLC(m_context, m_context->vertexAttribPointer(positionAttribLocation(), 3, GraphicsContext3D::FLOAT, false, 5 * sizeof(float), offset));
+ offset += 3 * sizeof(float);
+ GLC(m_context, m_context->vertexAttribPointer(texCoordAttribLocation(), 2, GraphicsContext3D::FLOAT, false, 5 * sizeof(float), offset));
+ GLC(m_context, m_context->enableVertexAttribArray(positionAttribLocation()));
+ GLC(m_context, m_context->enableVertexAttribArray(texCoordAttribLocation()));
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/GeometryBinding.h b/cc/GeometryBinding.h
new file mode 100644
index 0000000..c018ff7
--- /dev/null
+++ b/cc/GeometryBinding.h
@@ -0,0 +1,48 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GeometryBinding_h
+#define GeometryBinding_h
+
+#include "FloatRect.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class GeometryBinding {
+public:
+ GeometryBinding(WebKit::WebGraphicsContext3D*, const FloatRect& quadVertexRect);
+ ~GeometryBinding();
+
+ bool initialized() const { return m_initialized; }
+
+ WebKit::WebGraphicsContext3D* context() const { return m_context; }
+ unsigned quadVerticesVbo() const { return m_quadVerticesVbo; }
+ unsigned quadElementsVbo() const { return m_quadElementsVbo; }
+
+ void prepareForDraw();
+
+ // All layer shaders share the same attribute locations for the vertex
+ // positions and texture coordinates. This allows switching shaders without
+ // rebinding attribute arrays.
+ static int positionAttribLocation() { return 0; }
+ static int texCoordAttribLocation() { return 1; }
+
+private:
+ WebKit::WebGraphicsContext3D* m_context;
+ unsigned m_quadVerticesVbo;
+ unsigned m_quadElementsVbo;
+ bool m_initialized;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/HeadsUpDisplayLayerChromium.cpp b/cc/HeadsUpDisplayLayerChromium.cpp
new file mode 100644
index 0000000..9a782cd
--- /dev/null
+++ b/cc/HeadsUpDisplayLayerChromium.cpp
@@ -0,0 +1,70 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "HeadsUpDisplayLayerChromium.h"
+
+#include "CCHeadsUpDisplayLayerImpl.h"
+#include "CCLayerTreeHost.h"
+#include "TraceEvent.h"
+
+namespace WebCore {
+
+PassRefPtr<HeadsUpDisplayLayerChromium> HeadsUpDisplayLayerChromium::create()
+{
+ return adoptRef(new HeadsUpDisplayLayerChromium());
+}
+
+HeadsUpDisplayLayerChromium::HeadsUpDisplayLayerChromium()
+ : LayerChromium()
+{
+
+ setBounds(IntSize(512, 128));
+}
+
+HeadsUpDisplayLayerChromium::~HeadsUpDisplayLayerChromium()
+{
+}
+
+void HeadsUpDisplayLayerChromium::update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&)
+{
+ const CCLayerTreeSettings& settings = layerTreeHost()->settings();
+ int maxTextureSize = layerTreeHost()->rendererCapabilities().maxTextureSize;
+
+ IntSize bounds;
+ if (settings.showPlatformLayerTree || settings.showDebugRects()) {
+ bounds.setWidth(std::min(maxTextureSize, layerTreeHost()->deviceViewportSize().width()));
+ bounds.setHeight(std::min(maxTextureSize, layerTreeHost()->deviceViewportSize().height()));
+ } else {
+ bounds.setWidth(512);
+ bounds.setHeight(128);
+ }
+
+ setBounds(bounds);
+}
+
+void HeadsUpDisplayLayerChromium::setFontAtlas(PassOwnPtr<CCFontAtlas> fontAtlas)
+{
+ m_fontAtlas = fontAtlas;
+ setNeedsCommit();
+}
+
+PassOwnPtr<CCLayerImpl> HeadsUpDisplayLayerChromium::createCCLayerImpl()
+{
+ return CCHeadsUpDisplayLayerImpl::create(m_layerId);
+}
+
+void HeadsUpDisplayLayerChromium::pushPropertiesTo(CCLayerImpl* layerImpl)
+{
+ LayerChromium::pushPropertiesTo(layerImpl);
+
+ if (!m_fontAtlas)
+ return;
+
+ CCHeadsUpDisplayLayerImpl* hudLayerImpl = static_cast<CCHeadsUpDisplayLayerImpl*>(layerImpl);
+ hudLayerImpl->setFontAtlas(m_fontAtlas.release());
+}
+
+}
diff --git a/cc/HeadsUpDisplayLayerChromium.h b/cc/HeadsUpDisplayLayerChromium.h
new file mode 100644
index 0000000..2f81574
--- /dev/null
+++ b/cc/HeadsUpDisplayLayerChromium.h
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef HeadsUpDisplayLayerChromium_h
+#define HeadsUpDisplayLayerChromium_h
+
+#include "CCFontAtlas.h"
+#include "IntSize.h"
+#include "LayerChromium.h"
+
+namespace WebCore {
+
+class HeadsUpDisplayLayerChromium : public LayerChromium {
+public:
+ static PassRefPtr<HeadsUpDisplayLayerChromium> create();
+ virtual ~HeadsUpDisplayLayerChromium();
+
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual bool drawsContent() const OVERRIDE { return true; }
+
+ void setFontAtlas(PassOwnPtr<CCFontAtlas>);
+
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+
+protected:
+ HeadsUpDisplayLayerChromium();
+
+private:
+ OwnPtr<CCFontAtlas> m_fontAtlas;
+};
+
+}
+#endif
diff --git a/cc/IOSurfaceLayerChromium.cpp b/cc/IOSurfaceLayerChromium.cpp
new file mode 100644
index 0000000..fde7140
--- /dev/null
+++ b/cc/IOSurfaceLayerChromium.cpp
@@ -0,0 +1,56 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "IOSurfaceLayerChromium.h"
+
+#include "CCIOSurfaceLayerImpl.h"
+
+namespace WebCore {
+
+PassRefPtr<IOSurfaceLayerChromium> IOSurfaceLayerChromium::create()
+{
+ return adoptRef(new IOSurfaceLayerChromium());
+}
+
+IOSurfaceLayerChromium::IOSurfaceLayerChromium()
+ : LayerChromium()
+ , m_ioSurfaceId(0)
+{
+}
+
+IOSurfaceLayerChromium::~IOSurfaceLayerChromium()
+{
+}
+
+void IOSurfaceLayerChromium::setIOSurfaceProperties(uint32_t ioSurfaceId, const IntSize& size)
+{
+ m_ioSurfaceId = ioSurfaceId;
+ m_ioSurfaceSize = size;
+ setNeedsCommit();
+}
+
+PassOwnPtr<CCLayerImpl> IOSurfaceLayerChromium::createCCLayerImpl()
+{
+ return CCIOSurfaceLayerImpl::create(m_layerId);
+}
+
+bool IOSurfaceLayerChromium::drawsContent() const
+{
+ return m_ioSurfaceId && LayerChromium::drawsContent();
+}
+
+void IOSurfaceLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
+{
+ LayerChromium::pushPropertiesTo(layer);
+
+ CCIOSurfaceLayerImpl* textureLayer = static_cast<CCIOSurfaceLayerImpl*>(layer);
+ textureLayer->setIOSurfaceProperties(m_ioSurfaceId, m_ioSurfaceSize);
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/IOSurfaceLayerChromium.h b/cc/IOSurfaceLayerChromium.h
new file mode 100644
index 0000000..47247f8
--- /dev/null
+++ b/cc/IOSurfaceLayerChromium.h
@@ -0,0 +1,38 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef IOSurfaceLayerChromium_h
+#define IOSurfaceLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerChromium.h"
+
+namespace WebCore {
+
+class IOSurfaceLayerChromium : public LayerChromium {
+public:
+ static PassRefPtr<IOSurfaceLayerChromium> create();
+ virtual ~IOSurfaceLayerChromium();
+
+ void setIOSurfaceProperties(uint32_t ioSurfaceId, const IntSize&);
+
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+ virtual bool drawsContent() const OVERRIDE;
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+
+protected:
+ IOSurfaceLayerChromium();
+
+private:
+
+ uint32_t m_ioSurfaceId;
+ IntSize m_ioSurfaceSize;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/ImageLayerChromium.cpp b/cc/ImageLayerChromium.cpp
new file mode 100644
index 0000000..09c97d8
--- /dev/null
+++ b/cc/ImageLayerChromium.cpp
@@ -0,0 +1,161 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ImageLayerChromium.h"
+
+#include "CCLayerTreeHost.h"
+#include "LayerTextureUpdater.h"
+#include "PlatformColor.h"
+
+namespace WebCore {
+
+class ImageLayerTextureUpdater : public LayerTextureUpdater {
+public:
+ class Texture : public LayerTextureUpdater::Texture {
+ public:
+ Texture(ImageLayerTextureUpdater* textureUpdater, PassOwnPtr<CCPrioritizedTexture> texture)
+ : LayerTextureUpdater::Texture(texture)
+ , m_textureUpdater(textureUpdater)
+ {
+ }
+
+ virtual void updateRect(CCResourceProvider* resourceProvider, const IntRect& sourceRect, const IntSize& destOffset) OVERRIDE
+ {
+ textureUpdater()->updateTextureRect(resourceProvider, texture(), sourceRect, destOffset);
+ }
+
+ private:
+ ImageLayerTextureUpdater* textureUpdater() { return m_textureUpdater; }
+
+ ImageLayerTextureUpdater* m_textureUpdater;
+ };
+
+ static PassRefPtr<ImageLayerTextureUpdater> create()
+ {
+ return adoptRef(new ImageLayerTextureUpdater());
+ }
+
+ virtual ~ImageLayerTextureUpdater() { }
+
+ virtual PassOwnPtr<LayerTextureUpdater::Texture> createTexture(CCPrioritizedTextureManager* manager)
+ {
+ return adoptPtr(new Texture(this, CCPrioritizedTexture::create(manager)));
+ }
+
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum textureFormat) OVERRIDE
+ {
+ return PlatformColor::sameComponentOrder(textureFormat) ?
+ LayerTextureUpdater::SampledTexelFormatRGBA : LayerTextureUpdater::SampledTexelFormatBGRA;
+ }
+
+ void updateTextureRect(CCResourceProvider* resourceProvider, CCPrioritizedTexture* texture, const IntRect& sourceRect, const IntSize& destOffset)
+ {
+ // Source rect should never go outside the image pixels, even if this
+ // is requested because the texture extends outside the image.
+ IntRect clippedSourceRect = sourceRect;
+ IntRect imageRect = IntRect(0, 0, m_bitmap.width(), m_bitmap.height());
+ clippedSourceRect.intersect(imageRect);
+
+ IntSize clippedDestOffset = destOffset + IntSize(clippedSourceRect.location() - sourceRect.location());
+
+ SkAutoLockPixels lock(m_bitmap);
+ texture->upload(resourceProvider, static_cast<const uint8_t*>(m_bitmap.getPixels()), imageRect, clippedSourceRect, clippedDestOffset);
+ }
+
+ void setBitmap(const SkBitmap& bitmap)
+ {
+ m_bitmap = bitmap;
+ }
+
+private:
+ ImageLayerTextureUpdater() { }
+
+ SkBitmap m_bitmap;
+};
+
+PassRefPtr<ImageLayerChromium> ImageLayerChromium::create()
+{
+ return adoptRef(new ImageLayerChromium());
+}
+
+ImageLayerChromium::ImageLayerChromium()
+ : TiledLayerChromium()
+{
+}
+
+ImageLayerChromium::~ImageLayerChromium()
+{
+}
+
+void ImageLayerChromium::setBitmap(const SkBitmap& bitmap)
+{
+ // setBitmap() currently gets called whenever there is any
+ // style change that affects the layer even if that change doesn't
+ // affect the actual contents of the image (e.g. a CSS animation).
+ // With this check in place we avoid unecessary texture uploads.
+ if (bitmap.pixelRef() && bitmap.pixelRef() == m_bitmap.pixelRef())
+ return;
+
+ m_bitmap = bitmap;
+ setNeedsDisplay();
+}
+
+void ImageLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
+{
+ // Update the tile data before creating all the layer's tiles.
+ updateTileSizeAndTilingOption();
+
+ TiledLayerChromium::setTexturePriorities(priorityCalc);
+}
+
+void ImageLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+ createTextureUpdaterIfNeeded();
+ if (m_needsDisplay) {
+ m_textureUpdater->setBitmap(m_bitmap);
+ updateTileSizeAndTilingOption();
+ invalidateContentRect(IntRect(IntPoint(), contentBounds()));
+ m_needsDisplay = false;
+ }
+ TiledLayerChromium::update(queue, occlusion, stats);
+}
+
+void ImageLayerChromium::createTextureUpdaterIfNeeded()
+{
+ if (m_textureUpdater)
+ return;
+
+ m_textureUpdater = ImageLayerTextureUpdater::create();
+ GC3Denum textureFormat = layerTreeHost()->rendererCapabilities().bestTextureFormat;
+ setTextureFormat(textureFormat);
+ setSampledTexelFormat(textureUpdater()->sampledTexelFormat(textureFormat));
+}
+
+LayerTextureUpdater* ImageLayerChromium::textureUpdater() const
+{
+ return m_textureUpdater.get();
+}
+
+IntSize ImageLayerChromium::contentBounds() const
+{
+ return IntSize(m_bitmap.width(), m_bitmap.height());
+}
+
+bool ImageLayerChromium::drawsContent() const
+{
+ return !m_bitmap.isNull() && TiledLayerChromium::drawsContent();
+}
+
+bool ImageLayerChromium::needsContentsScale() const
+{
+ // Contents scale is not need for image layer because this can be done in compositor more efficiently.
+ return false;
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/ImageLayerChromium.h b/cc/ImageLayerChromium.h
new file mode 100644
index 0000000..8be33a7
--- /dev/null
+++ b/cc/ImageLayerChromium.h
@@ -0,0 +1,48 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef ImageLayerChromium_h
+#define ImageLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ContentLayerChromium.h"
+#include "SkBitmap.h"
+
+namespace WebCore {
+
+class ImageLayerTextureUpdater;
+
+// A Layer that contains only an Image element.
+class ImageLayerChromium : public TiledLayerChromium {
+public:
+ static PassRefPtr<ImageLayerChromium> create();
+ virtual ~ImageLayerChromium();
+
+ virtual bool drawsContent() const OVERRIDE;
+ virtual void setTexturePriorities(const CCPriorityCalculator&) OVERRIDE;
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual bool needsContentsScale() const OVERRIDE;
+
+ void setBitmap(const SkBitmap& image);
+
+private:
+ ImageLayerChromium();
+
+ void setTilingOption(TilingOption);
+
+ virtual LayerTextureUpdater* textureUpdater() const OVERRIDE;
+ virtual void createTextureUpdaterIfNeeded() OVERRIDE;
+ virtual IntSize contentBounds() const OVERRIDE;
+
+ SkBitmap m_bitmap;
+
+ RefPtr<ImageLayerTextureUpdater> m_textureUpdater;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/LayerChromium.cpp b/cc/LayerChromium.cpp
new file mode 100644
index 0000000..8dc15ad
--- /dev/null
+++ b/cc/LayerChromium.cpp
@@ -0,0 +1,717 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "LayerChromium.h"
+
+#include "CCActiveAnimation.h"
+#include "CCAnimationEvents.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHost.h"
+#include "CCSettings.h"
+#include "TextStream.h"
+
+#include <public/WebAnimationDelegate.h>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+static int s_nextLayerId = 1;
+
+PassRefPtr<LayerChromium> LayerChromium::create()
+{
+ return adoptRef(new LayerChromium());
+}
+
+LayerChromium::LayerChromium()
+ : m_needsDisplay(false)
+ , m_stackingOrderChanged(false)
+ , m_layerId(s_nextLayerId++)
+ , m_parent(0)
+ , m_layerTreeHost(0)
+ , m_layerAnimationController(CCLayerAnimationController::create(this))
+ , m_scrollable(false)
+ , m_shouldScrollOnMainThread(false)
+ , m_haveWheelEventHandlers(false)
+ , m_nonFastScrollableRegionChanged(false)
+ , m_anchorPoint(0.5, 0.5)
+ , m_backgroundColor(0)
+ , m_debugBorderColor(0)
+ , m_debugBorderWidth(0)
+ , m_opacity(1.0)
+ , m_anchorPointZ(0)
+ , m_isContainerForFixedPositionLayers(false)
+ , m_fixedToContainerLayer(false)
+ , m_isDrawable(false)
+ , m_masksToBounds(false)
+ , m_opaque(false)
+ , m_doubleSided(true)
+ , m_useLCDText(false)
+ , m_preserves3D(false)
+ , m_useParentBackfaceVisibility(false)
+ , m_drawCheckerboardForMissingTiles(false)
+ , m_forceRenderSurface(false)
+ , m_replicaLayer(0)
+ , m_drawOpacity(0)
+ , m_drawOpacityIsAnimating(false)
+ , m_renderTarget(0)
+ , m_drawTransformIsAnimating(false)
+ , m_screenSpaceTransformIsAnimating(false)
+ , m_contentsScale(1.0)
+ , m_layerAnimationDelegate(0)
+ , m_layerScrollDelegate(0)
+{
+ if (m_layerId < 0) {
+ s_nextLayerId = 1;
+ m_layerId = s_nextLayerId++;
+ }
+}
+
+LayerChromium::~LayerChromium()
+{
+ // Our parent should be holding a reference to us so there should be no
+ // way for us to be destroyed while we still have a parent.
+ ASSERT(!parent());
+
+ // Remove the parent reference from all children.
+ removeAllChildren();
+}
+
+void LayerChromium::setUseLCDText(bool useLCDText)
+{
+ m_useLCDText = useLCDText;
+}
+
+void LayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
+{
+ if (m_layerTreeHost == host)
+ return;
+
+ m_layerTreeHost = host;
+
+ for (size_t i = 0; i < m_children.size(); ++i)
+ m_children[i]->setLayerTreeHost(host);
+
+ if (m_maskLayer)
+ m_maskLayer->setLayerTreeHost(host);
+ if (m_replicaLayer)
+ m_replicaLayer->setLayerTreeHost(host);
+
+ // If this layer already has active animations, the host needs to be notified.
+ if (host && m_layerAnimationController->hasActiveAnimation())
+ host->didAddAnimation();
+}
+
+void LayerChromium::setNeedsCommit()
+{
+ if (m_layerTreeHost)
+ m_layerTreeHost->setNeedsCommit();
+}
+
+void LayerChromium::setParent(LayerChromium* layer)
+{
+ ASSERT(!layer || !layer->hasAncestor(this));
+ m_parent = layer;
+ setLayerTreeHost(m_parent ? m_parent->layerTreeHost() : 0);
+}
+
+bool LayerChromium::hasAncestor(LayerChromium* ancestor) const
+{
+ for (LayerChromium* layer = parent(); layer; layer = layer->parent()) {
+ if (layer == ancestor)
+ return true;
+ }
+ return false;
+}
+
+void LayerChromium::addChild(PassRefPtr<LayerChromium> child)
+{
+ insertChild(child, numChildren());
+}
+
+void LayerChromium::insertChild(PassRefPtr<LayerChromium> child, size_t index)
+{
+ index = min(index, m_children.size());
+ child->removeFromParent();
+ child->setParent(this);
+ child->m_stackingOrderChanged = true;
+ m_children.insert(index, child);
+ setNeedsCommit();
+}
+
+void LayerChromium::removeFromParent()
+{
+ if (m_parent)
+ m_parent->removeChild(this);
+}
+
+void LayerChromium::removeChild(LayerChromium* child)
+{
+ int foundIndex = indexOfChild(child);
+ if (foundIndex == -1)
+ return;
+
+ child->setParent(0);
+ m_children.remove(foundIndex);
+ setNeedsCommit();
+}
+
+void LayerChromium::replaceChild(LayerChromium* reference, PassRefPtr<LayerChromium> newLayer)
+{
+ ASSERT_ARG(reference, reference);
+ ASSERT_ARG(reference, reference->parent() == this);
+
+ if (reference == newLayer)
+ return;
+
+ int referenceIndex = indexOfChild(reference);
+ if (referenceIndex == -1) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ reference->removeFromParent();
+
+ if (newLayer) {
+ newLayer->removeFromParent();
+ insertChild(newLayer, referenceIndex);
+ }
+}
+
+int LayerChromium::indexOfChild(const LayerChromium* reference)
+{
+ for (size_t i = 0; i < m_children.size(); i++) {
+ if (m_children[i] == reference)
+ return i;
+ }
+ return -1;
+}
+
+void LayerChromium::setBounds(const IntSize& size)
+{
+ if (bounds() == size)
+ return;
+
+ bool firstResize = bounds().isEmpty() && !size.isEmpty();
+
+ m_bounds = size;
+
+ if (firstResize)
+ setNeedsDisplay();
+ else
+ setNeedsCommit();
+}
+
+LayerChromium* LayerChromium::rootLayer()
+{
+ LayerChromium* layer = this;
+ while (layer->parent())
+ layer = layer->parent();
+ return layer;
+}
+
+void LayerChromium::removeAllChildren()
+{
+ while (m_children.size()) {
+ LayerChromium* layer = m_children[0].get();
+ ASSERT(layer->parent());
+ layer->removeFromParent();
+ }
+}
+
+void LayerChromium::setChildren(const Vector<RefPtr<LayerChromium> >& children)
+{
+ if (children == m_children)
+ return;
+
+ removeAllChildren();
+ size_t listSize = children.size();
+ for (size_t i = 0; i < listSize; i++)
+ addChild(children[i]);
+}
+
+void LayerChromium::setAnchorPoint(const FloatPoint& anchorPoint)
+{
+ if (m_anchorPoint == anchorPoint)
+ return;
+ m_anchorPoint = anchorPoint;
+ setNeedsCommit();
+}
+
+void LayerChromium::setAnchorPointZ(float anchorPointZ)
+{
+ if (m_anchorPointZ == anchorPointZ)
+ return;
+ m_anchorPointZ = anchorPointZ;
+ setNeedsCommit();
+}
+
+void LayerChromium::setBackgroundColor(SkColor backgroundColor)
+{
+ if (m_backgroundColor == backgroundColor)
+ return;
+ m_backgroundColor = backgroundColor;
+ setNeedsCommit();
+}
+
+void LayerChromium::setMasksToBounds(bool masksToBounds)
+{
+ if (m_masksToBounds == masksToBounds)
+ return;
+ m_masksToBounds = masksToBounds;
+ setNeedsCommit();
+}
+
+void LayerChromium::setMaskLayer(LayerChromium* maskLayer)
+{
+ if (m_maskLayer == maskLayer)
+ return;
+ if (m_maskLayer)
+ m_maskLayer->setLayerTreeHost(0);
+ m_maskLayer = maskLayer;
+ if (m_maskLayer) {
+ m_maskLayer->setLayerTreeHost(m_layerTreeHost);
+ m_maskLayer->setIsMask(true);
+ }
+ setNeedsCommit();
+}
+
+void LayerChromium::setReplicaLayer(LayerChromium* layer)
+{
+ if (m_replicaLayer == layer)
+ return;
+ if (m_replicaLayer)
+ m_replicaLayer->setLayerTreeHost(0);
+ m_replicaLayer = layer;
+ if (m_replicaLayer)
+ m_replicaLayer->setLayerTreeHost(m_layerTreeHost);
+ setNeedsCommit();
+}
+
+void LayerChromium::setFilters(const WebKit::WebFilterOperations& filters)
+{
+ if (m_filters == filters)
+ return;
+ m_filters = filters;
+ setNeedsCommit();
+ if (!filters.isEmpty())
+ CCLayerTreeHost::setNeedsFilterContext(true);
+}
+
+void LayerChromium::setBackgroundFilters(const WebKit::WebFilterOperations& backgroundFilters)
+{
+ if (m_backgroundFilters == backgroundFilters)
+ return;
+ m_backgroundFilters = backgroundFilters;
+ setNeedsCommit();
+ if (!backgroundFilters.isEmpty())
+ CCLayerTreeHost::setNeedsFilterContext(true);
+}
+
+void LayerChromium::setOpacity(float opacity)
+{
+ if (m_opacity == opacity)
+ return;
+ m_opacity = opacity;
+ setNeedsCommit();
+}
+
+bool LayerChromium::opacityIsAnimating() const
+{
+ return m_layerAnimationController->isAnimatingProperty(CCActiveAnimation::Opacity);
+}
+
+void LayerChromium::setOpaque(bool opaque)
+{
+ if (m_opaque == opaque)
+ return;
+ m_opaque = opaque;
+ setNeedsDisplay();
+}
+
+void LayerChromium::setPosition(const FloatPoint& position)
+{
+ if (m_position == position)
+ return;
+ m_position = position;
+ setNeedsCommit();
+}
+
+void LayerChromium::setSublayerTransform(const WebTransformationMatrix& sublayerTransform)
+{
+ if (m_sublayerTransform == sublayerTransform)
+ return;
+ m_sublayerTransform = sublayerTransform;
+ setNeedsCommit();
+}
+
+void LayerChromium::setTransform(const WebTransformationMatrix& transform)
+{
+ if (m_transform == transform)
+ return;
+ m_transform = transform;
+ setNeedsCommit();
+}
+
+bool LayerChromium::transformIsAnimating() const
+{
+ return m_layerAnimationController->isAnimatingProperty(CCActiveAnimation::Transform);
+}
+
+void LayerChromium::setScrollPosition(const IntPoint& scrollPosition)
+{
+ if (m_scrollPosition == scrollPosition)
+ return;
+ m_scrollPosition = scrollPosition;
+ setNeedsCommit();
+}
+
+void LayerChromium::setMaxScrollPosition(const IntSize& maxScrollPosition)
+{
+ if (m_maxScrollPosition == maxScrollPosition)
+ return;
+ m_maxScrollPosition = maxScrollPosition;
+ setNeedsCommit();
+}
+
+void LayerChromium::setScrollable(bool scrollable)
+{
+ if (m_scrollable == scrollable)
+ return;
+ m_scrollable = scrollable;
+ setNeedsCommit();
+}
+
+void LayerChromium::setShouldScrollOnMainThread(bool shouldScrollOnMainThread)
+{
+ if (m_shouldScrollOnMainThread == shouldScrollOnMainThread)
+ return;
+ m_shouldScrollOnMainThread = shouldScrollOnMainThread;
+ setNeedsCommit();
+}
+
+void LayerChromium::setHaveWheelEventHandlers(bool haveWheelEventHandlers)
+{
+ if (m_haveWheelEventHandlers == haveWheelEventHandlers)
+ return;
+ m_haveWheelEventHandlers = haveWheelEventHandlers;
+ setNeedsCommit();
+}
+
+void LayerChromium::setNonFastScrollableRegion(const Region& region)
+{
+ if (m_nonFastScrollableRegion == region)
+ return;
+ m_nonFastScrollableRegion = region;
+ m_nonFastScrollableRegionChanged = true;
+ setNeedsCommit();
+}
+
+void LayerChromium::scrollBy(const IntSize& scrollDelta)
+{
+ setScrollPosition(scrollPosition() + scrollDelta);
+ if (m_layerScrollDelegate)
+ m_layerScrollDelegate->didScroll(scrollDelta);
+}
+
+void LayerChromium::setDrawCheckerboardForMissingTiles(bool checkerboard)
+{
+ if (m_drawCheckerboardForMissingTiles == checkerboard)
+ return;
+ m_drawCheckerboardForMissingTiles = checkerboard;
+ setNeedsCommit();
+}
+
+void LayerChromium::setForceRenderSurface(bool force)
+{
+ if (m_forceRenderSurface == force)
+ return;
+ m_forceRenderSurface = force;
+ setNeedsCommit();
+}
+
+void LayerChromium::setDoubleSided(bool doubleSided)
+{
+ if (m_doubleSided == doubleSided)
+ return;
+ m_doubleSided = doubleSided;
+ setNeedsCommit();
+}
+
+void LayerChromium::setIsDrawable(bool isDrawable)
+{
+ if (m_isDrawable == isDrawable)
+ return;
+
+ m_isDrawable = isDrawable;
+ setNeedsCommit();
+}
+
+LayerChromium* LayerChromium::parent() const
+{
+ return m_parent;
+}
+
+void LayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
+{
+ m_updateRect.unite(dirtyRect);
+
+ // Simply mark the contents as dirty. For non-root layers, the call to
+ // setNeedsCommit will schedule a fresh compositing pass.
+ // For the root layer, setNeedsCommit has no effect.
+ if (!dirtyRect.isEmpty())
+ m_needsDisplay = true;
+
+ setNeedsCommit();
+}
+
+bool LayerChromium::descendantIsFixedToContainerLayer() const
+{
+ for (size_t i = 0; i < m_children.size(); ++i) {
+ if (m_children[i]->fixedToContainerLayer() || m_children[i]->descendantIsFixedToContainerLayer())
+ return true;
+ }
+ return false;
+}
+
+void LayerChromium::setIsContainerForFixedPositionLayers(bool isContainerForFixedPositionLayers)
+{
+ if (m_isContainerForFixedPositionLayers == isContainerForFixedPositionLayers)
+ return;
+ m_isContainerForFixedPositionLayers = isContainerForFixedPositionLayers;
+
+ if (m_layerTreeHost && m_layerTreeHost->commitRequested())
+ return;
+
+ // Only request a commit if we have a fixed positioned descendant.
+ if (descendantIsFixedToContainerLayer())
+ setNeedsCommit();
+}
+
+void LayerChromium::setFixedToContainerLayer(bool fixedToContainerLayer)
+{
+ if (m_fixedToContainerLayer == fixedToContainerLayer)
+ return;
+ m_fixedToContainerLayer = fixedToContainerLayer;
+ setNeedsCommit();
+}
+
+void LayerChromium::pushPropertiesTo(CCLayerImpl* layer)
+{
+ layer->setAnchorPoint(m_anchorPoint);
+ layer->setAnchorPointZ(m_anchorPointZ);
+ layer->setBackgroundColor(m_backgroundColor);
+ layer->setBounds(m_bounds);
+ layer->setContentBounds(contentBounds());
+ layer->setDebugBorderColor(m_debugBorderColor);
+ layer->setDebugBorderWidth(m_debugBorderWidth);
+ layer->setDebugName(m_debugName.isolatedCopy()); // We have to use isolatedCopy() here to safely pass ownership to another thread.
+ layer->setDoubleSided(m_doubleSided);
+ layer->setDrawCheckerboardForMissingTiles(m_drawCheckerboardForMissingTiles);
+ layer->setForceRenderSurface(m_forceRenderSurface);
+ layer->setDrawsContent(drawsContent());
+ layer->setFilters(filters());
+ layer->setBackgroundFilters(backgroundFilters());
+ layer->setUseLCDText(m_useLCDText);
+ layer->setMasksToBounds(m_masksToBounds);
+ layer->setScrollable(m_scrollable);
+ layer->setShouldScrollOnMainThread(m_shouldScrollOnMainThread);
+ layer->setHaveWheelEventHandlers(m_haveWheelEventHandlers);
+ // Copying a Region is more expensive than most layer properties, since it involves copying two Vectors that may be
+ // arbitrarily large depending on page content, so we only push the property if it's changed.
+ if (m_nonFastScrollableRegionChanged) {
+ layer->setNonFastScrollableRegion(m_nonFastScrollableRegion);
+ m_nonFastScrollableRegionChanged = false;
+ }
+ layer->setOpaque(m_opaque);
+ if (!opacityIsAnimating())
+ layer->setOpacity(m_opacity);
+ layer->setPosition(m_position);
+ layer->setIsContainerForFixedPositionLayers(m_isContainerForFixedPositionLayers);
+ layer->setFixedToContainerLayer(m_fixedToContainerLayer);
+ layer->setPreserves3D(preserves3D());
+ layer->setUseParentBackfaceVisibility(m_useParentBackfaceVisibility);
+ layer->setScrollPosition(m_scrollPosition);
+ layer->setMaxScrollPosition(m_maxScrollPosition);
+ layer->setSublayerTransform(m_sublayerTransform);
+ if (!transformIsAnimating())
+ layer->setTransform(m_transform);
+
+ // If the main thread commits multiple times before the impl thread actually draws, then damage tracking
+ // will become incorrect if we simply clobber the updateRect here. The CCLayerImpl's updateRect needs to
+ // accumulate (i.e. union) any update changes that have occurred on the main thread.
+ m_updateRect.uniteIfNonZero(layer->updateRect());
+ layer->setUpdateRect(m_updateRect);
+
+ layer->setScrollDelta(layer->scrollDelta() - layer->sentScrollDelta());
+ layer->setSentScrollDelta(IntSize());
+
+ layer->setStackingOrderChanged(m_stackingOrderChanged);
+
+ if (maskLayer())
+ maskLayer()->pushPropertiesTo(layer->maskLayer());
+ if (replicaLayer())
+ replicaLayer()->pushPropertiesTo(layer->replicaLayer());
+
+ m_layerAnimationController->pushAnimationUpdatesTo(layer->layerAnimationController());
+
+ // Reset any state that should be cleared for the next update.
+ m_stackingOrderChanged = false;
+ m_updateRect = FloatRect();
+}
+
+PassOwnPtr<CCLayerImpl> LayerChromium::createCCLayerImpl()
+{
+ return CCLayerImpl::create(m_layerId);
+}
+
+void LayerChromium::setDebugBorderColor(SkColor color)
+{
+ m_debugBorderColor = color;
+ setNeedsCommit();
+}
+
+void LayerChromium::setDebugBorderWidth(float width)
+{
+ m_debugBorderWidth = width;
+ setNeedsCommit();
+}
+
+void LayerChromium::setDebugName(const String& debugName)
+{
+ m_debugName = debugName;
+ setNeedsCommit();
+}
+
+void LayerChromium::setContentsScale(float contentsScale)
+{
+ if (!needsContentsScale() || m_contentsScale == contentsScale)
+ return;
+ m_contentsScale = contentsScale;
+ setNeedsDisplay();
+}
+
+void LayerChromium::createRenderSurface()
+{
+ ASSERT(!m_renderSurface);
+ m_renderSurface = adoptPtr(new RenderSurfaceChromium(this));
+ setRenderTarget(this);
+}
+
+bool LayerChromium::descendantDrawsContent()
+{
+ for (size_t i = 0; i < m_children.size(); ++i) {
+ if (m_children[i]->drawsContent() || m_children[i]->descendantDrawsContent())
+ return true;
+ }
+ return false;
+}
+
+void LayerChromium::setOpacityFromAnimation(float opacity)
+{
+ // This is called due to an ongoing accelerated animation. Since this animation is
+ // also being run on the impl thread, there is no need to request a commit to push
+ // this value over, so set the value directly rather than calling setOpacity.
+ m_opacity = opacity;
+}
+
+void LayerChromium::setTransformFromAnimation(const WebTransformationMatrix& transform)
+{
+ // This is called due to an ongoing accelerated animation. Since this animation is
+ // also being run on the impl thread, there is no need to request a commit to push
+ // this value over, so set this value directly rather than calling setTransform.
+ m_transform = transform;
+}
+
+bool LayerChromium::addAnimation(PassOwnPtr<CCActiveAnimation> animation)
+{
+ if (!CCSettings::acceleratedAnimationEnabled())
+ return false;
+
+ m_layerAnimationController->addAnimation(animation);
+ if (m_layerTreeHost) {
+ m_layerTreeHost->didAddAnimation();
+ setNeedsCommit();
+ }
+ return true;
+}
+
+void LayerChromium::pauseAnimation(int animationId, double timeOffset)
+{
+ m_layerAnimationController->pauseAnimation(animationId, timeOffset);
+ setNeedsCommit();
+}
+
+void LayerChromium::removeAnimation(int animationId)
+{
+ m_layerAnimationController->removeAnimation(animationId);
+ setNeedsCommit();
+}
+
+void LayerChromium::suspendAnimations(double monotonicTime)
+{
+ m_layerAnimationController->suspendAnimations(monotonicTime);
+ setNeedsCommit();
+}
+
+void LayerChromium::resumeAnimations(double monotonicTime)
+{
+ m_layerAnimationController->resumeAnimations(monotonicTime);
+ setNeedsCommit();
+}
+
+void LayerChromium::setLayerAnimationController(PassOwnPtr<CCLayerAnimationController> layerAnimationController)
+{
+ m_layerAnimationController = layerAnimationController;
+ if (m_layerAnimationController) {
+ m_layerAnimationController->setClient(this);
+ m_layerAnimationController->setForceSync();
+ }
+ setNeedsCommit();
+}
+
+PassOwnPtr<CCLayerAnimationController> LayerChromium::releaseLayerAnimationController()
+{
+ OwnPtr<CCLayerAnimationController> toReturn = m_layerAnimationController.release();
+ m_layerAnimationController = CCLayerAnimationController::create(this);
+ return toReturn.release();
+}
+
+bool LayerChromium::hasActiveAnimation() const
+{
+ return m_layerAnimationController->hasActiveAnimation();
+}
+
+void LayerChromium::notifyAnimationStarted(const CCAnimationEvent& event, double wallClockTime)
+{
+ m_layerAnimationController->notifyAnimationStarted(event);
+ if (m_layerAnimationDelegate)
+ m_layerAnimationDelegate->notifyAnimationStarted(wallClockTime);
+}
+
+void LayerChromium::notifyAnimationFinished(double wallClockTime)
+{
+ if (m_layerAnimationDelegate)
+ m_layerAnimationDelegate->notifyAnimationFinished(wallClockTime);
+}
+
+Region LayerChromium::visibleContentOpaqueRegion() const
+{
+ if (opaque())
+ return visibleContentRect();
+ return Region();
+}
+
+void sortLayers(Vector<RefPtr<LayerChromium> >::iterator, Vector<RefPtr<LayerChromium> >::iterator, void*)
+{
+ // Currently we don't use z-order to decide what to paint, so there's no need to actually sort LayerChromiums.
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/LayerChromium.h b/cc/LayerChromium.h
new file mode 100644
index 0000000..273bf8e
--- /dev/null
+++ b/cc/LayerChromium.h
@@ -0,0 +1,381 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef LayerChromium_h
+#define LayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerAnimationController.h"
+#include "CCOcclusionTracker.h"
+#include "CCPrioritizedTexture.h"
+#include "FloatPoint.h"
+#include "Region.h"
+#include "RenderSurfaceChromium.h"
+#include "SkColor.h"
+
+#include <public/WebFilterOperations.h>
+#include <public/WebTransformationMatrix.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+class WebAnimationDelegate;
+}
+
+namespace WebCore {
+
+class CCActiveAnimation;
+struct CCAnimationEvent;
+class CCLayerAnimationDelegate;
+class CCLayerImpl;
+class CCLayerTreeHost;
+class CCTextureUpdateQueue;
+class ScrollbarLayerChromium;
+struct CCAnimationEvent;
+struct CCRenderingStats;
+
+// Delegate for handling scroll input for a LayerChromium.
+class LayerChromiumScrollDelegate {
+public:
+ virtual void didScroll(const IntSize&) = 0;
+
+protected:
+ virtual ~LayerChromiumScrollDelegate() { }
+};
+
+// Base class for composited layers. Special layer types are derived from
+// this class.
+class LayerChromium : public RefCounted<LayerChromium>, public CCLayerAnimationControllerClient {
+public:
+ static PassRefPtr<LayerChromium> create();
+
+ virtual ~LayerChromium();
+
+ // CCLayerAnimationControllerClient implementation
+ virtual int id() const OVERRIDE { return m_layerId; }
+ virtual void setOpacityFromAnimation(float) OVERRIDE;
+ virtual float opacity() const OVERRIDE { return m_opacity; }
+ virtual void setTransformFromAnimation(const WebKit::WebTransformationMatrix&) OVERRIDE;
+ // A layer's transform operates layer space. That is, entirely in logical,
+ // non-page-scaled pixels (that is, they have page zoom baked in, but not page scale).
+ // The root layer is a special case -- it operates in physical pixels.
+ virtual const WebKit::WebTransformationMatrix& transform() const OVERRIDE { return m_transform; }
+
+ LayerChromium* rootLayer();
+ LayerChromium* parent() const;
+ void addChild(PassRefPtr<LayerChromium>);
+ void insertChild(PassRefPtr<LayerChromium>, size_t index);
+ void replaceChild(LayerChromium* reference, PassRefPtr<LayerChromium> newLayer);
+ void removeFromParent();
+ void removeAllChildren();
+ void setChildren(const Vector<RefPtr<LayerChromium> >&);
+ const Vector<RefPtr<LayerChromium> >& children() const { return m_children; }
+
+ void setAnchorPoint(const FloatPoint&);
+ FloatPoint anchorPoint() const { return m_anchorPoint; }
+
+ void setAnchorPointZ(float);
+ float anchorPointZ() const { return m_anchorPointZ; }
+
+ void setBackgroundColor(SkColor);
+ SkColor backgroundColor() const { return m_backgroundColor; }
+
+ // A layer's bounds are in logical, non-page-scaled pixels (however, the
+ // root layer's bounds are in physical pixels).
+ void setBounds(const IntSize&);
+ const IntSize& bounds() const { return m_bounds; }
+ virtual IntSize contentBounds() const { return bounds(); }
+
+ void setMasksToBounds(bool);
+ bool masksToBounds() const { return m_masksToBounds; }
+
+ void setMaskLayer(LayerChromium*);
+ LayerChromium* maskLayer() const { return m_maskLayer.get(); }
+
+ virtual void setNeedsDisplayRect(const FloatRect& dirtyRect);
+ void setNeedsDisplay() { setNeedsDisplayRect(FloatRect(FloatPoint(), bounds())); }
+ virtual bool needsDisplay() const { return m_needsDisplay; }
+
+ void setOpacity(float);
+ bool opacityIsAnimating() const;
+
+ void setFilters(const WebKit::WebFilterOperations&);
+ const WebKit::WebFilterOperations& filters() const { return m_filters; }
+
+ // Background filters are filters applied to what is behind this layer, when they are viewed through non-opaque
+ // regions in this layer. They are used through the WebLayer interface, and are not exposed to HTML.
+ void setBackgroundFilters(const WebKit::WebFilterOperations&);
+ const WebKit::WebFilterOperations& backgroundFilters() const { return m_backgroundFilters; }
+
+ virtual void setOpaque(bool);
+ bool opaque() const { return m_opaque; }
+
+ void setPosition(const FloatPoint&);
+ FloatPoint position() const { return m_position; }
+
+ void setIsContainerForFixedPositionLayers(bool);
+ bool isContainerForFixedPositionLayers() const { return m_isContainerForFixedPositionLayers; }
+
+ void setFixedToContainerLayer(bool);
+ bool fixedToContainerLayer() const { return m_fixedToContainerLayer; }
+
+ void setSublayerTransform(const WebKit::WebTransformationMatrix&);
+ const WebKit::WebTransformationMatrix& sublayerTransform() const { return m_sublayerTransform; }
+
+ void setTransform(const WebKit::WebTransformationMatrix&);
+ bool transformIsAnimating() const;
+
+ const IntRect& visibleContentRect() const { return m_visibleContentRect; }
+ void setVisibleContentRect(const IntRect& visibleContentRect) { m_visibleContentRect = visibleContentRect; }
+
+ void setScrollPosition(const IntPoint&);
+ const IntPoint& scrollPosition() const { return m_scrollPosition; }
+
+ void setMaxScrollPosition(const IntSize&);
+ const IntSize& maxScrollPosition() const { return m_maxScrollPosition; }
+
+ void setScrollable(bool);
+ bool scrollable() const { return m_scrollable; }
+ void setShouldScrollOnMainThread(bool);
+ void setHaveWheelEventHandlers(bool);
+ const Region& nonFastScrollableRegion() { return m_nonFastScrollableRegion; }
+ void setNonFastScrollableRegion(const Region&);
+ void setNonFastScrollableRegionChanged() { m_nonFastScrollableRegionChanged = true; }
+ void setLayerScrollDelegate(LayerChromiumScrollDelegate* layerScrollDelegate) { m_layerScrollDelegate = layerScrollDelegate; }
+ void scrollBy(const IntSize&);
+
+ void setDrawCheckerboardForMissingTiles(bool);
+ bool drawCheckerboardForMissingTiles() const { return m_drawCheckerboardForMissingTiles; }
+
+ bool forceRenderSurface() const { return m_forceRenderSurface; }
+ void setForceRenderSurface(bool);
+
+ IntSize scrollDelta() const { return IntSize(); }
+
+ float pageScaleDelta() const { return 1; }
+
+ void setDoubleSided(bool);
+ bool doubleSided() const { return m_doubleSided; }
+
+ void setPreserves3D(bool preserve3D) { m_preserves3D = preserve3D; }
+ bool preserves3D() const { return m_preserves3D; }
+
+ void setUseParentBackfaceVisibility(bool useParentBackfaceVisibility) { m_useParentBackfaceVisibility = useParentBackfaceVisibility; }
+ bool useParentBackfaceVisibility() const { return m_useParentBackfaceVisibility; }
+
+ virtual void setUseLCDText(bool);
+ bool useLCDText() const { return m_useLCDText; }
+
+ virtual void setLayerTreeHost(CCLayerTreeHost*);
+
+ void setIsDrawable(bool);
+
+ void setReplicaLayer(LayerChromium*);
+ LayerChromium* replicaLayer() const { return m_replicaLayer.get(); }
+
+ bool hasMask() const { return m_maskLayer; }
+ bool hasReplica() const { return m_replicaLayer; }
+ bool replicaHasMask() const { return m_replicaLayer && (m_maskLayer || m_replicaLayer->m_maskLayer); }
+
+ // These methods typically need to be overwritten by derived classes.
+ virtual bool drawsContent() const { return m_isDrawable; }
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) { }
+ virtual bool needMoreUpdates() { return false; }
+ virtual void setIsMask(bool) { }
+ virtual void bindContentsTexture() { }
+ virtual bool needsContentsScale() const { return false; }
+
+ void setDebugBorderColor(SkColor);
+ void setDebugBorderWidth(float);
+ void setDebugName(const String&);
+
+ virtual void pushPropertiesTo(CCLayerImpl*);
+
+ void clearRenderSurface() { m_renderSurface.clear(); }
+ RenderSurfaceChromium* renderSurface() const { return m_renderSurface.get(); }
+ void createRenderSurface();
+
+ float drawOpacity() const { return m_drawOpacity; }
+ void setDrawOpacity(float opacity) { m_drawOpacity = opacity; }
+
+ bool drawOpacityIsAnimating() const { return m_drawOpacityIsAnimating; }
+ void setDrawOpacityIsAnimating(bool drawOpacityIsAnimating) { m_drawOpacityIsAnimating = drawOpacityIsAnimating; }
+
+ LayerChromium* renderTarget() const { ASSERT(!m_renderTarget || m_renderTarget->renderSurface()); return m_renderTarget; }
+ void setRenderTarget(LayerChromium* target) { m_renderTarget = target; }
+
+ bool drawTransformIsAnimating() const { return m_drawTransformIsAnimating; }
+ void setDrawTransformIsAnimating(bool animating) { m_drawTransformIsAnimating = animating; }
+ bool screenSpaceTransformIsAnimating() const { return m_screenSpaceTransformIsAnimating; }
+ void setScreenSpaceTransformIsAnimating(bool animating) { m_screenSpaceTransformIsAnimating = animating; }
+
+ // This moves from layer space, with origin in the center to target space with origin in the top left.
+ // That is, it converts from logical, non-page-scaled, to target pixels (and if the target is the
+ // root render surface, then this converts to physical pixels).
+ const WebKit::WebTransformationMatrix& drawTransform() const { return m_drawTransform; }
+ void setDrawTransform(const WebKit::WebTransformationMatrix& matrix) { m_drawTransform = matrix; }
+ // This moves from content space, with origin the top left to screen space with origin in the top left.
+ // It converts logical, non-page-scaled pixels to physical pixels.
+ const WebKit::WebTransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
+ void setScreenSpaceTransform(const WebKit::WebTransformationMatrix& matrix) { m_screenSpaceTransform = matrix; }
+ const IntRect& drawableContentRect() const { return m_drawableContentRect; }
+ void setDrawableContentRect(const IntRect& rect) { m_drawableContentRect = rect; }
+ // The contentsScale converts from logical, non-page-scaled pixels to target pixels.
+ // The contentsScale is 1 for the root layer as it is already in physical pixels.
+ float contentsScale() const { return m_contentsScale; }
+ void setContentsScale(float);
+
+ // Returns true if any of the layer's descendants has content to draw.
+ bool descendantDrawsContent();
+
+ CCLayerTreeHost* layerTreeHost() const { return m_layerTreeHost; }
+
+ // Set the priority of all desired textures in this layer.
+ virtual void setTexturePriorities(const CCPriorityCalculator&) { }
+
+ bool addAnimation(PassOwnPtr<CCActiveAnimation>);
+ void pauseAnimation(int animationId, double timeOffset);
+ void removeAnimation(int animationId);
+
+ void suspendAnimations(double monotonicTime);
+ void resumeAnimations(double monotonicTime);
+
+ CCLayerAnimationController* layerAnimationController() { return m_layerAnimationController.get(); }
+ void setLayerAnimationController(PassOwnPtr<CCLayerAnimationController>);
+ PassOwnPtr<CCLayerAnimationController> releaseLayerAnimationController();
+
+ void setLayerAnimationDelegate(WebKit::WebAnimationDelegate* layerAnimationDelegate) { m_layerAnimationDelegate = layerAnimationDelegate; }
+
+ bool hasActiveAnimation() const;
+
+ virtual void notifyAnimationStarted(const CCAnimationEvent&, double wallClockTime);
+ virtual void notifyAnimationFinished(double wallClockTime);
+
+ virtual Region visibleContentOpaqueRegion() const;
+
+ virtual ScrollbarLayerChromium* toScrollbarLayerChromium() { return 0; }
+
+protected:
+ friend class CCLayerImpl;
+ friend class TreeSynchronizer;
+
+ LayerChromium();
+
+ void setNeedsCommit();
+
+ // This flag is set when layer need repainting/updating.
+ bool m_needsDisplay;
+
+ // Tracks whether this layer may have changed stacking order with its siblings.
+ bool m_stackingOrderChanged;
+
+ // The update rect is the region of the compositor resource that was actually updated by the compositor.
+ // For layers that may do updating outside the compositor's control (i.e. plugin layers), this information
+ // is not available and the update rect will remain empty.
+ // Note this rect is in layer space (not content space).
+ FloatRect m_updateRect;
+
+ RefPtr<LayerChromium> m_maskLayer;
+
+ // Constructs a CCLayerImpl of the correct runtime type for this LayerChromium type.
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl();
+ int m_layerId;
+
+private:
+ void setParent(LayerChromium*);
+ bool hasAncestor(LayerChromium*) const;
+ bool descendantIsFixedToContainerLayer() const;
+
+ size_t numChildren() const { return m_children.size(); }
+
+ // Returns the index of the child or -1 if not found.
+ int indexOfChild(const LayerChromium*);
+
+ // This should only be called from removeFromParent.
+ void removeChild(LayerChromium*);
+
+ Vector<RefPtr<LayerChromium> > m_children;
+ LayerChromium* m_parent;
+
+ // LayerChromium instances have a weak pointer to their CCLayerTreeHost.
+ // This pointer value is nil when a LayerChromium is not in a tree and is
+ // updated via setLayerTreeHost() if a layer moves between trees.
+ CCLayerTreeHost* m_layerTreeHost;
+
+ OwnPtr<CCLayerAnimationController> m_layerAnimationController;
+
+ // Layer properties.
+ IntSize m_bounds;
+
+ // Uses layer's content space.
+ IntRect m_visibleContentRect;
+
+ IntPoint m_scrollPosition;
+ IntSize m_maxScrollPosition;
+ bool m_scrollable;
+ bool m_shouldScrollOnMainThread;
+ bool m_haveWheelEventHandlers;
+ Region m_nonFastScrollableRegion;
+ bool m_nonFastScrollableRegionChanged;
+ FloatPoint m_position;
+ FloatPoint m_anchorPoint;
+ SkColor m_backgroundColor;
+ SkColor m_debugBorderColor;
+ float m_debugBorderWidth;
+ String m_debugName;
+ float m_opacity;
+ WebKit::WebFilterOperations m_filters;
+ WebKit::WebFilterOperations m_backgroundFilters;
+ float m_anchorPointZ;
+ bool m_isContainerForFixedPositionLayers;
+ bool m_fixedToContainerLayer;
+ bool m_isDrawable;
+ bool m_masksToBounds;
+ bool m_opaque;
+ bool m_doubleSided;
+ bool m_useLCDText;
+ bool m_preserves3D;
+ bool m_useParentBackfaceVisibility;
+ bool m_drawCheckerboardForMissingTiles;
+ bool m_forceRenderSurface;
+
+ WebKit::WebTransformationMatrix m_transform;
+ WebKit::WebTransformationMatrix m_sublayerTransform;
+
+ // Replica layer used for reflections.
+ RefPtr<LayerChromium> m_replicaLayer;
+
+ // Transient properties.
+ OwnPtr<RenderSurfaceChromium> m_renderSurface;
+ float m_drawOpacity;
+ bool m_drawOpacityIsAnimating;
+
+ LayerChromium* m_renderTarget;
+
+ WebKit::WebTransformationMatrix m_drawTransform;
+ WebKit::WebTransformationMatrix m_screenSpaceTransform;
+ bool m_drawTransformIsAnimating;
+ bool m_screenSpaceTransformIsAnimating;
+
+ // Uses target surface space.
+ IntRect m_drawableContentRect;
+ float m_contentsScale;
+
+ WebKit::WebAnimationDelegate* m_layerAnimationDelegate;
+ LayerChromiumScrollDelegate* m_layerScrollDelegate;
+};
+
+void sortLayers(Vector<RefPtr<LayerChromium> >::iterator, Vector<RefPtr<LayerChromium> >::iterator, void*);
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/LayerPainterChromium.h b/cc/LayerPainterChromium.h
new file mode 100644
index 0000000..98eadf5
--- /dev/null
+++ b/cc/LayerPainterChromium.h
@@ -0,0 +1,27 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef LayerPainterChromium_h
+#define LayerPainterChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+class SkCanvas;
+
+namespace WebCore {
+
+class FloatRect;
+class IntRect;
+
+class LayerPainterChromium {
+public:
+ virtual ~LayerPainterChromium() { }
+ virtual void paint(SkCanvas*, const IntRect& contentRect, FloatRect& opaque) = 0;
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // LayerPainterChromium_h
+
diff --git a/cc/LayerTextureSubImage.cpp b/cc/LayerTextureSubImage.cpp
new file mode 100644
index 0000000..f555fa1
--- /dev/null
+++ b/cc/LayerTextureSubImage.cpp
@@ -0,0 +1,109 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerTextureSubImage.h"
+
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "Extensions3DChromium.h"
+#include "TraceEvent.h"
+#include <public/WebGraphicsContext3D.h>
+
+using WebKit::WebGraphicsContext3D;
+
+namespace WebCore {
+
+LayerTextureSubImage::LayerTextureSubImage(bool useMapTexSubImage)
+ : m_useMapTexSubImage(useMapTexSubImage)
+ , m_subImageSize(0)
+{
+}
+
+LayerTextureSubImage::~LayerTextureSubImage()
+{
+}
+
+void LayerTextureSubImage::upload(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebGraphicsContext3D* context)
+{
+ if (m_useMapTexSubImage)
+ uploadWithMapTexSubImage(image, imageRect, sourceRect, destOffset, format, context);
+ else
+ uploadWithTexSubImage(image, imageRect, sourceRect, destOffset, format, context);
+}
+
+void LayerTextureSubImage::uploadWithTexSubImage(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebGraphicsContext3D* context)
+{
+ TRACE_EVENT0("cc", "LayerTextureSubImage::uploadWithTexSubImage");
+
+ // Offset from image-rect to source-rect.
+ IntPoint offset(sourceRect.x() - imageRect.x(), sourceRect.y() - imageRect.y());
+
+ const uint8_t* pixelSource;
+ if (imageRect.width() == sourceRect.width() && !offset.x())
+ pixelSource = &image[4 * offset.y() * imageRect.width()];
+ else {
+ size_t neededSize = 4 * sourceRect.width() * sourceRect.height();
+ if (m_subImageSize < neededSize) {
+ m_subImage = adoptArrayPtr(new uint8_t[neededSize]);
+ m_subImageSize = neededSize;
+ }
+ // Strides not equal, so do a row-by-row memcpy from the
+ // paint results into a temp buffer for uploading.
+ for (int row = 0; row < sourceRect.height(); ++row)
+ memcpy(&m_subImage[sourceRect.width() * 4 * row],
+ &image[4 * (offset.x() + (offset.y() + row) * imageRect.width())],
+ sourceRect.width() * 4);
+
+ pixelSource = &m_subImage[0];
+ }
+
+ GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, destOffset.width(), destOffset.height(), sourceRect.width(), sourceRect.height(), format, GraphicsContext3D::UNSIGNED_BYTE, pixelSource));
+}
+
+void LayerTextureSubImage::uploadWithMapTexSubImage(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebGraphicsContext3D* context)
+{
+ TRACE_EVENT0("cc", "LayerTextureSubImage::uploadWithMapTexSubImage");
+ // Offset from image-rect to source-rect.
+ IntPoint offset(sourceRect.x() - imageRect.x(), sourceRect.y() - imageRect.y());
+
+ // Upload tile data via a mapped transfer buffer
+ uint8_t* pixelDest = static_cast<uint8_t*>(context->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, destOffset.width(), destOffset.height(), sourceRect.width(), sourceRect.height(), format, GraphicsContext3D::UNSIGNED_BYTE, Extensions3DChromium::WRITE_ONLY));
+
+ if (!pixelDest) {
+ uploadWithTexSubImage(image, imageRect, sourceRect, destOffset, format, context);
+ return;
+ }
+
+ unsigned int componentsPerPixel;
+ unsigned int bytesPerComponent;
+ if (!GraphicsContext3D::computeFormatAndTypeParameters(format, GraphicsContext3D::UNSIGNED_BYTE, &componentsPerPixel, &bytesPerComponent)) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ if (imageRect.width() == sourceRect.width() && !offset.x())
+ memcpy(pixelDest, &image[offset.y() * imageRect.width() * componentsPerPixel * bytesPerComponent], imageRect.width() * sourceRect.height() * componentsPerPixel * bytesPerComponent);
+ else {
+ // Strides not equal, so do a row-by-row memcpy from the
+ // paint results into the pixelDest
+ for (int row = 0; row < sourceRect.height(); ++row)
+ memcpy(&pixelDest[sourceRect.width() * row * componentsPerPixel * bytesPerComponent],
+ &image[4 * (offset.x() + (offset.y() + row) * imageRect.width())],
+ sourceRect.width() * componentsPerPixel * bytesPerComponent);
+ }
+ GLC(context, context->unmapTexSubImage2DCHROMIUM(pixelDest));
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/LayerTextureSubImage.h b/cc/LayerTextureSubImage.h
new file mode 100644
index 0000000..bb63bf1
--- /dev/null
+++ b/cc/LayerTextureSubImage.h
@@ -0,0 +1,46 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef LayerTextureSubImage_h
+#define LayerTextureSubImage_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "GraphicsTypes3D.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include <wtf/OwnArrayPtr.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class LayerTextureSubImage {
+public:
+ explicit LayerTextureSubImage(bool useMapSubForUpload);
+ ~LayerTextureSubImage();
+
+ void upload(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebKit::WebGraphicsContext3D*);
+
+private:
+ void uploadWithTexSubImage(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebKit::WebGraphicsContext3D*);
+ void uploadWithMapTexSubImage(const uint8_t* image, const IntRect& imageRect,
+ const IntRect& sourceRect, const IntSize& destOffset,
+ GC3Denum format, WebKit::WebGraphicsContext3D*);
+
+ bool m_useMapTexSubImage;
+ size_t m_subImageSize;
+ OwnArrayPtr<uint8_t> m_subImage;
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // LayerTextureSubImage_h
diff --git a/cc/LayerTextureUpdater.h b/cc/LayerTextureUpdater.h
new file mode 100644
index 0000000..9750498
--- /dev/null
+++ b/cc/LayerTextureUpdater.h
@@ -0,0 +1,63 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef LayerTextureUpdater_h
+#define LayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCPrioritizedTexture.h"
+#include "GraphicsTypes3D.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IntRect;
+class IntSize;
+class TextureManager;
+struct CCRenderingStats;
+
+class LayerTextureUpdater : public RefCounted<LayerTextureUpdater> {
+public:
+ // Allows texture uploaders to store per-tile resources.
+ class Texture {
+ public:
+ virtual ~Texture() { }
+
+ CCPrioritizedTexture* texture() { return m_texture.get(); }
+ void swapTextureWith(OwnPtr<CCPrioritizedTexture>& texture) { m_texture.swap(texture); }
+ virtual void prepareRect(const IntRect& /* sourceRect */, CCRenderingStats&) { }
+ virtual void updateRect(CCResourceProvider*, const IntRect& sourceRect, const IntSize& destOffset) = 0;
+ protected:
+ explicit Texture(PassOwnPtr<CCPrioritizedTexture> texture) : m_texture(texture) { }
+
+ private:
+ OwnPtr<CCPrioritizedTexture> m_texture;
+ };
+
+ virtual ~LayerTextureUpdater() { }
+
+ enum SampledTexelFormat {
+ SampledTexelFormatRGBA,
+ SampledTexelFormatBGRA,
+ SampledTexelFormatInvalid,
+ };
+ virtual PassOwnPtr<Texture> createTexture(CCPrioritizedTextureManager*) = 0;
+ // Returns the format of the texel uploaded by this interface.
+ // This format should not be confused by texture internal format.
+ // This format specifies the component order in the sampled texel.
+ // If the format is TexelFormatBGRA, vec4.x is blue and vec4.z is red.
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum textureFormat) = 0;
+ // The |resultingOpaqueRect| gives back a region of the layer that was painted opaque. If the layer is marked opaque in the updater,
+ // then this region should be ignored in preference for the entire layer's area.
+ virtual void prepareToUpdate(const IntRect& contentRect, const IntSize& tileSize, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats&) { }
+
+ // Set true by the layer when it is known that the entire output is going to be opaque.
+ virtual void setOpaque(bool) { }
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // LayerTextureUpdater_h
diff --git a/cc/PlatformColor.h b/cc/PlatformColor.h
new file mode 100644
index 0000000..2898047
--- /dev/null
+++ b/cc/PlatformColor.h
@@ -0,0 +1,58 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PlatformColor_h
+#define PlatformColor_h
+
+#include "Extensions3D.h"
+#include "GraphicsContext3D.h"
+#include "SkTypes.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+class PlatformColor {
+public:
+ static GraphicsContext3D::SourceDataFormat format()
+ {
+ return SK_B32_SHIFT ? GraphicsContext3D::SourceFormatRGBA8 : GraphicsContext3D::SourceFormatBGRA8;
+ }
+
+ // Returns the most efficient texture format for this platform.
+ static GC3Denum bestTextureFormat(WebKit::WebGraphicsContext3D* context, bool supportsBGRA8888)
+ {
+ GC3Denum textureFormat = GraphicsContext3D::RGBA;
+ switch (format()) {
+ case GraphicsContext3D::SourceFormatRGBA8:
+ break;
+ case GraphicsContext3D::SourceFormatBGRA8:
+ if (supportsBGRA8888)
+ textureFormat = Extensions3D::BGRA_EXT;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ return textureFormat;
+ }
+
+ // Return true if the given texture format has the same component order
+ // as the color on this platform.
+ static bool sameComponentOrder(GC3Denum textureFormat)
+ {
+ switch (format()) {
+ case GraphicsContext3D::SourceFormatRGBA8:
+ return textureFormat == GraphicsContext3D::RGBA;
+ case GraphicsContext3D::SourceFormatBGRA8:
+ return textureFormat == Extensions3D::BGRA_EXT;
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/cc/ProgramBinding.cpp b/cc/ProgramBinding.cpp
new file mode 100644
index 0000000..e61bf78
--- /dev/null
+++ b/cc/ProgramBinding.cpp
@@ -0,0 +1,149 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ProgramBinding.h"
+
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "GeometryBinding.h"
+#include "GraphicsContext3D.h"
+#include "TraceEvent.h"
+#include <public/WebGraphicsContext3D.h>
+#include <wtf/text/CString.h>
+
+using WebKit::WebGraphicsContext3D;
+
+namespace WebCore {
+
+ProgramBindingBase::ProgramBindingBase()
+ : m_program(0)
+ , m_vertexShaderId(0)
+ , m_fragmentShaderId(0)
+ , m_initialized(false)
+{
+}
+
+ProgramBindingBase::~ProgramBindingBase()
+{
+ // If you hit these asserts, you initialized but forgot to call cleanup().
+ ASSERT(!m_program);
+ ASSERT(!m_vertexShaderId);
+ ASSERT(!m_fragmentShaderId);
+ ASSERT(!m_initialized);
+}
+
+static bool contextLost(WebGraphicsContext3D* context)
+{
+ return (context->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR);
+}
+
+
+void ProgramBindingBase::init(WebGraphicsContext3D* context, const String& vertexShader, const String& fragmentShader)
+{
+ TRACE_EVENT0("cc", "ProgramBindingBase::init");
+ m_vertexShaderId = loadShader(context, GraphicsContext3D::VERTEX_SHADER, vertexShader);
+ if (!m_vertexShaderId) {
+ if (!contextLost(context))
+ LOG_ERROR("Failed to create vertex shader");
+ return;
+ }
+
+ m_fragmentShaderId = loadShader(context, GraphicsContext3D::FRAGMENT_SHADER, fragmentShader);
+ if (!m_fragmentShaderId) {
+ GLC(context, context->deleteShader(m_vertexShaderId));
+ m_vertexShaderId = 0;
+ if (!contextLost(context))
+ LOG_ERROR("Failed to create fragment shader");
+ return;
+ }
+
+ m_program = createShaderProgram(context, m_vertexShaderId, m_fragmentShaderId);
+ ASSERT(m_program || contextLost(context));
+}
+
+void ProgramBindingBase::link(WebGraphicsContext3D* context)
+{
+ GLC(context, context->linkProgram(m_program));
+ cleanupShaders(context);
+#ifndef NDEBUG
+ int linked = 0;
+ GLC(context, context->getProgramiv(m_program, GraphicsContext3D::LINK_STATUS, &linked));
+ if (!linked) {
+ if (!contextLost(context))
+ LOG_ERROR("Failed to link shader program");
+ GLC(context, context->deleteProgram(m_program));
+ return;
+ }
+#endif
+}
+
+void ProgramBindingBase::cleanup(WebGraphicsContext3D* context)
+{
+ m_initialized = false;
+ if (!m_program)
+ return;
+
+ ASSERT(context);
+ GLC(context, context->deleteProgram(m_program));
+ m_program = 0;
+
+ cleanupShaders(context);
+}
+
+unsigned ProgramBindingBase::loadShader(WebGraphicsContext3D* context, unsigned type, const String& shaderSource)
+{
+ unsigned shader = context->createShader(type);
+ if (!shader)
+ return 0;
+ String sourceString(shaderSource);
+ GLC(context, context->shaderSource(shader, sourceString.utf8().data()));
+ GLC(context, context->compileShader(shader));
+#ifndef NDEBUG
+ int compiled = 0;
+ GLC(context, context->getShaderiv(shader, GraphicsContext3D::COMPILE_STATUS, &compiled));
+ if (!compiled) {
+ GLC(context, context->deleteShader(shader));
+ return 0;
+ }
+#endif
+ return shader;
+}
+
+unsigned ProgramBindingBase::createShaderProgram(WebGraphicsContext3D* context, unsigned vertexShader, unsigned fragmentShader)
+{
+ unsigned programObject = context->createProgram();
+ if (!programObject) {
+ if (!contextLost(context))
+ LOG_ERROR("Failed to create shader program");
+ return 0;
+ }
+
+ GLC(context, context->attachShader(programObject, vertexShader));
+ GLC(context, context->attachShader(programObject, fragmentShader));
+
+ // Bind the common attrib locations.
+ GLC(context, context->bindAttribLocation(programObject, GeometryBinding::positionAttribLocation(), "a_position"));
+ GLC(context, context->bindAttribLocation(programObject, GeometryBinding::texCoordAttribLocation(), "a_texCoord"));
+
+ return programObject;
+}
+
+void ProgramBindingBase::cleanupShaders(WebGraphicsContext3D* context)
+{
+ if (m_vertexShaderId) {
+ GLC(context, context->deleteShader(m_vertexShaderId));
+ m_vertexShaderId = 0;
+ }
+ if (m_fragmentShaderId) {
+ GLC(context, context->deleteShader(m_fragmentShaderId));
+ m_fragmentShaderId = 0;
+ }
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/ProgramBinding.h b/cc/ProgramBinding.h
new file mode 100644
index 0000000..fdf0063
--- /dev/null
+++ b/cc/ProgramBinding.h
@@ -0,0 +1,84 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ProgramBinding_h
+#define ProgramBinding_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class ProgramBindingBase {
+public:
+ ProgramBindingBase();
+ ~ProgramBindingBase();
+
+ void init(WebKit::WebGraphicsContext3D*, const String& vertexShader, const String& fragmentShader);
+ void link(WebKit::WebGraphicsContext3D*);
+ void cleanup(WebKit::WebGraphicsContext3D*);
+
+ unsigned program() const { ASSERT(m_initialized); return m_program; }
+ bool initialized() const { return m_initialized; }
+
+protected:
+
+ unsigned loadShader(WebKit::WebGraphicsContext3D*, unsigned type, const String& shaderSource);
+ unsigned createShaderProgram(WebKit::WebGraphicsContext3D*, unsigned vertexShader, unsigned fragmentShader);
+ void cleanupShaders(WebKit::WebGraphicsContext3D*);
+
+ unsigned m_program;
+ unsigned m_vertexShaderId;
+ unsigned m_fragmentShaderId;
+ bool m_initialized;
+};
+
+template<class VertexShader, class FragmentShader>
+class ProgramBinding : public ProgramBindingBase {
+public:
+ explicit ProgramBinding(WebKit::WebGraphicsContext3D* context)
+ {
+ ProgramBindingBase::init(context, m_vertexShader.getShaderString(), m_fragmentShader.getShaderString());
+ }
+
+ void initialize(WebKit::WebGraphicsContext3D* context, bool usingBindUniform)
+ {
+ ASSERT(context);
+ ASSERT(m_program);
+ ASSERT(!m_initialized);
+
+ // Need to bind uniforms before linking
+ if (!usingBindUniform)
+ link(context);
+
+ int baseUniformIndex = 0;
+ m_vertexShader.init(context, m_program, usingBindUniform, &baseUniformIndex);
+ m_fragmentShader.init(context, m_program, usingBindUniform, &baseUniformIndex);
+
+ // Link after binding uniforms
+ if (usingBindUniform)
+ link(context);
+
+ m_initialized = true;
+ }
+
+ const VertexShader& vertexShader() const { return m_vertexShader; }
+ const FragmentShader& fragmentShader() const { return m_fragmentShader; }
+
+private:
+
+ VertexShader m_vertexShader;
+ FragmentShader m_fragmentShader;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/README b/cc/README
index da4767a..7b94aea 100644
--- a/cc/README
+++ b/cc/README
@@ -1,2 +1,17 @@
-The chromium compositor code will go here. For now, this directory is just a
-placeholder to make gclient happy.
+This is the chromium compositor implementation. Currently it's in state of
+transition from the WebKit repository to chromium, so there are only stubs
+here that are not compiled in by default.
+
+To try this out, do the following:
+
+0.) Run the 'copyfiles.py' script in this directory to sync the cc files from
+ their current location in WebKit into this directory.
+1.) Set the gyp variable 'use_libcc_for_compositor=1' and run gyp:
+ ./build/gyp_chromium -Duse_libcc_for_compositor=1
+2.) Build the 'cc' target to build just the compositor library, or build
+ 'cc_unittests' for the unit tests.
+
+Notes about the component=shared_library build:
+Because the compositor currently depends on non-exported symbols from inside
+WebKit, in the shared library build the cc library links into WebKit.dll.
+The unit tests don't currently work at all in the component build.
diff --git a/cc/RateLimiter.cpp b/cc/RateLimiter.cpp
new file mode 100644
index 0000000..910a33c
--- /dev/null
+++ b/cc/RateLimiter.cpp
@@ -0,0 +1,86 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RateLimiter.h"
+
+#include "CCProxy.h"
+#include "CCThread.h"
+#include "TraceEvent.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+class RateLimiter::Task : public CCThread::Task {
+public:
+ static PassOwnPtr<Task> create(RateLimiter* rateLimiter)
+ {
+ return adoptPtr(new Task(rateLimiter));
+ }
+ virtual ~Task() { }
+
+private:
+ explicit Task(RateLimiter* rateLimiter)
+ : CCThread::Task(this)
+ , m_rateLimiter(rateLimiter)
+ {
+ }
+
+ virtual void performTask() OVERRIDE
+ {
+ m_rateLimiter->rateLimitContext();
+ }
+
+ RefPtr<RateLimiter> m_rateLimiter;
+};
+
+PassRefPtr<RateLimiter> RateLimiter::create(WebKit::WebGraphicsContext3D* context, RateLimiterClient *client)
+{
+ return adoptRef(new RateLimiter(context, client));
+}
+
+RateLimiter::RateLimiter(WebKit::WebGraphicsContext3D* context, RateLimiterClient *client)
+ : m_context(context)
+ , m_active(false)
+ , m_client(client)
+{
+ ASSERT(context);
+}
+
+RateLimiter::~RateLimiter()
+{
+}
+
+void RateLimiter::start()
+{
+ if (m_active)
+ return;
+
+ TRACE_EVENT0("cc", "RateLimiter::start");
+ m_active = true;
+ CCProxy::mainThread()->postTask(RateLimiter::Task::create(this));
+}
+
+void RateLimiter::stop()
+{
+ TRACE_EVENT0("cc", "RateLimiter::stop");
+ m_client = 0;
+}
+
+void RateLimiter::rateLimitContext()
+{
+ if (!m_client)
+ return;
+
+ TRACE_EVENT0("cc", "RateLimiter::rateLimitContext");
+
+ m_active = false;
+ m_client->rateLimit();
+ m_context->rateLimitOffscreenContextCHROMIUM();
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/RateLimiter.h b/cc/RateLimiter.h
new file mode 100644
index 0000000..8cb8a1d
--- /dev/null
+++ b/cc/RateLimiter.h
@@ -0,0 +1,53 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RateLimiter_h
+#define RateLimiter_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class RateLimiterClient {
+public:
+ virtual void rateLimit() = 0;
+};
+
+// A RateLimiter can be used to make sure that a single context does not dominate all execution time.
+// To use, construct a RateLimiter class around the context and call start() whenever calls are made on the
+// context outside of normal flow control. RateLimiter will block if the context is too far ahead of the
+// compositor.
+class RateLimiter : public RefCounted<RateLimiter> {
+public:
+ static PassRefPtr<RateLimiter> create(WebKit::WebGraphicsContext3D*, RateLimiterClient*);
+ ~RateLimiter();
+
+ void start();
+
+ // Context and client will not be accessed after stop().
+ void stop();
+
+private:
+ RateLimiter(WebKit::WebGraphicsContext3D*, RateLimiterClient*);
+
+ class Task;
+ friend class Task;
+ void rateLimitContext();
+
+ WebKit::WebGraphicsContext3D* m_context;
+ bool m_active;
+ RateLimiterClient *m_client;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/RenderSurfaceChromium.cpp b/cc/RenderSurfaceChromium.cpp
new file mode 100644
index 0000000..7f207c4
--- /dev/null
+++ b/cc/RenderSurfaceChromium.cpp
@@ -0,0 +1,43 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "RenderSurfaceChromium.h"
+
+#include "CCMathUtil.h"
+#include "LayerChromium.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/text/CString.h>
+
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+RenderSurfaceChromium::RenderSurfaceChromium(LayerChromium* owningLayer)
+ : m_owningLayer(owningLayer)
+ , m_drawOpacity(1)
+ , m_drawOpacityIsAnimating(false)
+ , m_targetSurfaceTransformsAreAnimating(false)
+ , m_screenSpaceTransformsAreAnimating(false)
+ , m_nearestAncestorThatMovesPixels(0)
+{
+}
+
+RenderSurfaceChromium::~RenderSurfaceChromium()
+{
+}
+
+FloatRect RenderSurfaceChromium::drawableContentRect() const
+{
+ FloatRect drawableContentRect = CCMathUtil::mapClippedRect(m_drawTransform, m_contentRect);
+ if (m_owningLayer->hasReplica())
+ drawableContentRect.unite(CCMathUtil::mapClippedRect(m_replicaDrawTransform, m_contentRect));
+ return drawableContentRect;
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/RenderSurfaceChromium.h b/cc/RenderSurfaceChromium.h
new file mode 100644
index 0000000..bd27b1c
--- /dev/null
+++ b/cc/RenderSurfaceChromium.h
@@ -0,0 +1,99 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef RenderSurfaceChromium_h
+#define RenderSurfaceChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include <public/WebTransformationMatrix.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class LayerChromium;
+
+class RenderSurfaceChromium {
+ WTF_MAKE_NONCOPYABLE(RenderSurfaceChromium);
+public:
+ explicit RenderSurfaceChromium(LayerChromium*);
+ ~RenderSurfaceChromium();
+
+ // Returns the rect that encloses the RenderSurface including any reflection.
+ FloatRect drawableContentRect() const;
+
+ const IntRect& contentRect() const { return m_contentRect; }
+ void setContentRect(const IntRect& contentRect) { m_contentRect = contentRect; }
+
+ float drawOpacity() const { return m_drawOpacity; }
+ void setDrawOpacity(float drawOpacity) { m_drawOpacity = drawOpacity; }
+
+ bool drawOpacityIsAnimating() const { return m_drawOpacityIsAnimating; }
+ void setDrawOpacityIsAnimating(bool drawOpacityIsAnimating) { m_drawOpacityIsAnimating = drawOpacityIsAnimating; }
+
+ // This goes from content space with the origin in the center of the rect being transformed to the target space with the origin in the top left of the
+ // rect being transformed. Position the rect so that the origin is in the center of it before applying this transform.
+ const WebKit::WebTransformationMatrix& drawTransform() const { return m_drawTransform; }
+ void setDrawTransform(const WebKit::WebTransformationMatrix& drawTransform) { m_drawTransform = drawTransform; }
+
+ const WebKit::WebTransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
+ void setScreenSpaceTransform(const WebKit::WebTransformationMatrix& screenSpaceTransform) { m_screenSpaceTransform = screenSpaceTransform; }
+
+ const WebKit::WebTransformationMatrix& replicaDrawTransform() const { return m_replicaDrawTransform; }
+ void setReplicaDrawTransform(const WebKit::WebTransformationMatrix& replicaDrawTransform) { m_replicaDrawTransform = replicaDrawTransform; }
+
+ const WebKit::WebTransformationMatrix& replicaScreenSpaceTransform() const { return m_replicaScreenSpaceTransform; }
+ void setReplicaScreenSpaceTransform(const WebKit::WebTransformationMatrix& replicaScreenSpaceTransform) { m_replicaScreenSpaceTransform = replicaScreenSpaceTransform; }
+
+ bool targetSurfaceTransformsAreAnimating() const { return m_targetSurfaceTransformsAreAnimating; }
+ void setTargetSurfaceTransformsAreAnimating(bool animating) { m_targetSurfaceTransformsAreAnimating = animating; }
+ bool screenSpaceTransformsAreAnimating() const { return m_screenSpaceTransformsAreAnimating; }
+ void setScreenSpaceTransformsAreAnimating(bool animating) { m_screenSpaceTransformsAreAnimating = animating; }
+
+ const IntRect& clipRect() const { return m_clipRect; }
+ void setClipRect(const IntRect& clipRect) { m_clipRect = clipRect; }
+
+ void clearLayerList() { m_layerList.clear(); }
+ Vector<RefPtr<LayerChromium> >& layerList() { return m_layerList; }
+
+ void setNearestAncestorThatMovesPixels(RenderSurfaceChromium* surface) { m_nearestAncestorThatMovesPixels = surface; }
+ const RenderSurfaceChromium* nearestAncestorThatMovesPixels() const { return m_nearestAncestorThatMovesPixels; }
+
+private:
+ LayerChromium* m_owningLayer;
+
+ // Uses this surface's space.
+ IntRect m_contentRect;
+
+ float m_drawOpacity;
+ bool m_drawOpacityIsAnimating;
+ WebKit::WebTransformationMatrix m_drawTransform;
+ WebKit::WebTransformationMatrix m_screenSpaceTransform;
+ WebKit::WebTransformationMatrix m_replicaDrawTransform;
+ WebKit::WebTransformationMatrix m_replicaScreenSpaceTransform;
+ bool m_targetSurfaceTransformsAreAnimating;
+ bool m_screenSpaceTransformsAreAnimating;
+
+ // Uses the space of the surface's target surface.
+ IntRect m_clipRect;
+
+ Vector<RefPtr<LayerChromium> > m_layerList;
+
+ // The nearest ancestor target surface that will contain the contents of this surface, and that is going
+ // to move pixels within the surface (such as with a blur). This can point to itself.
+ RenderSurfaceChromium* m_nearestAncestorThatMovesPixels;
+
+ // For CCLayerIteratorActions
+ int m_targetRenderSurfaceLayerIndexHistory;
+ int m_currentLayerIndexHistory;
+ friend struct CCLayerIteratorActions;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/ScrollbarLayerChromium.cpp b/cc/ScrollbarLayerChromium.cpp
new file mode 100644
index 0000000..2584a26
--- /dev/null
+++ b/cc/ScrollbarLayerChromium.cpp
@@ -0,0 +1,261 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ScrollbarLayerChromium.h"
+
+#include "BitmapCanvasLayerTextureUpdater.h"
+#include "CCLayerTreeHost.h"
+#include "CCScrollbarLayerImpl.h"
+#include "CCTextureUpdateQueue.h"
+#include "LayerPainterChromium.h"
+#include <public/WebRect.h>
+
+using WebKit::WebRect;
+
+namespace WebCore {
+
+PassOwnPtr<CCLayerImpl> ScrollbarLayerChromium::createCCLayerImpl()
+{
+ return CCScrollbarLayerImpl::create(id());
+}
+
+PassRefPtr<ScrollbarLayerChromium> ScrollbarLayerChromium::create(PassOwnPtr<WebKit::WebScrollbar> scrollbar, WebKit::WebScrollbarThemePainter painter, PassOwnPtr<WebKit::WebScrollbarThemeGeometry> geometry, int scrollLayerId)
+{
+ return adoptRef(new ScrollbarLayerChromium(scrollbar, painter, geometry, scrollLayerId));
+}
+
+ScrollbarLayerChromium::ScrollbarLayerChromium(PassOwnPtr<WebKit::WebScrollbar> scrollbar, WebKit::WebScrollbarThemePainter painter, PassOwnPtr<WebKit::WebScrollbarThemeGeometry> geometry, int scrollLayerId)
+ : m_scrollbar(scrollbar)
+ , m_painter(painter)
+ , m_geometry(geometry)
+ , m_scrollLayerId(scrollLayerId)
+ , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
+{
+}
+
+void ScrollbarLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
+{
+ LayerChromium::pushPropertiesTo(layer);
+
+ CCScrollbarLayerImpl* scrollbarLayer = static_cast<CCScrollbarLayerImpl*>(layer);
+
+ if (!scrollbarLayer->scrollbarGeometry())
+ scrollbarLayer->setScrollbarGeometry(adoptPtr(m_geometry->clone()));
+
+ scrollbarLayer->setScrollbarData(m_scrollbar.get());
+
+ if (m_backTrack && m_backTrack->texture()->haveBackingTexture())
+ scrollbarLayer->setBackTrackResourceId(m_backTrack->texture()->resourceId());
+ else
+ scrollbarLayer->setBackTrackResourceId(0);
+
+ if (m_foreTrack && m_foreTrack->texture()->haveBackingTexture())
+ scrollbarLayer->setForeTrackResourceId(m_foreTrack->texture()->resourceId());
+ else
+ scrollbarLayer->setForeTrackResourceId(0);
+
+ if (m_thumb && m_thumb->texture()->haveBackingTexture())
+ scrollbarLayer->setThumbResourceId(m_thumb->texture()->resourceId());
+ else
+ scrollbarLayer->setThumbResourceId(0);
+}
+
+class ScrollbarBackgroundPainter : public LayerPainterChromium {
+ WTF_MAKE_NONCOPYABLE(ScrollbarBackgroundPainter);
+public:
+ static PassOwnPtr<ScrollbarBackgroundPainter> create(WebKit::WebScrollbar* scrollbar, WebKit::WebScrollbarThemePainter painter, WebKit::WebScrollbarThemeGeometry* geometry, WebKit::WebScrollbar::ScrollbarPart trackPart)
+ {
+ return adoptPtr(new ScrollbarBackgroundPainter(scrollbar, painter, geometry, trackPart));
+ }
+
+ virtual void paint(SkCanvas* skCanvas, const IntRect& contentRect, FloatRect&) OVERRIDE
+ {
+ WebKit::WebCanvas* canvas = skCanvas;
+ // The following is a simplification of ScrollbarThemeComposite::paint.
+ WebKit::WebRect contentWebRect(contentRect.x(), contentRect.y(), contentRect.width(), contentRect.height());
+ m_painter.paintScrollbarBackground(canvas, contentWebRect);
+
+ if (m_geometry->hasButtons(m_scrollbar)) {
+ WebRect backButtonStartPaintRect = m_geometry->backButtonStartRect(m_scrollbar);
+ m_painter.paintBackButtonStart(canvas, backButtonStartPaintRect);
+
+ WebRect backButtonEndPaintRect = m_geometry->backButtonEndRect(m_scrollbar);
+ m_painter.paintBackButtonEnd(canvas, backButtonEndPaintRect);
+
+ WebRect forwardButtonStartPaintRect = m_geometry->forwardButtonStartRect(m_scrollbar);
+ m_painter.paintForwardButtonStart(canvas, forwardButtonStartPaintRect);
+
+ WebRect forwardButtonEndPaintRect = m_geometry->forwardButtonEndRect(m_scrollbar);
+ m_painter.paintForwardButtonEnd(canvas, forwardButtonEndPaintRect);
+ }
+
+ WebRect trackPaintRect = m_geometry->trackRect(m_scrollbar);
+ m_painter.paintTrackBackground(canvas, trackPaintRect);
+
+ bool thumbPresent = m_geometry->hasThumb(m_scrollbar);
+ if (thumbPresent) {
+ if (m_trackPart == WebKit::WebScrollbar::ForwardTrackPart)
+ m_painter.paintForwardTrackPart(canvas, trackPaintRect);
+ else
+ m_painter.paintBackTrackPart(canvas, trackPaintRect);
+ }
+
+ m_painter.paintTickmarks(canvas, trackPaintRect);
+ }
+private:
+ ScrollbarBackgroundPainter(WebKit::WebScrollbar* scrollbar, WebKit::WebScrollbarThemePainter painter, WebKit::WebScrollbarThemeGeometry* geometry, WebKit::WebScrollbar::ScrollbarPart trackPart)
+ : m_scrollbar(scrollbar)
+ , m_painter(painter)
+ , m_geometry(geometry)
+ , m_trackPart(trackPart)
+ {
+ }
+
+ WebKit::WebScrollbar* m_scrollbar;
+ WebKit::WebScrollbarThemePainter m_painter;
+ WebKit::WebScrollbarThemeGeometry* m_geometry;
+ WebKit::WebScrollbar::ScrollbarPart m_trackPart;
+};
+
+class ScrollbarThumbPainter : public LayerPainterChromium {
+ WTF_MAKE_NONCOPYABLE(ScrollbarThumbPainter);
+public:
+ static PassOwnPtr<ScrollbarThumbPainter> create(WebKit::WebScrollbar* scrollbar, WebKit::WebScrollbarThemePainter painter, WebKit::WebScrollbarThemeGeometry* geometry)
+ {
+ return adoptPtr(new ScrollbarThumbPainter(scrollbar, painter, geometry));
+ }
+
+ virtual void paint(SkCanvas* skCanvas, const IntRect& contentRect, FloatRect& opaque) OVERRIDE
+ {
+ WebKit::WebCanvas* canvas = skCanvas;
+
+ // Consider the thumb to be at the origin when painting.
+ WebRect thumbRect = m_geometry->thumbRect(m_scrollbar);
+ thumbRect.x = 0;
+ thumbRect.y = 0;
+ m_painter.paintThumb(canvas, thumbRect);
+ }
+
+private:
+ ScrollbarThumbPainter(WebKit::WebScrollbar* scrollbar, WebKit::WebScrollbarThemePainter painter, WebKit::WebScrollbarThemeGeometry* geometry)
+ : m_scrollbar(scrollbar)
+ , m_painter(painter)
+ , m_geometry(geometry)
+ {
+ }
+
+ WebKit::WebScrollbar* m_scrollbar;
+ WebKit::WebScrollbarThemePainter m_painter;
+ WebKit::WebScrollbarThemeGeometry* m_geometry;
+};
+
+void ScrollbarLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
+{
+ if (!host || host != layerTreeHost()) {
+ m_backTrackUpdater.clear();
+ m_backTrack.clear();
+ m_thumbUpdater.clear();
+ m_thumb.clear();
+ }
+
+ LayerChromium::setLayerTreeHost(host);
+}
+
+void ScrollbarLayerChromium::createTextureUpdaterIfNeeded()
+{
+ m_textureFormat = layerTreeHost()->rendererCapabilities().bestTextureFormat;
+
+ if (!m_backTrackUpdater)
+ m_backTrackUpdater = BitmapCanvasLayerTextureUpdater::create(ScrollbarBackgroundPainter::create(m_scrollbar.get(), m_painter, m_geometry.get(), WebKit::WebScrollbar::BackTrackPart));
+ if (!m_backTrack)
+ m_backTrack = m_backTrackUpdater->createTexture(layerTreeHost()->contentsTextureManager());
+
+ // Only create two-part track if we think the two parts could be different in appearance.
+ if (m_scrollbar->isCustomScrollbar()) {
+ if (!m_foreTrackUpdater)
+ m_foreTrackUpdater = BitmapCanvasLayerTextureUpdater::create(ScrollbarBackgroundPainter::create(m_scrollbar.get(), m_painter, m_geometry.get(), WebKit::WebScrollbar::ForwardTrackPart));
+ if (!m_foreTrack)
+ m_foreTrack = m_foreTrackUpdater->createTexture(layerTreeHost()->contentsTextureManager());
+ }
+
+ if (!m_thumbUpdater)
+ m_thumbUpdater = BitmapCanvasLayerTextureUpdater::create(ScrollbarThumbPainter::create(m_scrollbar.get(), m_painter, m_geometry.get()));
+ if (!m_thumb)
+ m_thumb = m_thumbUpdater->createTexture(layerTreeHost()->contentsTextureManager());
+}
+
+void ScrollbarLayerChromium::updatePart(LayerTextureUpdater* painter, LayerTextureUpdater::Texture* texture, const IntRect& rect, CCTextureUpdateQueue& queue, CCRenderingStats& stats)
+{
+ // Skip painting and uploading if there are no invalidations and
+ // we already have valid texture data.
+ if (texture->texture()->haveBackingTexture()
+ && texture->texture()->size() == rect.size()
+ && m_updateRect.isEmpty())
+ return;
+
+ // We should always have enough memory for UI.
+ ASSERT(texture->texture()->canAcquireBackingTexture());
+ if (!texture->texture()->canAcquireBackingTexture())
+ return;
+
+ // Paint and upload the entire part.
+ IntRect paintedOpaqueRect;
+ painter->prepareToUpdate(rect, rect.size(), 1, 1, paintedOpaqueRect, stats);
+ texture->prepareRect(rect, stats);
+
+ IntSize destOffset(0, 0);
+ TextureUploader::Parameters upload = { texture, rect, destOffset };
+ queue.appendFullUpload(upload);
+}
+
+
+void ScrollbarLayerChromium::setTexturePriorities(const CCPriorityCalculator&)
+{
+ if (contentBounds().isEmpty())
+ return;
+
+ createTextureUpdaterIfNeeded();
+
+ bool drawsToRoot = !renderTarget()->parent();
+ if (m_backTrack) {
+ m_backTrack->texture()->setDimensions(contentBounds(), m_textureFormat);
+ m_backTrack->texture()->setRequestPriority(CCPriorityCalculator::uiPriority(drawsToRoot));
+ }
+ if (m_foreTrack) {
+ m_foreTrack->texture()->setDimensions(contentBounds(), m_textureFormat);
+ m_foreTrack->texture()->setRequestPriority(CCPriorityCalculator::uiPriority(drawsToRoot));
+ }
+ if (m_thumb) {
+ WebKit::WebRect thumbRect = m_geometry->thumbRect(m_scrollbar.get());
+ m_thumb->texture()->setDimensions(IntSize(thumbRect.width, thumbRect.height), m_textureFormat);
+ m_thumb->texture()->setRequestPriority(CCPriorityCalculator::uiPriority(drawsToRoot));
+ }
+}
+
+void ScrollbarLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker*, CCRenderingStats& stats)
+{
+ if (contentBounds().isEmpty())
+ return;
+
+ createTextureUpdaterIfNeeded();
+
+ IntPoint scrollbarOrigin(m_scrollbar->location().x, m_scrollbar->location().y);
+ IntRect contentRect(scrollbarOrigin, contentBounds());
+ updatePart(m_backTrackUpdater.get(), m_backTrack.get(), contentRect, queue, stats);
+ if (m_foreTrack && m_foreTrackUpdater)
+ updatePart(m_foreTrackUpdater.get(), m_foreTrack.get(), contentRect, queue, stats);
+
+ // Consider the thumb to be at the origin when painting.
+ WebKit::WebRect thumbRect = m_geometry->thumbRect(m_scrollbar.get());
+ IntRect originThumbRect = IntRect(0, 0, thumbRect.width, thumbRect.height);
+ if (!originThumbRect.isEmpty())
+ updatePart(m_thumbUpdater.get(), m_thumb.get(), originThumbRect, queue, stats);
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/ScrollbarLayerChromium.h b/cc/ScrollbarLayerChromium.h
new file mode 100644
index 0000000..f5f7d78
--- /dev/null
+++ b/cc/ScrollbarLayerChromium.h
@@ -0,0 +1,66 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef ScrollbarLayerChromium_h
+#define ScrollbarLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerChromium.h"
+#include "LayerTextureUpdater.h"
+#include <public/WebScrollbar.h>
+#include <public/WebScrollbarThemeGeometry.h>
+#include <public/WebScrollbarThemePainter.h>
+
+namespace WebCore {
+
+class Scrollbar;
+class ScrollbarThemeComposite;
+class CCTextureUpdateQueue;
+
+class ScrollbarLayerChromium : public LayerChromium {
+public:
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+ static PassRefPtr<ScrollbarLayerChromium> create(PassOwnPtr<WebKit::WebScrollbar>, WebKit::WebScrollbarThemePainter, PassOwnPtr<WebKit::WebScrollbarThemeGeometry>, int scrollLayerId);
+
+ // LayerChromium interface
+ virtual void setTexturePriorities(const CCPriorityCalculator&) OVERRIDE;
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual void setLayerTreeHost(CCLayerTreeHost*) OVERRIDE;
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+
+ int scrollLayerId() const { return m_scrollLayerId; }
+ void setScrollLayerId(int id) { m_scrollLayerId = id; }
+
+ virtual ScrollbarLayerChromium* toScrollbarLayerChromium() OVERRIDE { return this; }
+
+protected:
+ ScrollbarLayerChromium(PassOwnPtr<WebKit::WebScrollbar>, WebKit::WebScrollbarThemePainter, PassOwnPtr<WebKit::WebScrollbarThemeGeometry>, int scrollLayerId);
+
+private:
+ void updatePart(LayerTextureUpdater*, LayerTextureUpdater::Texture*, const IntRect&, CCTextureUpdateQueue&, CCRenderingStats&);
+ void createTextureUpdaterIfNeeded();
+
+ OwnPtr<WebKit::WebScrollbar> m_scrollbar;
+ WebKit::WebScrollbarThemePainter m_painter;
+ OwnPtr<WebKit::WebScrollbarThemeGeometry> m_geometry;
+ int m_scrollLayerId;
+
+ GC3Denum m_textureFormat;
+
+ RefPtr<LayerTextureUpdater> m_backTrackUpdater;
+ RefPtr<LayerTextureUpdater> m_foreTrackUpdater;
+ RefPtr<LayerTextureUpdater> m_thumbUpdater;
+
+ // All the parts of the scrollbar except the thumb
+ OwnPtr<LayerTextureUpdater::Texture> m_backTrack;
+ OwnPtr<LayerTextureUpdater::Texture> m_foreTrack;
+ OwnPtr<LayerTextureUpdater::Texture> m_thumb;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/ShaderChromium.cpp b/cc/ShaderChromium.cpp
new file mode 100644
index 0000000..270a11d
--- /dev/null
+++ b/cc/ShaderChromium.cpp
@@ -0,0 +1,884 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "ShaderChromium.h"
+
+#include <public/WebGraphicsContext3D.h>
+
+#define SHADER0(Src) #Src
+#define SHADER(Src) SHADER0(Src)
+
+using WebKit::WebGraphicsContext3D;
+
+namespace WebCore {
+
+namespace {
+
+static void getProgramUniformLocations(WebGraphicsContext3D* context, unsigned program, const char** shaderUniforms, size_t count, size_t maxLocations, int* locations, bool usingBindUniform, int* baseUniformIndex)
+{
+ for (size_t uniformIndex = 0; uniformIndex < count; uniformIndex ++) {
+ ASSERT(uniformIndex < maxLocations);
+
+ if (usingBindUniform) {
+ locations[uniformIndex] = (*baseUniformIndex)++;
+ context->bindUniformLocationCHROMIUM(program, locations[uniformIndex], shaderUniforms[uniformIndex]);
+ } else
+ locations[uniformIndex] = context->getUniformLocation(program, shaderUniforms[uniformIndex]);
+ }
+}
+
+}
+
+VertexShaderPosTex::VertexShaderPosTex()
+ : m_matrixLocation(-1)
+{
+}
+
+void VertexShaderPosTex::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ ASSERT(m_matrixLocation != -1);
+}
+
+String VertexShaderPosTex::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = a_texCoord;
+ }
+ );
+}
+
+VertexShaderPosTexYUVStretch::VertexShaderPosTexYUVStretch()
+ : m_matrixLocation(-1)
+ , m_yWidthScaleFactorLocation(-1)
+ , m_uvWidthScaleFactorLocation(-1)
+{
+}
+
+void VertexShaderPosTexYUVStretch::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "y_widthScaleFactor",
+ "uv_widthScaleFactor",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_yWidthScaleFactorLocation = locations[1];
+ m_uvWidthScaleFactorLocation = locations[2];
+ ASSERT(m_matrixLocation != -1 && m_yWidthScaleFactorLocation != -1 && m_uvWidthScaleFactorLocation != -1);
+}
+
+String VertexShaderPosTexYUVStretch::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ varying vec2 y_texCoord;
+ varying vec2 uv_texCoord;
+ uniform float y_widthScaleFactor;
+ uniform float uv_widthScaleFactor;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ y_texCoord = vec2(y_widthScaleFactor * a_texCoord.x, a_texCoord.y);
+ uv_texCoord = vec2(uv_widthScaleFactor * a_texCoord.x, a_texCoord.y);
+ }
+ );
+}
+
+VertexShaderPos::VertexShaderPos()
+ : m_matrixLocation(-1)
+{
+}
+
+void VertexShaderPos::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ ASSERT(m_matrixLocation != -1);
+}
+
+String VertexShaderPos::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ uniform mat4 matrix;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ }
+ );
+}
+
+VertexShaderPosTexTransform::VertexShaderPosTexTransform()
+ : m_matrixLocation(-1)
+ , m_texTransformLocation(-1)
+{
+}
+
+void VertexShaderPosTexTransform::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "texTransform",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_texTransformLocation = locations[1];
+ ASSERT(m_matrixLocation != -1 && m_texTransformLocation != -1);
+}
+
+String VertexShaderPosTexTransform::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform vec4 texTransform;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = a_texCoord * texTransform.zw + texTransform.xy;
+ }
+ );
+}
+
+VertexShaderQuad::VertexShaderQuad()
+ : m_matrixLocation(-1)
+ , m_pointLocation(-1)
+{
+}
+
+String VertexShaderPosTexIdentity::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = a_position;
+ v_texCoord = (a_position.xy + vec2(1.0)) * 0.5;
+ }
+ );
+}
+
+void VertexShaderQuad::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "point",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_pointLocation = locations[1];
+ ASSERT(m_matrixLocation != -1 && m_pointLocation != -1);
+}
+
+String VertexShaderQuad::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform vec2 point[4];
+ varying vec2 v_texCoord;
+ void main()
+ {
+ vec2 complement = abs(a_texCoord - 1.0);
+ vec4 pos = vec4(0.0, 0.0, a_position.z, a_position.w);
+ pos.xy += (complement.x * complement.y) * point[0];
+ pos.xy += (a_texCoord.x * complement.y) * point[1];
+ pos.xy += (a_texCoord.x * a_texCoord.y) * point[2];
+ pos.xy += (complement.x * a_texCoord.y) * point[3];
+ gl_Position = matrix * pos;
+ v_texCoord = pos.xy + vec2(0.5);
+ }
+ );
+}
+
+VertexShaderTile::VertexShaderTile()
+ : m_matrixLocation(-1)
+ , m_pointLocation(-1)
+ , m_vertexTexTransformLocation(-1)
+{
+}
+
+void VertexShaderTile::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "point",
+ "vertexTexTransform",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_pointLocation = locations[1];
+ m_vertexTexTransformLocation = locations[2];
+ ASSERT(m_matrixLocation != -1 && m_pointLocation != -1 && m_vertexTexTransformLocation != -1);
+}
+
+String VertexShaderTile::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform vec2 point[4];
+ uniform vec4 vertexTexTransform;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ vec2 complement = abs(a_texCoord - 1.0);
+ vec4 pos = vec4(0.0, 0.0, a_position.z, a_position.w);
+ pos.xy += (complement.x * complement.y) * point[0];
+ pos.xy += (a_texCoord.x * complement.y) * point[1];
+ pos.xy += (a_texCoord.x * a_texCoord.y) * point[2];
+ pos.xy += (complement.x * a_texCoord.y) * point[3];
+ gl_Position = matrix * pos;
+ v_texCoord = pos.xy * vertexTexTransform.zw + vertexTexTransform.xy;
+ }
+ );
+}
+
+VertexShaderVideoTransform::VertexShaderVideoTransform()
+ : m_matrixLocation(-1)
+ , m_texMatrixLocation(-1)
+{
+}
+
+bool VertexShaderVideoTransform::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "texMatrix",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_texMatrixLocation = locations[1];
+ return m_matrixLocation != -1 && m_texMatrixLocation != -1;
+}
+
+String VertexShaderVideoTransform::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform mat4 texMatrix;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = vec2(texMatrix * vec4(a_texCoord.x, 1.0 - a_texCoord.y, 0.0, 1.0));
+ }
+ );
+}
+
+FragmentTexAlphaBinding::FragmentTexAlphaBinding()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+{
+}
+
+void FragmentTexAlphaBinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ ASSERT(m_samplerLocation != -1 && m_alphaLocation != -1);
+}
+
+FragmentTexOpaqueBinding::FragmentTexOpaqueBinding()
+ : m_samplerLocation(-1)
+{
+}
+
+void FragmentTexOpaqueBinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ ASSERT(m_samplerLocation != -1);
+}
+
+String FragmentShaderRGBATexFlipAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y));
+ gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha;
+ }
+ );
+}
+
+bool FragmentShaderOESImageExternal::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ return m_samplerLocation != -1;
+}
+
+String FragmentShaderOESImageExternal::getShaderString() const
+{
+ // Cannot use the SHADER() macro because of the '#' char
+ return "#extension GL_OES_EGL_image_external : require \n"
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord;\n"
+ "uniform samplerExternalOES s_texture;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 texColor = texture2D(s_texture, v_texCoord);\n"
+ " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w);\n"
+ "}\n";
+}
+
+String FragmentShaderRGBATexAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = texColor * alpha;
+ }
+ );
+}
+
+String FragmentShaderRGBATexRectFlipAlpha::getShaderString() const
+{
+ // This must be paired with VertexShaderPosTexTransform to pick up the texTransform uniform.
+ // The necessary #extension preprocessing directive breaks the SHADER and SHADER0 macros.
+ return "#extension GL_ARB_texture_rectangle : require\n"
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord;\n"
+ "uniform vec4 texTransform;\n"
+ "uniform sampler2DRect s_texture;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 texColor = texture2DRect(s_texture, vec2(v_texCoord.x, texTransform.w - v_texCoord.y));\n"
+ " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha;\n"
+ "}\n";
+}
+
+String FragmentShaderRGBATexRectAlpha::getShaderString() const
+{
+ return "#extension GL_ARB_texture_rectangle : require\n"
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord;\n"
+ "uniform sampler2DRect s_texture;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 texColor = texture2DRect(s_texture, v_texCoord);\n"
+ " gl_FragColor = texColor * alpha;\n"
+ "}\n";
+}
+
+String FragmentShaderRGBATexOpaque::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.rgb, 1.0);
+ }
+ );
+}
+
+String FragmentShaderRGBATex::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ gl_FragColor = texture2D(s_texture, v_texCoord);
+ }
+ );
+}
+
+String FragmentShaderRGBATexSwizzleAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha;
+ }
+ );
+}
+
+String FragmentShaderRGBATexSwizzleOpaque::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, 1.0);
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaAA::FragmentShaderRGBATexAlphaAA()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_edgeLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaAA::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ "edge",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ m_edgeLocation = locations[2];
+ ASSERT(m_samplerLocation != -1 && m_alphaLocation != -1 && m_edgeLocation != -1);
+}
+
+String FragmentShaderRGBATexAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = texColor * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentTexClampAlphaAABinding::FragmentTexClampAlphaAABinding()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_fragmentTexTransformLocation(-1)
+ , m_edgeLocation(-1)
+{
+}
+
+void FragmentTexClampAlphaAABinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ "fragmentTexTransform",
+ "edge",
+ };
+ int locations[4];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ m_fragmentTexTransformLocation = locations[2];
+ m_edgeLocation = locations[3];
+ ASSERT(m_samplerLocation != -1 && m_alphaLocation != -1 && m_fragmentTexTransformLocation != -1 && m_edgeLocation != -1);
+}
+
+String FragmentShaderRGBATexClampAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec4 fragmentTexTransform;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw + fragmentTexTransform.xy;
+ vec4 texColor = texture2D(s_texture, texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = texColor * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+String FragmentShaderRGBATexClampSwizzleAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec4 fragmentTexTransform;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw + fragmentTexTransform.xy;
+ vec4 texColor = texture2D(s_texture, texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaMask::FragmentShaderRGBATexAlphaMask()
+ : m_samplerLocation(-1)
+ , m_maskSamplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_maskTexCoordScaleLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaMask::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "s_mask",
+ "alpha",
+ "maskTexCoordScale",
+ "maskTexCoordOffset",
+ };
+ int locations[5];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_maskSamplerLocation = locations[1];
+ m_alphaLocation = locations[2];
+ m_maskTexCoordScaleLocation = locations[3];
+ m_maskTexCoordOffsetLocation = locations[4];
+ ASSERT(m_samplerLocation != -1 && m_maskSamplerLocation != -1 && m_alphaLocation != -1);
+}
+
+String FragmentShaderRGBATexAlphaMask::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform sampler2D s_mask;
+ uniform vec2 maskTexCoordScale;
+ uniform vec2 maskTexCoordOffset;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec2 maskTexCoord = vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);
+ vec4 maskColor = texture2D(s_mask, maskTexCoord);
+ gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w;
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaMaskAA::FragmentShaderRGBATexAlphaMaskAA()
+ : m_samplerLocation(-1)
+ , m_maskSamplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_edgeLocation(-1)
+ , m_maskTexCoordScaleLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaMaskAA::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "s_mask",
+ "alpha",
+ "edge",
+ "maskTexCoordScale",
+ "maskTexCoordOffset",
+ };
+ int locations[6];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_maskSamplerLocation = locations[1];
+ m_alphaLocation = locations[2];
+ m_edgeLocation = locations[3];
+ m_maskTexCoordScaleLocation = locations[4];
+ m_maskTexCoordOffsetLocation = locations[5];
+ ASSERT(m_samplerLocation != -1 && m_maskSamplerLocation != -1 && m_alphaLocation != -1 && m_edgeLocation != -1);
+}
+
+String FragmentShaderRGBATexAlphaMaskAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform sampler2D s_mask;
+ uniform vec2 maskTexCoordScale;
+ uniform vec2 maskTexCoordOffset;
+ uniform float alpha;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec2 maskTexCoord = vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);
+ vec4 maskColor = texture2D(s_mask, maskTexCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentShaderYUVVideo::FragmentShaderYUVVideo()
+ : m_yTextureLocation(-1)
+ , m_uTextureLocation(-1)
+ , m_vTextureLocation(-1)
+ , m_alphaLocation(-1)
+ , m_ccMatrixLocation(-1)
+ , m_yuvAdjLocation(-1)
+{
+}
+
+void FragmentShaderYUVVideo::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "y_texture",
+ "u_texture",
+ "v_texture",
+ "alpha",
+ "cc_matrix",
+ "yuv_adj",
+ };
+ int locations[6];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_yTextureLocation = locations[0];
+ m_uTextureLocation = locations[1];
+ m_vTextureLocation = locations[2];
+ m_alphaLocation = locations[3];
+ m_ccMatrixLocation = locations[4];
+ m_yuvAdjLocation = locations[5];
+
+ ASSERT(m_yTextureLocation != -1 && m_uTextureLocation != -1 && m_vTextureLocation != -1
+ && m_alphaLocation != -1 && m_ccMatrixLocation != -1 && m_yuvAdjLocation != -1);
+}
+
+String FragmentShaderYUVVideo::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ precision mediump int;
+ varying vec2 y_texCoord;
+ varying vec2 uv_texCoord;
+ uniform sampler2D y_texture;
+ uniform sampler2D u_texture;
+ uniform sampler2D v_texture;
+ uniform float alpha;
+ uniform vec3 yuv_adj;
+ uniform mat3 cc_matrix;
+ void main()
+ {
+ float y_raw = texture2D(y_texture, y_texCoord).x;
+ float u_unsigned = texture2D(u_texture, uv_texCoord).x;
+ float v_unsigned = texture2D(v_texture, uv_texCoord).x;
+ vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned) + yuv_adj;
+ vec3 rgb = cc_matrix * yuv;
+ gl_FragColor = vec4(rgb, float(1)) * alpha;
+ }
+ );
+}
+
+FragmentShaderColor::FragmentShaderColor()
+ : m_colorLocation(-1)
+{
+}
+
+void FragmentShaderColor::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "color",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_colorLocation = locations[0];
+ ASSERT(m_colorLocation != -1);
+}
+
+String FragmentShaderColor::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ uniform vec4 color;
+ void main()
+ {
+ gl_FragColor = color;
+ }
+ );
+}
+
+FragmentShaderCheckerboard::FragmentShaderCheckerboard()
+ : m_alphaLocation(-1)
+ , m_texTransformLocation(-1)
+ , m_frequencyLocation(-1)
+{
+}
+
+void FragmentShaderCheckerboard::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "alpha",
+ "texTransform",
+ "frequency",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, WTF_ARRAY_LENGTH(shaderUniforms), WTF_ARRAY_LENGTH(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_alphaLocation = locations[0];
+ m_texTransformLocation = locations[1];
+ m_frequencyLocation = locations[2];
+ ASSERT(m_alphaLocation != -1 && m_texTransformLocation != -1 && m_frequencyLocation != -1);
+}
+
+String FragmentShaderCheckerboard::getShaderString() const
+{
+ // Shader based on Example 13-17 of "OpenGL ES 2.0 Programming Guide"
+ // by Munshi, Ginsburg, Shreiner.
+ return SHADER(
+ precision mediump float;
+ precision mediump int;
+ varying vec2 v_texCoord;
+ uniform float alpha;
+ uniform float frequency;
+ uniform vec4 texTransform;
+ void main()
+ {
+ vec4 color1 = vec4(1.0, 1.0, 1.0, 1.0);
+ vec4 color2 = vec4(0.945, 0.945, 0.945, 1.0);
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * texTransform.zw + texTransform.xy;
+ vec2 coord = mod(floor(texCoord * frequency * 2.0), 2.0);
+ float picker = abs(coord.x - coord.y);
+ gl_FragColor = mix(color1, color2, picker) * alpha;
+ }
+ );
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/ShaderChromium.h b/cc/ShaderChromium.h
new file mode 100644
index 0000000..ffb6767
--- /dev/null
+++ b/cc/ShaderChromium.h
@@ -0,0 +1,351 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ShaderChromium_h
+#define ShaderChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "SkColorPriv.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class VertexShaderPosTex {
+public:
+ VertexShaderPosTex();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+
+private:
+ int m_matrixLocation;
+};
+
+class VertexShaderPosTexYUVStretch {
+public:
+ VertexShaderPosTexYUVStretch();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int yWidthScaleFactorLocation() const { return m_yWidthScaleFactorLocation; }
+ int uvWidthScaleFactorLocation() const { return m_uvWidthScaleFactorLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_yWidthScaleFactorLocation;
+ int m_uvWidthScaleFactorLocation;
+};
+
+class VertexShaderPos {
+public:
+ VertexShaderPos();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+
+private:
+ int m_matrixLocation;
+};
+
+class VertexShaderPosTexIdentity {
+public:
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex) { }
+ String getShaderString() const;
+};
+
+class VertexShaderPosTexTransform {
+public:
+ VertexShaderPosTexTransform();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int texTransformLocation() const { return m_texTransformLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_texTransformLocation;
+};
+
+class VertexShaderQuad {
+public:
+ VertexShaderQuad();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int pointLocation() const { return m_pointLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_pointLocation;
+};
+
+class VertexShaderTile {
+public:
+ VertexShaderTile();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int pointLocation() const { return m_pointLocation; }
+ int vertexTexTransformLocation() const { return m_vertexTexTransformLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_pointLocation;
+ int m_vertexTexTransformLocation;
+};
+
+class VertexShaderVideoTransform {
+public:
+ VertexShaderVideoTransform();
+
+ bool init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int texMatrixLocation() const { return m_texMatrixLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_texMatrixLocation;
+};
+
+class FragmentTexAlphaBinding {
+public:
+ FragmentTexAlphaBinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int edgeLocation() const { return -1; }
+ int fragmentTexTransformLocation() const { return -1; }
+ int samplerLocation() const { return m_samplerLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+};
+
+class FragmentTexOpaqueBinding {
+public:
+ FragmentTexOpaqueBinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return -1; }
+ int edgeLocation() const { return -1; }
+ int fragmentTexTransformLocation() const { return -1; }
+ int samplerLocation() const { return m_samplerLocation; }
+
+private:
+ int m_samplerLocation;
+};
+
+class FragmentShaderRGBATexFlipAlpha : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATexAlpha : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATexRectFlipAlpha : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATexRectAlpha : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATexOpaque : public FragmentTexOpaqueBinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATex : public FragmentTexOpaqueBinding {
+public:
+ String getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel with alpha.
+class FragmentShaderRGBATexSwizzleAlpha : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel without alpha.
+class FragmentShaderRGBATexSwizzleOpaque : public FragmentTexOpaqueBinding {
+public:
+ String getShaderString() const;
+};
+
+// Fragment shader for external textures.
+class FragmentShaderOESImageExternal : public FragmentTexAlphaBinding {
+public:
+ String getShaderString() const;
+ bool init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+private:
+ int m_samplerLocation;
+};
+
+class FragmentShaderRGBATexAlphaAA {
+public:
+ FragmentShaderRGBATexAlphaAA();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ String getShaderString() const;
+
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+ int m_edgeLocation;
+};
+
+class FragmentTexClampAlphaAABinding {
+public:
+ FragmentTexClampAlphaAABinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int fragmentTexTransformLocation() const { return m_fragmentTexTransformLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+ int m_fragmentTexTransformLocation;
+ int m_edgeLocation;
+};
+
+class FragmentShaderRGBATexClampAlphaAA : public FragmentTexClampAlphaAABinding {
+public:
+ String getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel.
+class FragmentShaderRGBATexClampSwizzleAlphaAA : public FragmentTexClampAlphaAABinding {
+public:
+ String getShaderString() const;
+};
+
+class FragmentShaderRGBATexAlphaMask {
+public:
+ FragmentShaderRGBATexAlphaMask();
+ String getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int maskSamplerLocation() const { return m_maskSamplerLocation; }
+ int maskTexCoordScaleLocation() const { return m_maskTexCoordScaleLocation; }
+ int maskTexCoordOffsetLocation() const { return m_maskTexCoordOffsetLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_maskSamplerLocation;
+ int m_alphaLocation;
+ int m_maskTexCoordScaleLocation;
+ int m_maskTexCoordOffsetLocation;
+};
+
+class FragmentShaderRGBATexAlphaMaskAA {
+public:
+ FragmentShaderRGBATexAlphaMaskAA();
+ String getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int maskSamplerLocation() const { return m_maskSamplerLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+ int maskTexCoordScaleLocation() const { return m_maskTexCoordScaleLocation; }
+ int maskTexCoordOffsetLocation() const { return m_maskTexCoordOffsetLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_maskSamplerLocation;
+ int m_alphaLocation;
+ int m_edgeLocation;
+ int m_maskTexCoordScaleLocation;
+ int m_maskTexCoordOffsetLocation;
+};
+
+class FragmentShaderYUVVideo {
+public:
+ FragmentShaderYUVVideo();
+ String getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+
+ int yTextureLocation() const { return m_yTextureLocation; }
+ int uTextureLocation() const { return m_uTextureLocation; }
+ int vTextureLocation() const { return m_vTextureLocation; }
+ int alphaLocation() const { return m_alphaLocation; }
+ int ccMatrixLocation() const { return m_ccMatrixLocation; }
+ int yuvAdjLocation() const { return m_yuvAdjLocation; }
+
+private:
+ int m_yTextureLocation;
+ int m_uTextureLocation;
+ int m_vTextureLocation;
+ int m_alphaLocation;
+ int m_ccMatrixLocation;
+ int m_yuvAdjLocation;
+};
+
+class FragmentShaderColor {
+public:
+ FragmentShaderColor();
+ String getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int colorLocation() const { return m_colorLocation; }
+
+private:
+ int m_colorLocation;
+};
+
+class FragmentShaderCheckerboard {
+public:
+ FragmentShaderCheckerboard();
+ String getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int texTransformLocation() const { return m_texTransformLocation; }
+ int frequencyLocation() const { return m_frequencyLocation; }
+private:
+ int m_alphaLocation;
+ int m_texTransformLocation;
+ int m_frequencyLocation;
+};
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/SkPictureCanvasLayerTextureUpdater.cpp b/cc/SkPictureCanvasLayerTextureUpdater.cpp
new file mode 100644
index 0000000..3668d34
--- /dev/null
+++ b/cc/SkPictureCanvasLayerTextureUpdater.cpp
@@ -0,0 +1,47 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "SkPictureCanvasLayerTextureUpdater.h"
+
+#include "LayerPainterChromium.h"
+#include "SkCanvas.h"
+#include "TraceEvent.h"
+
+namespace WebCore {
+
+SkPictureCanvasLayerTextureUpdater::SkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium> painter)
+ : CanvasLayerTextureUpdater(painter)
+ , m_layerIsOpaque(false)
+{
+}
+
+SkPictureCanvasLayerTextureUpdater::~SkPictureCanvasLayerTextureUpdater()
+{
+}
+
+void SkPictureCanvasLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats& stats)
+{
+ SkCanvas* canvas = m_picture.beginRecording(contentRect.width(), contentRect.height());
+ paintContents(canvas, contentRect, contentsWidthScale, contentsHeightScale, resultingOpaqueRect, stats);
+ m_picture.endRecording();
+}
+
+void SkPictureCanvasLayerTextureUpdater::drawPicture(SkCanvas* canvas)
+{
+ TRACE_EVENT0("cc", "SkPictureCanvasLayerTextureUpdater::drawPicture");
+ canvas->drawPicture(m_picture);
+}
+
+void SkPictureCanvasLayerTextureUpdater::setOpaque(bool opaque)
+{
+ m_layerIsOpaque = opaque;
+}
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/SkPictureCanvasLayerTextureUpdater.h b/cc/SkPictureCanvasLayerTextureUpdater.h
new file mode 100644
index 0000000..2635ff7
--- /dev/null
+++ b/cc/SkPictureCanvasLayerTextureUpdater.h
@@ -0,0 +1,48 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef SkPictureCanvasLayerTextureUpdater_h
+#define SkPictureCanvasLayerTextureUpdater_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CanvasLayerTextureUpdater.h"
+#include "SkPicture.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+class LayerPainterChromium;
+
+// This class records the contentRect into an SkPicture. Subclasses, provide
+// different implementations of tile updating based on this recorded picture.
+// The BitmapSkPictureCanvasLayerTextureUpdater and
+// FrameBufferSkPictureCanvasLayerTextureUpdater are two examples of such
+// implementations.
+class SkPictureCanvasLayerTextureUpdater : public CanvasLayerTextureUpdater {
+public:
+ virtual ~SkPictureCanvasLayerTextureUpdater();
+
+ virtual void setOpaque(bool) OVERRIDE;
+
+protected:
+ explicit SkPictureCanvasLayerTextureUpdater(PassOwnPtr<LayerPainterChromium>);
+
+ virtual void prepareToUpdate(const IntRect& contentRect, const IntSize& tileSize, float contentsWidthScale, float contentsHeightScale, IntRect& resultingOpaqueRect, CCRenderingStats&) OVERRIDE;
+ void drawPicture(SkCanvas*);
+
+ bool layerIsOpaque() const { return m_layerIsOpaque; }
+
+private:
+ // Recording canvas.
+ SkPicture m_picture;
+ // True when it is known that all output pixels will be opaque.
+ bool m_layerIsOpaque;
+};
+
+} // namespace WebCore
+#endif // USE(ACCELERATED_COMPOSITING)
+#endif // SkPictureCanvasLayerTextureUpdater_h
diff --git a/cc/SolidColorLayerChromium.cpp b/cc/SolidColorLayerChromium.cpp
new file mode 100644
index 0000000..b3652ba
--- /dev/null
+++ b/cc/SolidColorLayerChromium.cpp
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "SolidColorLayerChromium.h"
+
+#include "CCSolidColorLayerImpl.h"
+
+namespace WebCore {
+
+PassOwnPtr<CCLayerImpl> SolidColorLayerChromium::createCCLayerImpl()
+{
+ return CCSolidColorLayerImpl::create(id());
+}
+
+PassRefPtr<SolidColorLayerChromium> SolidColorLayerChromium::create()
+{
+ return adoptRef(new SolidColorLayerChromium());
+}
+
+SolidColorLayerChromium::SolidColorLayerChromium()
+ : LayerChromium()
+{
+}
+
+SolidColorLayerChromium::~SolidColorLayerChromium()
+{
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/SolidColorLayerChromium.h b/cc/SolidColorLayerChromium.h
new file mode 100644
index 0000000..53ac9dc
--- /dev/null
+++ b/cc/SolidColorLayerChromium.h
@@ -0,0 +1,32 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef SolidColorLayerChromium_h
+#define SolidColorLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerChromium.h"
+
+namespace WebCore {
+
+// A Layer that renders a solid color. The color is specified by using
+// setBackgroundColor() on the base class.
+class SolidColorLayerChromium : public LayerChromium {
+public:
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+ static PassRefPtr<SolidColorLayerChromium> create();
+
+ virtual ~SolidColorLayerChromium();
+
+protected:
+ SolidColorLayerChromium();
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
+
diff --git a/cc/TextureCopier.cpp b/cc/TextureCopier.cpp
new file mode 100644
index 0000000..e2cdd4b
--- /dev/null
+++ b/cc/TextureCopier.cpp
@@ -0,0 +1,101 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "TextureCopier.h"
+
+#include "CCRendererGL.h" // For the GLC() macro.
+#include "GraphicsContext3D.h"
+#include "TraceEvent.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+#if USE(ACCELERATED_COMPOSITING)
+AcceleratedTextureCopier::AcceleratedTextureCopier(WebKit::WebGraphicsContext3D* context, bool usingBindUniforms)
+ : m_context(context)
+ , m_usingBindUniforms(usingBindUniforms)
+{
+ ASSERT(m_context);
+ GLC(m_context, m_fbo = m_context->createFramebuffer());
+ GLC(m_context, m_positionBuffer = m_context->createBuffer());
+
+ static const float kPositions[4][4] = {
+ {-1, -1, 0, 1},
+ { 1, -1, 0, 1},
+ { 1, 1, 0, 1},
+ {-1, 1, 0, 1}
+ };
+
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_positionBuffer));
+ GLC(m_context, m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(kPositions), kPositions, GraphicsContext3D::STATIC_DRAW));
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0));
+
+ m_blitProgram = adoptPtr(new BlitProgram(m_context));
+}
+
+AcceleratedTextureCopier::~AcceleratedTextureCopier()
+{
+ if (m_blitProgram)
+ m_blitProgram->cleanup(m_context);
+ if (m_positionBuffer)
+ GLC(m_context, m_context->deleteBuffer(m_positionBuffer));
+ if (m_fbo)
+ GLC(m_context, m_context->deleteFramebuffer(m_fbo));
+}
+
+void AcceleratedTextureCopier::copyTexture(Parameters parameters)
+{
+ TRACE_EVENT0("cc", "TextureCopier::copyTexture");
+
+ // Note: this code does not restore the viewport, bound program, 2D texture, framebuffer, buffer or blend enable.
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo));
+ GLC(m_context, m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, parameters.destTexture, 0));
+
+#if OS(ANDROID)
+ // Clear destination to improve performance on tiling GPUs.
+ // TODO: Use EXT_discard_framebuffer or skip clearing if it isn't available.
+ GLC(m_context, m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT));
+#endif
+
+ GLC(m_context, m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, parameters.sourceTexture));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST));
+
+ if (!m_blitProgram->initialized())
+ m_blitProgram->initialize(m_context, m_usingBindUniforms);
+
+ // TODO: Use EXT_framebuffer_blit if available.
+ GLC(m_context, m_context->useProgram(m_blitProgram->program()));
+
+ const int kPositionAttribute = 0;
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_positionBuffer));
+ GLC(m_context, m_context->vertexAttribPointer(kPositionAttribute, 4, GraphicsContext3D::FLOAT, false, 0, 0));
+ GLC(m_context, m_context->enableVertexAttribArray(kPositionAttribute));
+ GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0));
+
+ GLC(m_context, m_context->viewport(0, 0, parameters.size.width(), parameters.size.height()));
+ GLC(m_context, m_context->disable(GraphicsContext3D::BLEND));
+ GLC(m_context, m_context->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4));
+
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
+ GLC(m_context, m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
+ GLC(m_context, m_context->disableVertexAttribArray(kPositionAttribute));
+
+ GLC(m_context, m_context->useProgram(0));
+
+ GLC(m_context, m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, 0, 0));
+ GLC(m_context, m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0));
+ GLC(m_context, m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0));
+}
+
+void AcceleratedTextureCopier::flush()
+{
+ GLC(m_context, m_context->flush());
+}
+
+}
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/TextureCopier.h b/cc/TextureCopier.h
new file mode 100644
index 0000000..adffddc
--- /dev/null
+++ b/cc/TextureCopier.h
@@ -0,0 +1,68 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TextureCopier_h
+#define TextureCopier_h
+
+#include "GraphicsContext3D.h"
+#include "ProgramBinding.h"
+#include "ShaderChromium.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+class IntSize;
+
+class TextureCopier {
+public:
+ struct Parameters {
+ unsigned sourceTexture;
+ unsigned destTexture;
+ IntSize size;
+ };
+ // Copy the base level contents of |sourceTexture| to |destTexture|. Both texture objects
+ // must be complete and have a base level of |size| dimensions. The color formats do not need
+ // to match, but |destTexture| must have a renderable format.
+ virtual void copyTexture(Parameters) = 0;
+ virtual void flush() = 0;
+
+ virtual ~TextureCopier() { }
+};
+
+#if USE(ACCELERATED_COMPOSITING)
+
+class AcceleratedTextureCopier : public TextureCopier {
+ WTF_MAKE_NONCOPYABLE(AcceleratedTextureCopier);
+public:
+ static PassOwnPtr<AcceleratedTextureCopier> create(WebKit::WebGraphicsContext3D* context, bool usingBindUniforms)
+ {
+ return adoptPtr(new AcceleratedTextureCopier(context, usingBindUniforms));
+ }
+ virtual ~AcceleratedTextureCopier();
+
+ virtual void copyTexture(Parameters) OVERRIDE;
+ virtual void flush() OVERRIDE;
+
+protected:
+ AcceleratedTextureCopier(WebKit::WebGraphicsContext3D*, bool usingBindUniforms);
+
+private:
+ typedef ProgramBinding<VertexShaderPosTexIdentity, FragmentShaderRGBATex> BlitProgram;
+
+ WebKit::WebGraphicsContext3D* m_context;
+ Platform3DObject m_fbo;
+ Platform3DObject m_positionBuffer;
+ OwnPtr<BlitProgram> m_blitProgram;
+ bool m_usingBindUniforms;
+};
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+}
+
+#endif
diff --git a/cc/TextureLayerChromium.cpp b/cc/TextureLayerChromium.cpp
new file mode 100644
index 0000000..0ba2e61
--- /dev/null
+++ b/cc/TextureLayerChromium.cpp
@@ -0,0 +1,133 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "TextureLayerChromium.h"
+
+#include "CCLayerTreeHost.h"
+#include "CCTextureLayerImpl.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebCore {
+
+PassRefPtr<TextureLayerChromium> TextureLayerChromium::create(TextureLayerChromiumClient* client)
+{
+ return adoptRef(new TextureLayerChromium(client));
+}
+
+TextureLayerChromium::TextureLayerChromium(TextureLayerChromiumClient* client)
+ : LayerChromium()
+ , m_client(client)
+ , m_flipped(true)
+ , m_uvRect(0, 0, 1, 1)
+ , m_premultipliedAlpha(true)
+ , m_rateLimitContext(false)
+ , m_contextLost(false)
+ , m_textureId(0)
+{
+}
+
+TextureLayerChromium::~TextureLayerChromium()
+{
+ if (layerTreeHost()) {
+ if (m_textureId)
+ layerTreeHost()->acquireLayerTextures();
+ if (m_rateLimitContext && m_client)
+ layerTreeHost()->stopRateLimiter(m_client->context());
+ }
+}
+
+PassOwnPtr<CCLayerImpl> TextureLayerChromium::createCCLayerImpl()
+{
+ return CCTextureLayerImpl::create(m_layerId);
+}
+
+void TextureLayerChromium::setFlipped(bool flipped)
+{
+ m_flipped = flipped;
+ setNeedsCommit();
+}
+
+void TextureLayerChromium::setUVRect(const FloatRect& rect)
+{
+ m_uvRect = rect;
+ setNeedsCommit();
+}
+
+void TextureLayerChromium::setPremultipliedAlpha(bool premultipliedAlpha)
+{
+ m_premultipliedAlpha = premultipliedAlpha;
+ setNeedsCommit();
+}
+
+void TextureLayerChromium::setRateLimitContext(bool rateLimit)
+{
+ if (!rateLimit && m_rateLimitContext && m_client && layerTreeHost())
+ layerTreeHost()->stopRateLimiter(m_client->context());
+
+ m_rateLimitContext = rateLimit;
+}
+
+void TextureLayerChromium::setTextureId(unsigned id)
+{
+ if (m_textureId == id)
+ return;
+ if (m_textureId && layerTreeHost())
+ layerTreeHost()->acquireLayerTextures();
+ m_textureId = id;
+ setNeedsCommit();
+}
+
+void TextureLayerChromium::willModifyTexture()
+{
+ if (layerTreeHost())
+ layerTreeHost()->acquireLayerTextures();
+}
+
+void TextureLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
+{
+ LayerChromium::setNeedsDisplayRect(dirtyRect);
+
+ if (m_rateLimitContext && m_client && layerTreeHost())
+ layerTreeHost()->startRateLimiter(m_client->context());
+}
+
+void TextureLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
+{
+ if (m_textureId && layerTreeHost() && host != layerTreeHost())
+ layerTreeHost()->acquireLayerTextures();
+ LayerChromium::setLayerTreeHost(host);
+}
+
+bool TextureLayerChromium::drawsContent() const
+{
+ return (m_client || m_textureId) && !m_contextLost && LayerChromium::drawsContent();
+}
+
+void TextureLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker*, CCRenderingStats&)
+{
+ if (m_client) {
+ m_textureId = m_client->prepareTexture(queue);
+ m_contextLost = m_client->context()->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR;
+ }
+
+ m_needsDisplay = false;
+}
+
+void TextureLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
+{
+ LayerChromium::pushPropertiesTo(layer);
+
+ CCTextureLayerImpl* textureLayer = static_cast<CCTextureLayerImpl*>(layer);
+ textureLayer->setFlipped(m_flipped);
+ textureLayer->setUVRect(m_uvRect);
+ textureLayer->setPremultipliedAlpha(m_premultipliedAlpha);
+ textureLayer->setTextureId(m_textureId);
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/TextureLayerChromium.h b/cc/TextureLayerChromium.h
new file mode 100644
index 0000000..5728d74
--- /dev/null
+++ b/cc/TextureLayerChromium.h
@@ -0,0 +1,90 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef TextureLayerChromium_h
+#define TextureLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerChromium.h"
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class TextureLayerChromiumClient {
+public:
+ // Called to prepare this layer's texture for compositing. The client may queue a texture
+ // upload or copy on the CCTextureUpdateQueue.
+ // Returns the texture ID to be used for compositing.
+ virtual unsigned prepareTexture(CCTextureUpdateQueue&) = 0;
+
+ // Returns the context that is providing the texture. Used for rate limiting and detecting lost context.
+ virtual WebKit::WebGraphicsContext3D* context() = 0;
+
+protected:
+ virtual ~TextureLayerChromiumClient() { }
+};
+
+// A Layer containing a the rendered output of a plugin instance.
+class TextureLayerChromium : public LayerChromium {
+public:
+ // If this texture layer requires special preparation logic for each frame driven by
+ // the compositor, pass in a non-nil client. Pass in a nil client pointer if texture updates
+ // are driven by an external process.
+ static PassRefPtr<TextureLayerChromium> create(TextureLayerChromiumClient*);
+ virtual ~TextureLayerChromium();
+
+ void clearClient() { m_client = 0; }
+
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+
+ // Sets whether this texture should be Y-flipped at draw time. Defaults to true.
+ void setFlipped(bool);
+
+ // Sets a UV transform to be used at draw time. Defaults to (0, 0, 1, 1).
+ void setUVRect(const FloatRect&);
+
+ // Sets whether the alpha channel is premultiplied or unpremultiplied. Defaults to true.
+ void setPremultipliedAlpha(bool);
+
+ // Sets whether this context should rate limit on damage to prevent too many frames from
+ // being queued up before the compositor gets a chance to run. Requires a non-nil client.
+ // Defaults to false.
+ void setRateLimitContext(bool);
+
+ // Code path for plugins which supply their own texture ID.
+ void setTextureId(unsigned);
+
+ void willModifyTexture();
+
+ virtual void setNeedsDisplayRect(const FloatRect&) OVERRIDE;
+
+ virtual void setLayerTreeHost(CCLayerTreeHost*) OVERRIDE;
+ virtual bool drawsContent() const OVERRIDE;
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+
+protected:
+ explicit TextureLayerChromium(TextureLayerChromiumClient*);
+
+private:
+ TextureLayerChromiumClient* m_client;
+
+ bool m_flipped;
+ FloatRect m_uvRect;
+ bool m_premultipliedAlpha;
+ bool m_rateLimitContext;
+ bool m_contextLost;
+
+ unsigned m_textureId;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/TextureUploader.h b/cc/TextureUploader.h
new file mode 100644
index 0000000..1826fcb
--- /dev/null
+++ b/cc/TextureUploader.h
@@ -0,0 +1,30 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TextureUploader_h
+#define TextureUploader_h
+
+#include "LayerTextureUpdater.h"
+
+namespace WebCore {
+
+class TextureUploader {
+public:
+ struct Parameters {
+ LayerTextureUpdater::Texture* texture;
+ IntRect sourceRect;
+ IntSize destOffset;
+ };
+
+ virtual ~TextureUploader() { }
+
+ virtual bool isBusy() = 0;
+ virtual void beginUploads() = 0;
+ virtual void endUploads() = 0;
+ virtual void uploadTexture(CCResourceProvider*, Parameters) = 0;
+};
+
+}
+
+#endif
diff --git a/cc/ThrottledTextureUploader.cpp b/cc/ThrottledTextureUploader.cpp
new file mode 100644
index 0000000..594342e
--- /dev/null
+++ b/cc/ThrottledTextureUploader.cpp
@@ -0,0 +1,118 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "ThrottledTextureUploader.h"
+
+#include "Extensions3DChromium.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace {
+
+// Number of pending texture update queries to allow.
+static const size_t maxPendingQueries = 2;
+
+} // anonymous namespace
+
+namespace WebCore {
+
+ThrottledTextureUploader::Query::Query(WebKit::WebGraphicsContext3D* context)
+ : m_context(context)
+ , m_queryId(0)
+{
+ m_queryId = m_context->createQueryEXT();
+}
+
+ThrottledTextureUploader::Query::~Query()
+{
+ m_context->deleteQueryEXT(m_queryId);
+}
+
+void ThrottledTextureUploader::Query::begin()
+{
+ m_context->beginQueryEXT(Extensions3DChromium::COMMANDS_ISSUED_CHROMIUM, m_queryId);
+}
+
+void ThrottledTextureUploader::Query::end()
+{
+ m_context->endQueryEXT(Extensions3DChromium::COMMANDS_ISSUED_CHROMIUM);
+}
+
+bool ThrottledTextureUploader::Query::isPending()
+{
+ unsigned available = 1;
+ m_context->getQueryObjectuivEXT(m_queryId, Extensions3DChromium::QUERY_RESULT_AVAILABLE_EXT, &available);
+ return !available;
+}
+
+void ThrottledTextureUploader::Query::wait()
+{
+ unsigned result;
+ m_context->getQueryObjectuivEXT(m_queryId, Extensions3DChromium::QUERY_RESULT_EXT, &result);
+}
+
+ThrottledTextureUploader::ThrottledTextureUploader(WebKit::WebGraphicsContext3D* context)
+ : m_context(context)
+ , m_maxPendingQueries(maxPendingQueries)
+{
+}
+
+ThrottledTextureUploader::ThrottledTextureUploader(WebKit::WebGraphicsContext3D* context, size_t pendingUploadLimit)
+ : m_context(context)
+ , m_maxPendingQueries(pendingUploadLimit)
+{
+ ASSERT(m_context);
+}
+
+ThrottledTextureUploader::~ThrottledTextureUploader()
+{
+}
+
+bool ThrottledTextureUploader::isBusy()
+{
+ processQueries();
+
+ if (!m_availableQueries.isEmpty())
+ return false;
+
+ if (m_pendingQueries.size() == m_maxPendingQueries)
+ return true;
+
+ m_availableQueries.append(Query::create(m_context));
+ return false;
+}
+
+void ThrottledTextureUploader::beginUploads()
+{
+ // Wait for query to become available.
+ while (isBusy())
+ m_pendingQueries.first()->wait();
+
+ ASSERT(!m_availableQueries.isEmpty());
+ m_availableQueries.first()->begin();
+}
+
+void ThrottledTextureUploader::endUploads()
+{
+ m_availableQueries.first()->end();
+ m_pendingQueries.append(m_availableQueries.takeFirst());
+}
+
+void ThrottledTextureUploader::uploadTexture(CCResourceProvider* resourceProvider, Parameters upload)
+{
+ upload.texture->updateRect(resourceProvider, upload.sourceRect, upload.destOffset);
+}
+
+void ThrottledTextureUploader::processQueries()
+{
+ while (!m_pendingQueries.isEmpty()) {
+ if (m_pendingQueries.first()->isPending())
+ break;
+
+ m_availableQueries.append(m_pendingQueries.takeFirst());
+ }
+}
+
+}
diff --git a/cc/ThrottledTextureUploader.h b/cc/ThrottledTextureUploader.h
new file mode 100644
index 0000000..f51d866
--- /dev/null
+++ b/cc/ThrottledTextureUploader.h
@@ -0,0 +1,68 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ThrottledTextureUploader_h
+#define ThrottledTextureUploader_h
+
+#include "TextureUploader.h"
+
+#include <wtf/Deque.h>
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace WebCore {
+
+class ThrottledTextureUploader : public TextureUploader {
+ WTF_MAKE_NONCOPYABLE(ThrottledTextureUploader);
+public:
+ static PassOwnPtr<ThrottledTextureUploader> create(WebKit::WebGraphicsContext3D* context)
+ {
+ return adoptPtr(new ThrottledTextureUploader(context));
+ }
+ static PassOwnPtr<ThrottledTextureUploader> create(WebKit::WebGraphicsContext3D* context, size_t pendingUploadLimit)
+ {
+ return adoptPtr(new ThrottledTextureUploader(context, pendingUploadLimit));
+ }
+ virtual ~ThrottledTextureUploader();
+
+ virtual bool isBusy() OVERRIDE;
+ virtual void beginUploads() OVERRIDE;
+ virtual void endUploads() OVERRIDE;
+ virtual void uploadTexture(CCResourceProvider*, Parameters) OVERRIDE;
+
+private:
+ class Query {
+ public:
+ static PassOwnPtr<Query> create(WebKit::WebGraphicsContext3D* context) { return adoptPtr(new Query(context)); }
+
+ virtual ~Query();
+
+ void begin();
+ void end();
+ bool isPending();
+ void wait();
+
+ private:
+ explicit Query(WebKit::WebGraphicsContext3D*);
+
+ WebKit::WebGraphicsContext3D* m_context;
+ unsigned m_queryId;
+ };
+
+ ThrottledTextureUploader(WebKit::WebGraphicsContext3D*);
+ ThrottledTextureUploader(WebKit::WebGraphicsContext3D*, size_t pendingUploadLimit);
+
+ void processQueries();
+
+ WebKit::WebGraphicsContext3D* m_context;
+ size_t m_maxPendingQueries;
+ Deque<OwnPtr<Query> > m_pendingQueries;
+ Deque<OwnPtr<Query> > m_availableQueries;
+};
+
+}
+
+#endif
diff --git a/cc/TiledLayerChromium.cpp b/cc/TiledLayerChromium.cpp
new file mode 100644
index 0000000..8a6e4b9
--- /dev/null
+++ b/cc/TiledLayerChromium.cpp
@@ -0,0 +1,791 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "TiledLayerChromium.h"
+
+#include "CCLayerImpl.h"
+#include "CCLayerTreeHost.h"
+#include "CCOverdrawMetrics.h"
+#include "CCTextureUpdateQueue.h"
+#include "CCTiledLayerImpl.h"
+#include "GraphicsContext3D.h"
+#include "Region.h"
+#include "TextStream.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+using WebKit::WebTransformationMatrix;
+
+namespace WebCore {
+
+class UpdatableTile : public CCLayerTilingData::Tile {
+ WTF_MAKE_NONCOPYABLE(UpdatableTile);
+public:
+ static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Texture> texture)
+ {
+ return adoptPtr(new UpdatableTile(texture));
+ }
+
+ LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
+ CCPrioritizedTexture* managedTexture() { return m_texture->texture(); }
+
+ bool isDirty() const { return !dirtyRect.isEmpty(); }
+
+ // Reset update state for the current frame. This should occur before painting
+ // for all layers. Since painting one layer can invalidate another layer
+ // after it has already painted, mark all non-dirty tiles as valid before painting
+ // such that invalidations during painting won't prevent them from being pushed.
+ void resetUpdateState()
+ {
+ updateRect = IntRect();
+ occluded = false;
+ partialUpdate = false;
+ validForFrame = !isDirty();
+ }
+
+ // This promises to update the tile and therefore also guarantees the tile
+ // will be valid for this frame. dirtyRect is copied into updateRect so
+ // we can continue to track re-entrant invalidations that occur during painting.
+ void markForUpdate()
+ {
+ validForFrame = true;
+ updateRect = dirtyRect;
+ dirtyRect = IntRect();
+ }
+
+ IntRect dirtyRect;
+ IntRect updateRect;
+ bool partialUpdate;
+ bool validForFrame;
+ bool occluded;
+ bool isInUseOnImpl;
+private:
+ explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture)
+ : partialUpdate(false)
+ , validForFrame(false)
+ , occluded(false)
+ , isInUseOnImpl(false)
+ , m_texture(texture)
+ {
+ }
+
+ OwnPtr<LayerTextureUpdater::Texture> m_texture;
+};
+
+TiledLayerChromium::TiledLayerChromium()
+ : LayerChromium()
+ , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
+ , m_skipsDraw(false)
+ , m_failedUpdate(false)
+ , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
+ , m_tilingOption(AutoTile)
+{
+ m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderTexels);
+}
+
+TiledLayerChromium::~TiledLayerChromium()
+{
+}
+
+PassOwnPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
+{
+ return CCTiledLayerImpl::create(id());
+}
+
+void TiledLayerChromium::updateTileSizeAndTilingOption()
+{
+ ASSERT(layerTreeHost());
+
+ const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize;
+ const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize;
+ int layerWidth = contentBounds().width();
+ int layerHeight = contentBounds().height();
+
+ const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(defaultTileSize.height(), layerHeight));
+
+ // Tile if both dimensions large, or any one dimension large and the other
+ // extends into a second tile but the total layer area isn't larger than that
+ // of the largest possible untiled layer. This heuristic allows for long skinny layers
+ // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids
+ // creating very large tiles.
+ const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height();
+ const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height())
+ && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height());
+ const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
+
+ bool isTiled;
+ if (m_tilingOption == AlwaysTile)
+ isTiled = true;
+ else if (m_tilingOption == NeverTile)
+ isTiled = false;
+ else
+ isTiled = autoTiled;
+
+ IntSize requestedSize = isTiled ? tileSize : contentBounds();
+ const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize;
+ IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
+ setTileSize(clampedSize);
+}
+
+void TiledLayerChromium::updateBounds()
+{
+ IntSize oldBounds = m_tiler->bounds();
+ IntSize newBounds = contentBounds();
+ if (oldBounds == newBounds)
+ return;
+ m_tiler->setBounds(newBounds);
+
+ // Invalidate any areas that the new bounds exposes.
+ Region oldRegion(IntRect(IntPoint(), oldBounds));
+ Region newRegion(IntRect(IntPoint(), newBounds));
+ newRegion.subtract(oldRegion);
+ Vector<IntRect> rects = newRegion.rects();
+ for (size_t i = 0; i < rects.size(); ++i)
+ invalidateContentRect(rects[i]);
+}
+
+void TiledLayerChromium::setTileSize(const IntSize& size)
+{
+ m_tiler->setTileSize(size);
+}
+
+void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption)
+{
+ m_tiler->setBorderTexelOption(borderTexelOption);
+}
+
+bool TiledLayerChromium::drawsContent() const
+{
+ if (!LayerChromium::drawsContent())
+ return false;
+
+ bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1;
+ if (m_tilingOption == NeverTile && hasMoreThanOneTile)
+ return false;
+
+ return true;
+}
+
+bool TiledLayerChromium::needsContentsScale() const
+{
+ return true;
+}
+
+IntSize TiledLayerChromium::contentBounds() const
+{
+ return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale()));
+}
+
+void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
+{
+ m_tilingOption = tilingOption;
+}
+
+void TiledLayerChromium::setIsMask(bool isMask)
+{
+ setTilingOption(isMask ? NeverTile : AutoTile);
+}
+
+void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
+{
+ LayerChromium::pushPropertiesTo(layer);
+
+ CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
+
+ tiledLayer->setSkipsDraw(m_skipsDraw);
+ tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
+ tiledLayer->setTilingData(*m_tiler);
+ Vector<UpdatableTile*> invalidTiles;
+
+ for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+ int i = iter->first.first;
+ int j = iter->first.second;
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ tile->isInUseOnImpl = false;
+ if (!tile->managedTexture()->haveBackingTexture()) {
+ invalidTiles.append(tile);
+ continue;
+ }
+ if (!tile->validForFrame)
+ continue;
+
+ tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(), tile->opaqueRect());
+ tile->isInUseOnImpl = true;
+ }
+ for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
+ m_tiler->takeTile((*iter)->i(), (*iter)->j());
+}
+
+CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const
+{
+ if (!layerTreeHost())
+ return 0;
+ return layerTreeHost()->contentsTextureManager();
+}
+
+void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
+{
+ if (host && host != layerTreeHost()) {
+ for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ tile->managedTexture()->setTextureManager(host->contentsTextureManager());
+ }
+ }
+ LayerChromium::setLayerTreeHost(host);
+}
+
+UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
+{
+ return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
+}
+
+UpdatableTile* TiledLayerChromium::createTile(int i, int j)
+{
+ createTextureUpdaterIfNeeded();
+
+ OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTexture(textureManager())));
+ tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat);
+
+ UpdatableTile* addedTile = tile.get();
+ m_tiler->addTile(tile.release(), i, j);
+
+ addedTile->dirtyRect = m_tiler->tileRect(addedTile);
+
+ // Temporary diagnostic crash.
+ if (!addedTile)
+ CRASH();
+ if (!tileAt(i, j))
+ CRASH();
+
+ return addedTile;
+}
+
+void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
+{
+ float contentsWidthScale = static_cast<float>(contentBounds().width()) / bounds().width();
+ float contentsHeightScale = static_cast<float>(contentBounds().height()) / bounds().height();
+ FloatRect scaledDirtyRect(dirtyRect);
+ scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale);
+ IntRect dirty = enclosingIntRect(scaledDirtyRect);
+ invalidateContentRect(dirty);
+ LayerChromium::setNeedsDisplayRect(dirtyRect);
+}
+
+void TiledLayerChromium::setUseLCDText(bool useLCDText)
+{
+ LayerChromium::setUseLCDText(useLCDText);
+
+ CCLayerTilingData::BorderTexelOption borderTexelOption;
+#if OS(ANDROID)
+ // Always want border texels and GL_LINEAR due to pinch zoom.
+ borderTexelOption = CCLayerTilingData::HasBorderTexels;
+#else
+ borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
+#endif
+ setBorderTexelOption(borderTexelOption);
+}
+
+void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect)
+{
+ updateBounds();
+ if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw)
+ return;
+
+ for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+ ASSERT(tile);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ IntRect bound = m_tiler->tileRect(tile);
+ bound.intersect(contentRect);
+ tile->dirtyRect.unite(bound);
+ }
+}
+
+// Returns true if tile is dirty and only part of it needs to be updated.
+bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
+{
+ return !tile->dirtyRect.contains(m_tiler->tileRect(tile));
+}
+
+// Dirty tiles with valid textures needs buffered update to guarantee that
+// we don't modify textures currently used for drawing by the impl thread.
+bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
+{
+ if (!tile->managedTexture()->haveBackingTexture())
+ return false;
+
+ if (!tile->isDirty())
+ return false;
+
+ if (!tile->isInUseOnImpl)
+ return false;
+
+ return true;
+}
+
+
+bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats, bool& didPaint)
+{
+ didPaint = false;
+ createTextureUpdaterIfNeeded();
+
+ bool ignoreOcclusions = !occlusion;
+ if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) {
+ m_failedUpdate = true;
+ return false;
+ }
+
+ IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions);
+
+ if (occlusion)
+ occlusion->overdrawMetrics().didPaint(paintRect);
+
+ if (paintRect.isEmpty())
+ return true;
+
+ didPaint = true;
+ updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats);
+ return true;
+}
+
+void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const CCOcclusionTracker* occlusion)
+{
+ // There is some difficult dependancies between occlusions, recording occlusion metrics
+ // and requesting memory so those are encapsulated in this function:
+ // - We only want to call requestLate on unoccluded textures (to preserve
+ // memory for other layers when near OOM).
+ // - We only want to record occlusion metrics if all memory requests succeed.
+
+ int occludedTileCount = 0;
+ bool succeeded = true;
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ ASSERT(tile); // Did setTexturePriorities get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occlusion pass?
+ IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), visibleContentRect());
+ if (occlusion && occlusion->occluded(this, visibleTileRect)) {
+ tile->occluded = true;
+ occludedTileCount++;
+ } else {
+ succeeded &= tile->managedTexture()->requestLate();
+ }
+ }
+ }
+
+ if (!succeeded)
+ return;
+
+ // FIXME: Remove the loop and just pass the count!
+ for (int i = 0; i < occludedTileCount; i++)
+ occlusion->overdrawMetrics().didCullTileForUpload();
+}
+
+bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
+{
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ ASSERT(tile); // Did setTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+
+ // Ensure the entire tile is dirty if we don't have the texture.
+ if (!tile->managedTexture()->haveBackingTexture())
+ tile->dirtyRect = m_tiler->tileRect(tile);
+
+ // If using occlusion and the visible region of the tile is occluded,
+ // don't reserve a texture or update the tile.
+ if (tile->occluded && !ignoreOcclusions)
+ continue;
+
+ if (!tile->managedTexture()->canAcquireBackingTexture())
+ return false;
+ }
+ }
+ return true;
+}
+
+IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions)
+{
+ IntRect paintRect;
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ ASSERT(tile); // Did setTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ if (tile->occluded && !ignoreOcclusions)
+ continue;
+ paintRect.unite(tile->dirtyRect);
+ tile->markForUpdate();
+ }
+ }
+ return paintRect;
+}
+
+void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+ // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
+ m_updateRect = FloatRect(paintRect);
+ float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
+ float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
+ m_updateRect.scale(widthScale, heightScale);
+
+ // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
+ // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
+ // However, we can't free the memory backing the SkCanvas until the paint finishes,
+ // so we grab a local reference here to hold the updater alive until the paint completes.
+ RefPtr<LayerTextureUpdater> protector(textureUpdater());
+ IntRect paintedOpaqueRect;
+ textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats);
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ ASSERT(tile); // Did setTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+
+ IntRect tileRect = m_tiler->tileBounds(i, j);
+
+ // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
+ const IntRect& dirtyRect = tile->updateRect;
+ if (dirtyRect.isEmpty())
+ continue;
+
+ // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some
+ // other part of the tile opaque.
+ IntRect tilePaintedRect = intersection(tileRect, paintRect);
+ IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaqueRect);
+ if (!tilePaintedRect.isEmpty()) {
+ IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRect(), tilePaintedRect);
+ bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.contains(paintInsideTileOpaqueRect);
+ bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect);
+
+ if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect)
+ tile->setOpaqueRect(tilePaintedOpaqueRect);
+ }
+
+ // sourceRect starts as a full-sized tile with border texels included.
+ IntRect sourceRect = m_tiler->tileRect(tile);
+ sourceRect.intersect(dirtyRect);
+ // Paint rect not guaranteed to line up on tile boundaries, so
+ // make sure that sourceRect doesn't extend outside of it.
+ sourceRect.intersect(paintRect);
+
+ tile->updateRect = sourceRect;
+
+ if (sourceRect.isEmpty())
+ continue;
+
+ tile->texture()->prepareRect(sourceRect, stats);
+ if (occlusion)
+ occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect());
+
+ const IntPoint anchor = m_tiler->tileRect(tile).location();
+
+ // Calculate tile-space rectangle to upload into.
+ IntSize destOffset(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y());
+ if (destOffset.width() < 0)
+ CRASH();
+ if (destOffset.height() < 0)
+ CRASH();
+
+ // Offset from paint rectangle to this tile's dirty rectangle.
+ IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y());
+ if (paintOffset.x() < 0)
+ CRASH();
+ if (paintOffset.y() < 0)
+ CRASH();
+ if (paintOffset.x() + sourceRect.width() > paintRect.width())
+ CRASH();
+ if (paintOffset.y() + sourceRect.height() > paintRect.height())
+ CRASH();
+
+ TextureUploader::Parameters upload = { tile->texture(), sourceRect, destOffset };
+ if (tile->partialUpdate)
+ queue.appendPartialUpload(upload);
+ else
+ queue.appendFullUpload(upload);
+ }
+ }
+}
+
+namespace {
+// This picks a small animated layer to be anything less than one viewport. This
+// is specifically for page transitions which are viewport-sized layers. The extra
+// 64 pixels is due to these layers being slightly larger than the viewport in some cases.
+bool isSmallAnimatedLayer(TiledLayerChromium* layer)
+{
+ if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnimating())
+ return false;
+ IntSize viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->deviceViewportSize() : IntSize();
+ IntRect contentRect(IntPoint::zero(), layer->contentBounds());
+ return contentRect.width() <= viewportSize.width() + 64
+ && contentRect.height() <= viewportSize.height() + 64;
+}
+
+// FIXME: Remove this and make this based on distance once distance can be calculated
+// for offscreen layers. For now, prioritize all small animated layers after 512
+// pixels of pre-painting.
+void setPriorityForTexture(const CCPriorityCalculator& priorityCalc,
+ const IntRect& visibleRect,
+ const IntRect& tileRect,
+ bool drawsToRoot,
+ bool isSmallAnimatedLayer,
+ CCPrioritizedTexture* texture)
+{
+ int priority = CCPriorityCalculator::lowestPriority();
+ if (!visibleRect.isEmpty())
+ priority = priorityCalc.priorityFromDistance(visibleRect, tileRect, drawsToRoot);
+ if (isSmallAnimatedLayer)
+ priority = CCPriorityCalculator::maxPriority(priority, priorityCalc.priorityFromDistance(512, drawsToRoot));
+ if (priority != CCPriorityCalculator::lowestPriority())
+ texture->setRequestPriority(priority);
+}
+}
+
+void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
+{
+ updateBounds();
+ resetUpdateState();
+
+ if (m_tiler->hasEmptyBounds())
+ return;
+
+ bool drawsToRoot = !renderTarget()->parent();
+ bool smallAnimatedLayer = isSmallAnimatedLayer(this);
+
+ // Minimally create the tiles in the desired pre-paint rect.
+ IntRect createTilesRect = idlePaintRect();
+ if (!createTilesRect.isEmpty()) {
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bottom);
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ if (!tileAt(i, j))
+ createTile(i, j);
+ }
+ }
+ }
+
+ // Also, minimally create all tiles for small animated layers and also
+ // double-buffer them since we have limited their size to be reasonable.
+ IntRect doubleBufferedRect = visibleContentRect();
+ if (smallAnimatedLayer)
+ doubleBufferedRect = IntRect(IntPoint::zero(), contentBounds());
+
+ // Create additional textures for double-buffered updates when needed.
+ // These textures must stay alive while the updated textures are incrementally
+ // uploaded, swapped atomically via pushProperties, and finally deleted
+ // after the commit is complete, after which they can be recycled.
+ if (!doubleBufferedRect.isEmpty()) {
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(doubleBufferedRect, left, top, right, bottom);
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ if (!tile)
+ tile = createTile(i, j);
+ // We need an additional texture if the tile needs a buffered-update and it's not a partial update.
+ // FIXME: Decide if partial update should be allowed based on cost
+ // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
+ if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !tileNeedsBufferedUpdate(tile))
+ continue;
+ if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate()) {
+ tile->partialUpdate = true;
+ continue;
+ }
+
+ IntRect tileRect = m_tiler->tileRect(tile);
+ tile->dirtyRect = tileRect;
+ LayerTextureUpdater::Texture* backBuffer = tile->texture();
+ setPriorityForTexture(priorityCalc, visibleContentRect(), tile->dirtyRect, drawsToRoot, smallAnimatedLayer, backBuffer->texture());
+ OwnPtr<CCPrioritizedTexture> frontBuffer = CCPrioritizedTexture::create(backBuffer->texture()->textureManager(),
+ backBuffer->texture()->size(),
+ backBuffer->texture()->format());
+ // Swap backBuffer into frontBuffer and add it to delete after commit queue.
+ backBuffer->swapTextureWith(frontBuffer);
+ layerTreeHost()->deleteTextureAfterCommit(frontBuffer.release());
+ }
+ }
+ }
+
+ // Now update priorities on all tiles we have in the layer, no matter where they are.
+ for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ IntRect tileRect = m_tiler->tileRect(tile);
+ setPriorityForTexture(priorityCalc, visibleContentRect(), tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture());
+ }
+}
+
+Region TiledLayerChromium::visibleContentOpaqueRegion() const
+{
+ if (m_skipsDraw)
+ return Region();
+ if (opaque())
+ return visibleContentRect();
+ return m_tiler->opaqueRegionInContentRect(visibleContentRect());
+}
+
+void TiledLayerChromium::resetUpdateState()
+{
+ m_skipsDraw = false;
+ m_failedUpdate = false;
+
+ CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end();
+ for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ tile->resetUpdateState();
+ }
+}
+
+void TiledLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+ ASSERT(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped?
+ updateBounds();
+ if (m_tiler->hasEmptyBounds() || !drawsContent())
+ return;
+
+ bool didPaint = false;
+
+ // Animation pre-paint. If the layer is small, try to paint it all
+ // immediately whether or not it is occluded, to avoid paint/upload
+ // hiccups while it is animating.
+ if (isSmallAnimatedLayer(this)) {
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(IntRect(IntPoint::zero(), contentBounds()), left, top, right, bottom);
+ updateTiles(left, top, right, bottom, queue, 0, stats, didPaint);
+ if (didPaint)
+ return;
+ // This was an attempt to paint the entire layer so if we fail it's okay,
+ // just fallback on painting visible etc. below.
+ m_failedUpdate = false;
+ }
+
+ if (visibleContentRect().isEmpty())
+ return;
+
+ // Visible painting. First occlude visible tiles and paint the non-occluded tiles.
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bottom);
+ markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
+ m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint);
+ if (m_skipsDraw)
+ m_tiler->reset();
+ if (m_skipsDraw || didPaint)
+ return;
+
+ // If we have already painting everything visible. Do some pre-painting while idle.
+ IntRect idlePaintContentRect = idlePaintRect();
+ if (idlePaintContentRect.isEmpty())
+ return;
+
+ // Prepaint anything that was occluded but inside the layer's visible region.
+ if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
+ return;
+
+ int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
+ m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
+
+ // Then expand outwards from the visible area until we find a dirty row or column to update.
+ while (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom) {
+ if (bottom < prepaintBottom) {
+ ++bottom;
+ if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
+ return;
+ }
+ if (top > prepaintTop) {
+ --top;
+ if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
+ return;
+ }
+ if (left > prepaintLeft) {
+ --left;
+ if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
+ return;
+ }
+ if (right < prepaintRight) {
+ ++right;
+ if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
+ return;
+ }
+ }
+}
+
+bool TiledLayerChromium::needsIdlePaint()
+{
+ // Don't trigger more paints if we failed (as we'll just fail again).
+ if (m_failedUpdate || visibleContentRect().isEmpty() || m_tiler->hasEmptyBounds() || !drawsContent())
+ return false;
+
+ IntRect idlePaintContentRect = idlePaintRect();
+ if (idlePaintContentRect.isEmpty())
+ return false;
+
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = tileAt(i, j);
+ ASSERT(tile); // Did setTexturePriorities get skipped?
+ if (!tile)
+ continue;
+
+ bool updated = !tile->updateRect.isEmpty();
+ bool canAcquire = tile->managedTexture()->canAcquireBackingTexture();
+ bool dirty = tile->isDirty() || !tile->managedTexture()->haveBackingTexture();
+ if (!updated && canAcquire && dirty)
+ return true;
+ }
+ }
+ return false;
+}
+
+IntRect TiledLayerChromium::idlePaintRect()
+{
+ // Don't inflate an empty rect.
+ if (visibleContentRect().isEmpty())
+ return IntRect();
+
+ // FIXME: This can be made a lot larger now! We should increase
+ // this slowly while insuring it doesn't cause any perf issues.
+ IntRect prepaintRect = visibleContentRect();
+ prepaintRect.inflateX(m_tiler->tileSize().width());
+ prepaintRect.inflateY(m_tiler->tileSize().height() * 2);
+ IntRect contentRect(IntPoint::zero(), contentBounds());
+ prepaintRect.intersect(contentRect);
+
+ return prepaintRect;
+}
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/TiledLayerChromium.h b/cc/TiledLayerChromium.h
new file mode 100644
index 0000000..c7f57f0
--- /dev/null
+++ b/cc/TiledLayerChromium.h
@@ -0,0 +1,106 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TiledLayerChromium_h
+#define TiledLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "CCLayerTilingData.h"
+#include "LayerChromium.h"
+#include "LayerTextureUpdater.h"
+
+namespace WebCore {
+class UpdatableTile;
+
+class TiledLayerChromium : public LayerChromium {
+public:
+ enum TilingOption { AlwaysTile, NeverTile, AutoTile };
+
+ virtual ~TiledLayerChromium();
+
+ virtual void setIsMask(bool) OVERRIDE;
+
+ virtual void pushPropertiesTo(CCLayerImpl*) OVERRIDE;
+
+ virtual bool drawsContent() const OVERRIDE;
+ virtual bool needsContentsScale() const OVERRIDE;
+
+ virtual IntSize contentBounds() const OVERRIDE;
+
+ virtual void setNeedsDisplayRect(const FloatRect&) OVERRIDE;
+
+ virtual void setUseLCDText(bool) OVERRIDE;
+
+ virtual void setLayerTreeHost(CCLayerTreeHost*) OVERRIDE;
+
+ virtual void setTexturePriorities(const CCPriorityCalculator&) OVERRIDE;
+
+ virtual Region visibleContentOpaqueRegion() const OVERRIDE;
+
+ virtual void update(CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&) OVERRIDE;
+
+protected:
+ TiledLayerChromium();
+
+ void updateTileSizeAndTilingOption();
+ void updateBounds();
+
+ // Exposed to subclasses for testing.
+ void setTileSize(const IntSize&);
+ void setTextureFormat(GC3Denum textureFormat) { m_textureFormat = textureFormat; }
+ void setBorderTexelOption(CCLayerTilingData::BorderTexelOption);
+ void setSampledTexelFormat(LayerTextureUpdater::SampledTexelFormat sampledTexelFormat) { m_sampledTexelFormat = sampledTexelFormat; }
+ size_t numPaintedTiles() { return m_tiler->tiles().size(); }
+
+ virtual LayerTextureUpdater* textureUpdater() const = 0;
+ virtual void createTextureUpdaterIfNeeded() = 0;
+
+ // Set invalidations to be potentially repainted during update().
+ void invalidateContentRect(const IntRect& contentRect);
+
+ // Reset state on tiles that will be used for updating the layer.
+ void resetUpdateState();
+
+ // After preparing an update, returns true if more painting is needed.
+ bool needsIdlePaint();
+ IntRect idlePaintRect();
+
+ bool skipsDraw() const { return m_skipsDraw; }
+
+ // Virtual for testing
+ virtual CCPrioritizedTextureManager* textureManager() const;
+
+private:
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+
+ void createTilerIfNeeded();
+ void setTilingOption(TilingOption);
+
+ bool tileOnlyNeedsPartialUpdate(UpdatableTile*);
+ bool tileNeedsBufferedUpdate(UpdatableTile*);
+
+ void markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const CCOcclusionTracker*);
+
+ bool updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&, bool& didPaint);
+ bool haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions);
+ IntRect markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions);
+ void updateTileTextures(const IntRect& paintRect, int left, int top, int right, int bottom, CCTextureUpdateQueue&, const CCOcclusionTracker*, CCRenderingStats&);
+
+ UpdatableTile* tileAt(int, int) const;
+ UpdatableTile* createTile(int, int);
+
+ GC3Denum m_textureFormat;
+ bool m_skipsDraw;
+ bool m_failedUpdate;
+ LayerTextureUpdater::SampledTexelFormat m_sampledTexelFormat;
+
+ TilingOption m_tilingOption;
+ OwnPtr<CCLayerTilingData> m_tiler;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/TreeSynchronizer.cpp b/cc/TreeSynchronizer.cpp
new file mode 100644
index 0000000..29e676c
--- /dev/null
+++ b/cc/TreeSynchronizer.cpp
@@ -0,0 +1,112 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "TreeSynchronizer.h"
+
+#include "CCLayerImpl.h"
+#include "CCScrollbarAnimationController.h"
+#include "CCScrollbarLayerImpl.h"
+#include "LayerChromium.h"
+#include "ScrollbarLayerChromium.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+PassOwnPtr<CCLayerImpl> TreeSynchronizer::synchronizeTrees(LayerChromium* layerChromiumRoot, PassOwnPtr<CCLayerImpl> oldCCLayerImplRoot, CCLayerTreeHostImpl* hostImpl)
+{
+ OwnPtrCCLayerImplMap oldLayers;
+ RawPtrCCLayerImplMap newLayers;
+
+ collectExistingCCLayerImplRecursive(oldLayers, oldCCLayerImplRoot);
+
+ OwnPtr<CCLayerImpl> newTree = synchronizeTreeRecursive(newLayers, oldLayers, layerChromiumRoot, hostImpl);
+
+ updateScrollbarLayerPointersRecursive(newLayers, layerChromiumRoot);
+
+ return newTree.release();
+}
+
+void TreeSynchronizer::collectExistingCCLayerImplRecursive(OwnPtrCCLayerImplMap& oldLayers, PassOwnPtr<CCLayerImpl> popCCLayerImpl)
+{
+ OwnPtr<CCLayerImpl> ccLayerImpl = popCCLayerImpl;
+
+ if (!ccLayerImpl)
+ return;
+
+ Vector<OwnPtr<CCLayerImpl> >& children = ccLayerImpl->m_children;
+ for (size_t i = 0; i < children.size(); ++i)
+ collectExistingCCLayerImplRecursive(oldLayers, children[i].release());
+
+ collectExistingCCLayerImplRecursive(oldLayers, ccLayerImpl->m_maskLayer.release());
+ collectExistingCCLayerImplRecursive(oldLayers, ccLayerImpl->m_replicaLayer.release());
+
+ int id = ccLayerImpl->id();
+ oldLayers.set(id, ccLayerImpl.release());
+}
+
+PassOwnPtr<CCLayerImpl> TreeSynchronizer::reuseOrCreateCCLayerImpl(RawPtrCCLayerImplMap& newLayers, OwnPtrCCLayerImplMap& oldLayers, LayerChromium* layer)
+{
+ OwnPtr<CCLayerImpl> ccLayerImpl = oldLayers.take(layer->id());
+
+ if (!ccLayerImpl)
+ ccLayerImpl = layer->createCCLayerImpl();
+
+ newLayers.set(layer->id(), ccLayerImpl.get());
+ return ccLayerImpl.release();
+}
+
+PassOwnPtr<CCLayerImpl> TreeSynchronizer::synchronizeTreeRecursive(RawPtrCCLayerImplMap& newLayers, OwnPtrCCLayerImplMap& oldLayers, LayerChromium* layer, CCLayerTreeHostImpl* hostImpl)
+{
+ if (!layer)
+ return nullptr;
+
+ OwnPtr<CCLayerImpl> ccLayerImpl = reuseOrCreateCCLayerImpl(newLayers, oldLayers, layer);
+
+ ccLayerImpl->clearChildList();
+ const Vector<RefPtr<LayerChromium> >& children = layer->children();
+ for (size_t i = 0; i < children.size(); ++i)
+ ccLayerImpl->addChild(synchronizeTreeRecursive(newLayers, oldLayers, children[i].get(), hostImpl));
+
+ ccLayerImpl->setMaskLayer(synchronizeTreeRecursive(newLayers, oldLayers, layer->maskLayer(), hostImpl));
+ ccLayerImpl->setReplicaLayer(synchronizeTreeRecursive(newLayers, oldLayers, layer->replicaLayer(), hostImpl));
+
+ layer->pushPropertiesTo(ccLayerImpl.get());
+ ccLayerImpl->setLayerTreeHostImpl(hostImpl);
+
+ // Remove all dangling pointers. The pointers will be setup later in updateScrollbarLayerPointersRecursive phase
+ if (CCScrollbarAnimationController* scrollbarController = ccLayerImpl->scrollbarAnimationController()) {
+ scrollbarController->setHorizontalScrollbarLayer(0);
+ scrollbarController->setVerticalScrollbarLayer(0);
+ }
+
+ return ccLayerImpl.release();
+}
+
+void TreeSynchronizer::updateScrollbarLayerPointersRecursive(const RawPtrCCLayerImplMap& newLayers, LayerChromium* layer)
+{
+ if (!layer)
+ return;
+
+ const Vector<RefPtr<LayerChromium> >& children = layer->children();
+ for (size_t i = 0; i < children.size(); ++i)
+ updateScrollbarLayerPointersRecursive(newLayers, children[i].get());
+
+ ScrollbarLayerChromium* scrollbarLayer = layer->toScrollbarLayerChromium();
+ if (!scrollbarLayer)
+ return;
+
+ CCScrollbarLayerImpl* ccScrollbarLayerImpl = static_cast<CCScrollbarLayerImpl*>(newLayers.get(scrollbarLayer->id()));
+ ASSERT(ccScrollbarLayerImpl);
+ CCLayerImpl* ccScrollLayerImpl = newLayers.get(scrollbarLayer->scrollLayerId());
+ ASSERT(ccScrollLayerImpl);
+
+ if (ccScrollbarLayerImpl->orientation() == WebKit::WebScrollbar::Horizontal)
+ ccScrollLayerImpl->setHorizontalScrollbarLayer(ccScrollbarLayerImpl);
+ else
+ ccScrollLayerImpl->setVerticalScrollbarLayer(ccScrollbarLayerImpl);
+}
+
+} // namespace WebCore
diff --git a/cc/TreeSynchronizer.h b/cc/TreeSynchronizer.h
new file mode 100644
index 0000000..5c59e5b
--- /dev/null
+++ b/cc/TreeSynchronizer.h
@@ -0,0 +1,41 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TreeSynchronizer_h
+#define TreeSynchronizer_h
+
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCLayerImpl;
+class CCLayerTreeHostImpl;
+class LayerChromium;
+
+class TreeSynchronizer {
+WTF_MAKE_NONCOPYABLE(TreeSynchronizer);
+public:
+ // Accepts a LayerChromium tree and returns a reference to a CCLayerImpl tree that duplicates the structure
+ // of the LayerChromium tree, reusing the CCLayerImpls in the tree provided by oldCCLayerImplRoot if possible.
+ static PassOwnPtr<CCLayerImpl> synchronizeTrees(LayerChromium* layerRoot, PassOwnPtr<CCLayerImpl> oldCCLayerImplRoot, CCLayerTreeHostImpl*);
+
+private:
+ TreeSynchronizer(); // Not instantiable.
+
+ typedef HashMap<int, OwnPtr<CCLayerImpl> > OwnPtrCCLayerImplMap;
+ typedef HashMap<int, CCLayerImpl*> RawPtrCCLayerImplMap;
+
+ // Declared as static member functions so they can access functions on LayerChromium as a friend class.
+ static PassOwnPtr<CCLayerImpl> reuseOrCreateCCLayerImpl(RawPtrCCLayerImplMap& newLayers, OwnPtrCCLayerImplMap& oldLayers, LayerChromium*);
+ static void collectExistingCCLayerImplRecursive(OwnPtrCCLayerImplMap& oldLayers, PassOwnPtr<CCLayerImpl>);
+ static PassOwnPtr<CCLayerImpl> synchronizeTreeRecursive(RawPtrCCLayerImplMap& newLayers, OwnPtrCCLayerImplMap& oldLayers, LayerChromium*, CCLayerTreeHostImpl*);
+ static void updateScrollbarLayerPointersRecursive(const RawPtrCCLayerImplMap& newLayers, LayerChromium*);
+};
+
+} // namespace WebCore
+
+#endif // TreeSynchronizer_h
diff --git a/cc/UnthrottledTextureUploader.h b/cc/UnthrottledTextureUploader.h
new file mode 100644
index 0000000..36a6cb4
--- /dev/null
+++ b/cc/UnthrottledTextureUploader.h
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UnthrottledTextureUploader_h
+#define UnthrottledTextureUploader_h
+
+#include "CCResourceProvider.h"
+#include "TextureUploader.h"
+
+namespace WebCore {
+
+class UnthrottledTextureUploader : public TextureUploader {
+ WTF_MAKE_NONCOPYABLE(UnthrottledTextureUploader);
+public:
+ static PassOwnPtr<UnthrottledTextureUploader> create()
+ {
+ return adoptPtr(new UnthrottledTextureUploader());
+ }
+ virtual ~UnthrottledTextureUploader() { }
+
+ virtual bool isBusy() OVERRIDE { return false; }
+ virtual void beginUploads() OVERRIDE { }
+ virtual void endUploads() OVERRIDE { }
+ virtual void uploadTexture(CCResourceProvider* resourceProvider, Parameters upload) OVERRIDE { upload.texture->updateRect(resourceProvider, upload.sourceRect, upload.destOffset); }
+
+protected:
+ UnthrottledTextureUploader() { }
+};
+
+}
+
+#endif
diff --git a/cc/VideoLayerChromium.cpp b/cc/VideoLayerChromium.cpp
new file mode 100644
index 0000000..7bfee0c
--- /dev/null
+++ b/cc/VideoLayerChromium.cpp
@@ -0,0 +1,37 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "VideoLayerChromium.h"
+
+#include "CCVideoLayerImpl.h"
+
+namespace WebCore {
+
+PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(WebKit::WebVideoFrameProvider* provider)
+{
+ return adoptRef(new VideoLayerChromium(provider));
+}
+
+VideoLayerChromium::VideoLayerChromium(WebKit::WebVideoFrameProvider* provider)
+ : LayerChromium()
+ , m_provider(provider)
+{
+ ASSERT(m_provider);
+}
+
+VideoLayerChromium::~VideoLayerChromium()
+{
+}
+
+PassOwnPtr<CCLayerImpl> VideoLayerChromium::createCCLayerImpl()
+{
+ return CCVideoLayerImpl::create(m_layerId, m_provider);
+}
+
+} // namespace WebCore
+
+#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/cc/VideoLayerChromium.h b/cc/VideoLayerChromium.h
new file mode 100644
index 0000000..88671d0
--- /dev/null
+++ b/cc/VideoLayerChromium.h
@@ -0,0 +1,40 @@
+// Copyright 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef VideoLayerChromium_h
+#define VideoLayerChromium_h
+
+#if USE(ACCELERATED_COMPOSITING)
+
+#include "LayerChromium.h"
+
+namespace WebKit {
+class WebVideoFrameProvider;
+}
+
+namespace WebCore {
+
+class CCVideoLayerImpl;
+
+// A Layer that contains a Video element.
+class VideoLayerChromium : public LayerChromium {
+public:
+
+ static PassRefPtr<VideoLayerChromium> create(WebKit::WebVideoFrameProvider*);
+ virtual ~VideoLayerChromium();
+
+ virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE;
+
+private:
+ explicit VideoLayerChromium(WebKit::WebVideoFrameProvider*);
+
+ // This pointer is only for passing to CCVideoLayerImpl's constructor. It should never be dereferenced by this class.
+ WebKit::WebVideoFrameProvider* m_provider;
+};
+
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+#endif
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 7b9b665..115a42f 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -5,12 +5,247 @@
{
'variables': {
'chromium_code': 0,
+ 'use_libcc_for_compositor%': 0,
+ 'cc_source_files': [
+ 'BitmapCanvasLayerTextureUpdater.cpp',
+ 'BitmapCanvasLayerTextureUpdater.h',
+ 'BitmapSkPictureCanvasLayerTextureUpdater.cpp',
+ 'BitmapSkPictureCanvasLayerTextureUpdater.h',
+ 'CCActiveAnimation.cpp',
+ 'CCActiveAnimation.h',
+ 'CCActiveGestureAnimation.cpp',
+ 'CCActiveGestureAnimation.h',
+ 'CCAnimationCurve.cpp',
+ 'CCAnimationCurve.h',
+ 'CCAnimationEvents.h',
+ 'CCCheckerboardDrawQuad.cpp',
+ 'CCCheckerboardDrawQuad.h',
+ 'CCCompletionEvent.h',
+ 'CCDamageTracker.cpp',
+ 'CCDamageTracker.h',
+ 'CCDebugBorderDrawQuad.cpp',
+ 'CCDebugBorderDrawQuad.h',
+ 'CCDebugRectHistory.cpp',
+ 'CCDebugRectHistory.h',
+ 'CCDelayBasedTimeSource.cpp',
+ 'CCDelayBasedTimeSource.h',
+ 'CCDirectRenderer.cpp',
+ 'CCDirectRenderer.h',
+ 'CCDrawQuad.cpp',
+ 'CCDrawQuad.h',
+ 'CCFontAtlas.cpp',
+ 'CCFontAtlas.h',
+ 'CCFrameRateController.cpp',
+ 'CCFrameRateController.h',
+ 'CCFrameRateCounter.cpp',
+ 'CCFrameRateCounter.h',
+ 'CCGestureCurve.h',
+ 'CCGraphicsContext.h',
+ 'CCHeadsUpDisplayLayerImpl.cpp',
+ 'CCHeadsUpDisplayLayerImpl.h',
+ 'CCIOSurfaceDrawQuad.cpp',
+ 'CCIOSurfaceDrawQuad.h',
+ 'CCIOSurfaceLayerImpl.cpp',
+ 'CCIOSurfaceLayerImpl.h',
+ 'CCInputHandler.h',
+ 'CCKeyframedAnimationCurve.cpp',
+ 'CCKeyframedAnimationCurve.h',
+ 'CCLayerAnimationController.cpp',
+ 'CCLayerAnimationController.h',
+ 'CCLayerImpl.cpp',
+ 'CCLayerImpl.h',
+ 'CCLayerIterator.cpp',
+ 'CCLayerIterator.h',
+ 'CCLayerQuad.cpp',
+ 'CCLayerQuad.h',
+ 'CCLayerSorter.cpp',
+ 'CCLayerSorter.h',
+ 'CCLayerTilingData.cpp',
+ 'CCLayerTilingData.h',
+ 'CCLayerTreeHost.cpp',
+ 'CCLayerTreeHost.h',
+ 'CCLayerTreeHostCommon.cpp',
+ 'CCLayerTreeHostCommon.h',
+ 'CCLayerTreeHostImpl.cpp',
+ 'CCLayerTreeHostImpl.h',
+ 'CCMathUtil.cpp',
+ 'CCMathUtil.h',
+ 'CCOcclusionTracker.cpp',
+ 'CCOcclusionTracker.h',
+ 'CCOverdrawMetrics.cpp',
+ 'CCOverdrawMetrics.h',
+ 'CCPageScaleAnimation.cpp',
+ 'CCPageScaleAnimation.h',
+ 'CCPrioritizedTexture.cpp',
+ 'CCPrioritizedTexture.h',
+ 'CCPrioritizedTextureManager.cpp',
+ 'CCPrioritizedTextureManager.h',
+ 'CCPriorityCalculator.cpp',
+ 'CCPriorityCalculator.h',
+ 'CCProxy.cpp',
+ 'CCProxy.h',
+ 'CCQuadCuller.cpp',
+ 'CCQuadCuller.h',
+ 'CCQuadSink.h',
+ 'CCRenderPass.cpp',
+ 'CCRenderPass.h',
+ 'CCRenderPassDrawQuad.cpp',
+ 'CCRenderPassDrawQuad.h',
+ 'CCRenderSurface.cpp',
+ 'CCRenderSurface.h',
+ 'CCRenderSurfaceFilters.cpp',
+ 'CCRenderSurfaceFilters.h',
+ 'CCRenderer.h',
+ 'CCRendererGL.cpp',
+ 'CCRendererGL.h',
+ 'CCRenderingStats.h',
+ 'CCResourceProvider.cpp',
+ 'CCResourceProvider.h',
+ 'CCScheduler.cpp',
+ 'CCScheduler.h',
+ 'CCSchedulerStateMachine.cpp',
+ 'CCSchedulerStateMachine.h',
+ 'CCScopedTexture.cpp',
+ 'CCScopedTexture.h',
+ 'CCScopedThreadProxy.h',
+ 'CCScrollbarAnimationController.cpp',
+ 'CCScrollbarAnimationController.h',
+ 'CCScrollbarAnimationControllerLinearFade.cpp',
+ 'CCScrollbarAnimationControllerLinearFade.h',
+ 'CCScrollbarLayerImpl.cpp',
+ 'CCScrollbarLayerImpl.h',
+ 'CCSettings.cpp',
+ 'CCSettings.h',
+ 'CCSharedQuadState.cpp',
+ 'CCSharedQuadState.h',
+ 'CCSingleThreadProxy.cpp',
+ 'CCSingleThreadProxy.h',
+ 'CCSolidColorDrawQuad.cpp',
+ 'CCSolidColorDrawQuad.h',
+ 'CCSolidColorLayerImpl.cpp',
+ 'CCSolidColorLayerImpl.h',
+ 'CCStreamVideoDrawQuad.cpp',
+ 'CCStreamVideoDrawQuad.h',
+ 'CCTexture.cpp',
+ 'CCTexture.h',
+ 'CCTextureDrawQuad.cpp',
+ 'CCTextureDrawQuad.h',
+ 'CCTextureLayerImpl.cpp',
+ 'CCTextureLayerImpl.h',
+ 'CCTextureUpdateController.cpp',
+ 'CCTextureUpdateController.h',
+ 'CCTextureUpdateQueue.cpp',
+ 'CCTextureUpdateQueue.h',
+ 'CCThread.h',
+ 'CCThreadProxy.cpp',
+ 'CCThreadProxy.h',
+ 'CCThreadTask.h',
+ 'CCTileDrawQuad.cpp',
+ 'CCTileDrawQuad.h',
+ 'CCTiledLayerImpl.cpp',
+ 'CCTiledLayerImpl.h',
+ 'CCTimeSource.h',
+ 'CCTimer.cpp',
+ 'CCTimer.h',
+ 'CCTimingFunction.cpp',
+ 'CCTimingFunction.h',
+ 'CCVideoLayerImpl.cpp',
+ 'CCVideoLayerImpl.h',
+ 'CCYUVVideoDrawQuad.cpp',
+ 'CCYUVVideoDrawQuad.h',
+ 'CanvasLayerTextureUpdater.cpp',
+ 'CanvasLayerTextureUpdater.h',
+ 'ContentLayerChromium.cpp',
+ 'ContentLayerChromium.h',
+ 'FrameBufferSkPictureCanvasLayerTextureUpdater.cpp',
+ 'FrameBufferSkPictureCanvasLayerTextureUpdater.h',
+ 'GeometryBinding.cpp',
+ 'GeometryBinding.h',
+ 'HeadsUpDisplayLayerChromium.cpp',
+ 'HeadsUpDisplayLayerChromium.h',
+ 'IOSurfaceLayerChromium.cpp',
+ 'IOSurfaceLayerChromium.h',
+ 'ImageLayerChromium.cpp',
+ 'ImageLayerChromium.h',
+ 'LayerChromium.cpp',
+ 'LayerChromium.h',
+ 'LayerPainterChromium.h',
+ 'LayerTextureSubImage.cpp',
+ 'LayerTextureSubImage.h',
+ 'LayerTextureUpdater.h',
+ 'PlatformColor.h',
+ 'ProgramBinding.cpp',
+ 'ProgramBinding.h',
+ 'RateLimiter.cpp',
+ 'RateLimiter.h',
+ 'RenderSurfaceChromium.cpp',
+ 'RenderSurfaceChromium.h',
+ 'ScrollbarLayerChromium.cpp',
+ 'ScrollbarLayerChromium.h',
+ 'ShaderChromium.cpp',
+ 'ShaderChromium.h',
+ 'SkPictureCanvasLayerTextureUpdater.cpp',
+ 'SkPictureCanvasLayerTextureUpdater.h',
+ 'SolidColorLayerChromium.cpp',
+ 'SolidColorLayerChromium.h',
+ 'TextureCopier.cpp',
+ 'TextureCopier.h',
+ 'TextureLayerChromium.cpp',
+ 'TextureLayerChromium.h',
+ 'TextureUploader.h',
+ 'ThrottledTextureUploader.cpp',
+ 'ThrottledTextureUploader.h',
+ 'UnthrottledTextureUploader.h',
+ 'TiledLayerChromium.cpp',
+ 'TiledLayerChromium.h',
+ 'TreeSynchronizer.cpp',
+ 'TreeSynchronizer.h',
+ 'VideoLayerChromium.cpp',
+ 'VideoLayerChromium.h',
+ ],
},
-
'targets': [
{
'target_name': 'cc',
'type': 'static_library',
- }
- ]
+ 'conditions': [
+ ['use_libcc_for_compositor==1', {
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '<(DEPTH)/skia/skia.gyp:skia',
+ '<(DEPTH)/third_party/WebKit/Source/Platform/Platform.gyp/Platform.gyp:webkit_platform',
+ # We have to depend on WTF directly to pick up the correct defines for WTF headers - for instance USE_SYSTEM_MALLOC.
+ '<(DEPTH)/third_party/WebKit/Source/WTF/WTF.gyp/WTF.gyp:wtf',
+ '<(DEPTH)/ui/gl/gl.gyp:gl',
+ '<(DEPTH)/ui/ui.gyp:ui',
+ ],
+ 'defines': [
+ 'WTF_USE_ACCELERATED_COMPOSITING=1',
+ ],
+ 'include_dirs': [
+ 'stubs',
+ ],
+ 'sources': [
+ '<@(cc_source_files)',
+ 'stubs/Extensions3DChromium.h',
+ 'stubs/Extensions3D.h',
+ 'stubs/GraphicsContext3D.h',
+ 'stubs/GraphicsTypes3D.h',
+ 'stubs/NotImplemented.h',
+ 'stubs/Region.h',
+ 'stubs/ScrollbarThemeClient.h',
+ 'stubs/ScrollTypes.h',
+ 'stubs/SharedGraphicsContext3D.h',
+ 'stubs/SkiaUtils.h',
+ 'stubs/TextStream.h',
+ 'stubs/TilingData.h',
+ 'stubs/Timer.h',
+ 'stubs/TraceEvent.h',
+ 'stubs/UnitBezier.h',
+ ],
+ }],
+ ],
+ },
+ ],
}
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
new file mode 100644
index 0000000..5e873f8
--- /dev/null
+++ b/cc/cc_tests.gyp
@@ -0,0 +1,105 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 0,
+ 'use_libcc_for_compositor%': 0,
+ 'cc_tests_source_files': [
+ 'CCActiveAnimationTest.cpp',
+ 'CCDamageTrackerTest.cpp',
+ 'CCDelayBasedTimeSourceTest.cpp',
+ 'CCFrameRateControllerTest.cpp',
+ 'CCKeyframedAnimationCurveTest.cpp',
+ 'CCLayerAnimationControllerTest.cpp',
+ 'CCLayerImplTest.cpp',
+ 'CCLayerIteratorTest.cpp',
+ 'CCLayerQuadTest.cpp',
+ 'CCLayerSorterTest.cpp',
+ 'CCLayerTreeHostCommonTest.cpp',
+ 'CCLayerTreeHostImplTest.cpp',
+ 'CCLayerTreeHostTest.cpp',
+ 'CCMathUtilTest.cpp',
+ 'CCOcclusionTrackerTest.cpp',
+ 'CCPrioritizedTextureTest.cpp',
+ 'CCQuadCullerTest.cpp',
+ 'CCRenderSurfaceFiltersTest.cpp',
+ 'CCRenderSurfaceTest.cpp',
+ 'CCResourceProviderTest.cpp',
+ 'CCSchedulerStateMachineTest.cpp',
+ 'CCSchedulerTest.cpp',
+ 'CCSchedulerTest.cpp',
+ 'CCScopedTextureTest.cpp',
+ 'CCScrollbarAnimationControllerLinearFadeTest.cpp',
+ 'CCSolidColorLayerImplTest.cpp',
+ 'CCTextureUpdateControllerTest.cpp',
+ 'CCThreadTaskTest.cpp',
+ 'CCThreadedTest.cpp',
+ 'CCThreadedTest.h',
+ 'CCTiledLayerImplTest.cpp',
+ 'CCTimerTest.cpp',
+ 'test/CCAnimationTestCommon.cpp',
+ 'test/CCAnimationTestCommon.h',
+ 'test/CCLayerTestCommon.cpp',
+ 'test/CCLayerTestCommon.h',
+ 'test/CCLayerTreeTestCommon.h',
+ 'test/CCLayerTreeTestCommon.h',
+ 'test/CCOcclusionTrackerTestCommon.h',
+ 'test/CCSchedulerTestCommon.h',
+ 'test/CCSchedulerTestCommon.h',
+ 'test/CCTestCommon.h',
+ 'test/CCTiledLayerTestCommon.cpp',
+ 'test/CCTiledLayerTestCommon.h',
+ 'test/CompositorFakeWebGraphicsContext3D.h',
+ 'test/FakeCCGraphicsContext.h',
+ 'test/FakeCCLayerTreeHostClient.h',
+ 'test/FakeGraphicsContext3DTest.cpp',
+ 'test/FakeWebCompositorOutputSurface.h',
+ 'test/FakeWebGraphicsContext3D.h',
+ 'test/FakeWebScrollbarThemeGeometry.h',
+ 'test/MockCCQuadCuller.h',
+ ]
+ },
+ 'conditions': [
+ ['use_libcc_for_compositor==1 and component!="shared_library"', {
+ 'targets': [
+ {
+ 'target_name': 'cc_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/webkit/support/webkit_support.gyp:webkit_support',
+ '<(DEPTH)/skia/skia.gyp:skia',
+ # We have to depend on WTF directly to pick up the correct defines for WTF headers - for instance USE_SYSTEM_MALLOC.
+ '<(DEPTH)/third_party/WebKit/Source/WTF/WTF.gyp/WTF.gyp:wtf',
+ '<(DEPTH)/third_party/WebKit/Source/Platform/Platform.gyp/Platform.gyp:webkit_platform',
+ 'cc.gyp:cc',
+ ],
+ 'defines': [
+ 'WTF_USE_ACCELERATED_COMPOSITING=1',
+ ],
+ 'include_dirs': [
+ 'stubs',
+ 'test',
+ '.',
+ ],
+ 'sources': [
+ '<@(cc_tests_source_files)',
+ 'test/run_all_unittests.cc',
+ ],
+ },
+ ],
+ }, {
+ 'targets': [
+ {
+ 'target_name': 'cc_unittests',
+ 'type': 'none',
+ }
+ ]
+ }],
+ ],
+}
+
diff --git a/cc/copyfiles.py b/cc/copyfiles.py
new file mode 100644
index 0000000..8366ccb
--- /dev/null
+++ b/cc/copyfiles.py
@@ -0,0 +1,78 @@
+# Copyright (c) 2012 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import re
+import shutil
+
+prefixes = ["../third_party/WebKit/Source/WebCore/platform/graphics/chromium",
+ "../third_party/WebKit/Source/WebCore/platform/graphics/chromium/cc",
+ "../third_party/WebKit/Source/WebKit/chromium/tests"]
+
+def Copy(name):
+ src = name
+ dst = name
+ if name.startswith("test/"):
+ src = src[5:]
+ fullsrc = ""
+ for prefix in prefixes:
+ candidate = "%s/%s" % (prefix, src)
+ if os.path.exists(candidate):
+ fullsrc = candidate
+ break
+ assert fullsrc != ""
+ shutil.copyfile(fullsrc, dst)
+ print "copying from %s to %s" % (fullsrc, dst)
+ return dst
+
+def FixCopyrightHeaderText(text, year):
+ header_template = """// Copyright %s The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+"""
+
+ while text[0].find(" */") == -1:
+ text = text[1:]
+ text = text[1:]
+
+ return (header_template % year) + "".join(text)
+
+def FixCopyrightHeader(filepath):
+ with open(filepath, "r") as f:
+ text = f.readlines()
+
+ if filepath.endswith("CCPageScaleAnimation.cpp"):
+ year = 2012
+ else:
+ pattern = ".*Copyright \(C\) (20[01][0-9])"
+ m = re.match(pattern, text[0])
+ if m == None:
+ m = re.match(pattern, text[1])
+ assert m
+ year = m.group(1)
+
+ fixed_text = FixCopyrightHeaderText(text, year)
+ with open(filepath, "w") as f:
+ f.write(fixed_text)
+
+def Readfile(gypfile):
+ with open(gypfile, "r") as cc_gyp:
+ obj = eval(cc_gyp.read())
+ return obj
+
+def Main():
+ files = Readfile("cc.gyp")['variables']['cc_source_files']
+ for f in files:
+ dst = Copy(f)
+ FixCopyrightHeader(dst)
+
+ files = Readfile("cc_tests.gyp")['variables']['cc_tests_source_files']
+ for f in files:
+ dst = Copy(f)
+ FixCopyrightHeader(dst)
+
+if __name__ == '__main__':
+ import sys
+ os.chdir(os.path.dirname(__file__))
+ sys.exit(Main())
diff --git a/cc/stubs/Extensions3D.h b/cc/stubs/Extensions3D.h
new file mode 100644
index 0000000..b2e34d7
--- /dev/null
+++ b/cc/stubs/Extensions3D.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_EXTENSIONS3D_H_
+#define CC_STUBS_EXTENSIONS3D_H_
+
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+namespace WebCore {
+
+class Extensions3D {
+public:
+ enum {
+ TEXTURE_RECTANGLE_ARB = GL_TEXTURE_RECTANGLE_ARB,
+ BGRA_EXT = GL_BGRA_EXT,
+ RGBA8_OES = GL_RGBA8_OES,
+ };
+};
+
+}
+
+
+#endif // CC_STUBS_EXTENSIONS3D_H_
+
diff --git a/cc/stubs/Extensions3DChromium.h b/cc/stubs/Extensions3DChromium.h
new file mode 100644
index 0000000..335023c
--- /dev/null
+++ b/cc/stubs/Extensions3DChromium.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_EXTENSIONS3DCHROMIUM_H_
+#define CC_STUBS_EXTENSIONS3DCHROMIUM_H_
+
+#include "Extensions3D.h"
+
+// These enum names collides with gl2ext.h, so we just redefine them.
+#ifdef GL_TEXTURE_EXTERNAL_OES
+#undef GL_TEXTURE_EXTERNAL_OES
+#endif
+#ifdef GL_TEXTURE_USAGE_ANGLE
+#undef GL_TEXTURE_USAGE_ANGLE
+#endif
+#ifdef GL_FRAMEBUFFER_ATTACHMENT_ANGLE
+#undef GL_FRAMEBUFFER_ATTACHMENT_ANGLE
+#endif
+
+namespace WebCore {
+
+class Extensions3DChromium {
+public:
+ enum {
+ // GL_OES_EGL_image_external
+ GL_TEXTURE_EXTERNAL_OES = 0x8D65,
+
+ // GL_CHROMIUM_map_sub (enums inherited from GL_ARB_vertex_buffer_object)
+ READ_ONLY = 0x88B8,
+ WRITE_ONLY = 0x88B9,
+
+ // GL_ANGLE_texture_usage
+ GL_TEXTURE_USAGE_ANGLE = 0x93A2,
+ GL_FRAMEBUFFER_ATTACHMENT_ANGLE = 0x93A3,
+
+ // GL_EXT_texture_storage
+ BGRA8_EXT = 0x93A1,
+
+ // GL_EXT_occlusion_query_boolean
+ ANY_SAMPLES_PASSED_EXT = 0x8C2F,
+ ANY_SAMPLES_PASSED_CONSERVATIVE_EXT = 0x8D6A,
+ CURRENT_QUERY_EXT = 0x8865,
+ QUERY_RESULT_EXT = 0x8866,
+ QUERY_RESULT_AVAILABLE_EXT = 0x8867,
+
+ // GL_CHROMIUM_command_buffer_query
+ COMMANDS_ISSUED_CHROMIUM = 0x84F2
+ };
+};
+
+}
+
+#endif // CC_STUBS_EXTENSIONS3DCHROMIUM_H_
+
diff --git a/cc/stubs/GraphicsContext3D.h b/cc/stubs/GraphicsContext3D.h
new file mode 100644
index 0000000..41404f2
--- /dev/null
+++ b/cc/stubs/GraphicsContext3D.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_GRAPHICSCONTEXT3D_H_
+#define CC_STUBS_GRAPHICSCONTEXT3D_H_
+
+#include "GraphicsTypes3D.h"
+#include "IntSize.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace WebCore {
+
+class GraphicsContext3D {
+public:
+ enum SourceDataFormat { SourceFormatRGBA8, SourceFormatBGRA8 };
+ static bool computeFormatAndTypeParameters(unsigned, unsigned, unsigned* componentsPerPixel, unsigned* bytesPerComponent)
+ {
+ *componentsPerPixel = 4;
+ *bytesPerComponent = 1;
+ return true;
+ }
+
+ enum {
+ ARRAY_BUFFER = GL_ARRAY_BUFFER,
+ BLEND = GL_BLEND,
+ CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
+ COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0,
+ COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT,
+ COMPILE_STATUS = GL_COMPILE_STATUS,
+ CULL_FACE = GL_CULL_FACE,
+ DEPTH_TEST = GL_DEPTH_TEST,
+ ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER,
+ EXTENSIONS = GL_EXTENSIONS,
+ FLOAT = GL_FLOAT,
+ FRAGMENT_SHADER = GL_FRAGMENT_SHADER,
+ FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE,
+ FRAMEBUFFER = GL_FRAMEBUFFER,
+ INVALID_ENUM = GL_INVALID_ENUM,
+ INVALID_VALUE = GL_INVALID_VALUE,
+ LINEAR = GL_LINEAR,
+ LINE_LOOP = GL_LINE_LOOP ,
+ LINK_STATUS = GL_LINK_STATUS,
+ LUMINANCE = GL_LUMINANCE,
+ MAX_TEXTURE_SIZE = GL_MAX_TEXTURE_SIZE,
+ NEAREST = GL_NEAREST,
+ NO_ERROR = GL_NO_ERROR,
+ ONE = GL_ONE,
+ ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA,
+ RGBA = GL_RGBA,
+ RGB = GL_RGB,
+ SCISSOR_TEST = GL_SCISSOR_TEST,
+ SRC_ALPHA = GL_SRC_ALPHA,
+ STATIC_DRAW = GL_STATIC_DRAW,
+ TEXTURE0 = GL_TEXTURE0,
+ TEXTURE1 = GL_TEXTURE1,
+ TEXTURE_2D = GL_TEXTURE_2D,
+ TEXTURE2 = GL_TEXTURE2,
+ TEXTURE3 = GL_TEXTURE3,
+ TEXTURE_MAG_FILTER = GL_TEXTURE_MAG_FILTER,
+ TEXTURE_MIN_FILTER = GL_TEXTURE_MIN_FILTER,
+ TEXTURE_WRAP_S = GL_TEXTURE_WRAP_S,
+ TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T,
+ TRIANGLES = GL_TRIANGLES,
+ TRIANGLE_FAN = GL_TRIANGLE_FAN,
+ UNSIGNED_BYTE = GL_UNSIGNED_BYTE,
+ UNSIGNED_SHORT = GL_UNSIGNED_SHORT,
+ VERTEX_SHADER = GL_VERTEX_SHADER,
+ ZERO = GL_ZERO,
+ };
+};
+
+}
+
+#endif // CC_STUBS_GRAPHICSCONTEXT3D_H_
+
diff --git a/cc/stubs/GraphicsTypes3D.h b/cc/stubs/GraphicsTypes3D.h
new file mode 100644
index 0000000..a01414e
--- /dev/null
+++ b/cc/stubs/GraphicsTypes3D.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_GRAPHICSTYPES3D_H_
+#define CC_STUBS_GRAPHICSTYPES3D_H_
+
+#define NullPlatform3DObject 0
+
+typedef unsigned char GC3Dboolean;
+typedef unsigned GC3Denum;
+typedef unsigned GC3Duint;
+typedef unsigned Platform3DObject;
+typedef int GC3Dint;
+typedef signed char GC3Dbyte;
+
+#endif // CC_STUBS_GRAPHICSTYPES3D_H_
+
diff --git a/cc/stubs/NotImplemented.h b/cc/stubs/NotImplemented.h
new file mode 100644
index 0000000..7c6bba5
--- /dev/null
+++ b/cc/stubs/NotImplemented.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_NOTIMPLEMENTED_H
+#define CC_STUBS_NOTIMPLEMENTED_H
+
+#define notImplemented() do { } while(0)
+
+#endif // CC_STUBS_NOTIMPLEMENTED_H
diff --git a/cc/stubs/Region.h b/cc/stubs/Region.h
new file mode 100644
index 0000000..c7f8f50
--- /dev/null
+++ b/cc/stubs/Region.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_REGION_H_
+#define CC_STUBS_REGION_H_
+
+#include "third_party/WebKit/Source/WebCore/platform/graphics/Region.h"
+
+#endif // CC_STUBS_REGION_H_
diff --git a/cc/stubs/SkiaUtils.h b/cc/stubs/SkiaUtils.h
new file mode 100644
index 0000000..f5ceb4eb
--- /dev/null
+++ b/cc/stubs/SkiaUtils.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_SKIAUTILS_H_
+#define CC_STUBS_SKIAUTILS_H_
+
+namespace WebCore {
+
+inline SkScalar WebCoreFloatToSkScalar(float f)
+{
+ return SkFloatToScalar(isfinite(f) ? f : 0);
+}
+
+}
+
+#endif // CC_STUBS_SKIAUTILS_H_
diff --git a/cc/stubs/TextStream.h b/cc/stubs/TextStream.h
new file mode 100644
index 0000000..a9d02d6
--- /dev/null
+++ b/cc/stubs/TextStream.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_TEXTSTREAM_H_
+#define CC_STUBS_TEXTSTREAM_H_
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class TextStream {
+public:
+ TextStream& operator<<(const String&) { return *this; }
+ TextStream& operator<<(const char*) { return *this; }
+ TextStream& operator<<(int) { return *this; }
+ String release() { return ""; }
+};
+
+}
+
+#endif // CC_STUBS_TEXTSTREAM_H_
+
diff --git a/cc/stubs/TilingData.h b/cc/stubs/TilingData.h
new file mode 100644
index 0000000..03e48eb
--- /dev/null
+++ b/cc/stubs/TilingData.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_STUBS_TILINGDATA_H_
+#define CC_STUBS_TILINGDATA_H_
+
+#include "third_party/WebKit/Source/WebCore/platform/graphics/gpu/TilingData.h"
+
+#endif // CC_STUBS_TILINGDATA_H_
diff --git a/cc/stubs/TraceEvent.h b/cc/stubs/TraceEvent.h
new file mode 100644
index 0000000..75587a3
--- /dev/null
+++ b/cc/stubs/TraceEvent.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Chromium's LOG() macro collides with one from WTF.
+#ifdef LOG
+#undef LOG
+#endif
+
+#include "base/debug/trace_event.h"
diff --git a/cc/stubs/UnitBezier.h b/cc/stubs/UnitBezier.h
new file mode 100644
index 0000000..90f810c
--- /dev/null
+++ b/cc/stubs/UnitBezier.h
@@ -0,0 +1,6 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(jamesr): Remove or refactor this dependency.
+#include "third_party/WebKit/Source/WebCore/platform/graphics/UnitBezier.h"
diff --git a/cc/test/CCAnimationTestCommon.cpp b/cc/test/CCAnimationTestCommon.cpp
new file mode 100644
index 0000000..db09228
--- /dev/null
+++ b/cc/test/CCAnimationTestCommon.cpp
@@ -0,0 +1,156 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCAnimationTestCommon.h"
+
+#include "CCKeyframedAnimationCurve.h"
+#include "CCLayerAnimationController.h"
+#include "CCLayerImpl.h"
+#include "LayerChromium.h"
+#include <public/WebTransformOperations.h>
+
+using namespace WebCore;
+
+namespace {
+
+template <class Target>
+void addOpacityTransition(Target& target, double duration, float startOpacity, float endOpacity, bool useTimingFunction)
+{
+ OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create());
+
+ if (duration > 0)
+ curve->addKeyframe(CCFloatKeyframe::create(0, startOpacity, useTimingFunction ? nullptr : CCEaseTimingFunction::create()));
+ curve->addKeyframe(CCFloatKeyframe::create(duration, endOpacity, nullptr));
+
+ OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), 0, 0, CCActiveAnimation::Opacity));
+ animation->setNeedsSynchronizedStartTime(true);
+
+ target.addAnimation(animation.release());
+}
+
+template <class Target>
+void addAnimatedTransform(Target& target, double duration, int deltaX, int deltaY)
+{
+ static int id = 0;
+ OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create());
+
+ if (duration > 0) {
+ WebKit::WebTransformOperations startOperations;
+ startOperations.appendTranslate(deltaX, deltaY, 0);
+ curve->addKeyframe(CCTransformKeyframe::create(0, startOperations, nullptr));
+ }
+
+ WebKit::WebTransformOperations operations;
+ operations.appendTranslate(deltaX, deltaY, 0);
+ curve->addKeyframe(CCTransformKeyframe::create(duration, operations, nullptr));
+
+ OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), id++, 0, CCActiveAnimation::Transform));
+ animation->setNeedsSynchronizedStartTime(true);
+
+ target.addAnimation(animation.release());
+}
+
+} // namespace
+
+namespace WebKitTests {
+
+FakeFloatAnimationCurve::FakeFloatAnimationCurve()
+{
+}
+
+FakeFloatAnimationCurve::~FakeFloatAnimationCurve()
+{
+}
+
+PassOwnPtr<WebCore::CCAnimationCurve> FakeFloatAnimationCurve::clone() const
+{
+ return adoptPtr(new FakeFloatAnimationCurve);
+}
+
+FakeTransformTransition::FakeTransformTransition(double duration)
+ : m_duration(duration)
+{
+}
+
+FakeTransformTransition::~FakeTransformTransition()
+{
+}
+
+WebKit::WebTransformationMatrix FakeTransformTransition::getValue(double time) const
+{
+ return WebKit::WebTransformationMatrix();
+}
+
+PassOwnPtr<WebCore::CCAnimationCurve> FakeTransformTransition::clone() const
+{
+ return adoptPtr(new FakeTransformTransition(*this));
+}
+
+
+FakeFloatTransition::FakeFloatTransition(double duration, float from, float to)
+ : m_duration(duration)
+ , m_from(from)
+ , m_to(to)
+{
+}
+
+FakeFloatTransition::~FakeFloatTransition()
+{
+}
+
+float FakeFloatTransition::getValue(double time) const
+{
+ time /= m_duration;
+ if (time >= 1)
+ time = 1;
+ return (1 - time) * m_from + time * m_to;
+}
+
+FakeLayerAnimationControllerClient::FakeLayerAnimationControllerClient()
+ : m_opacity(0)
+{
+}
+
+FakeLayerAnimationControllerClient::~FakeLayerAnimationControllerClient()
+{
+}
+
+PassOwnPtr<WebCore::CCAnimationCurve> FakeFloatTransition::clone() const
+{
+ return adoptPtr(new FakeFloatTransition(*this));
+}
+
+void addOpacityTransitionToController(WebCore::CCLayerAnimationController& controller, double duration, float startOpacity, float endOpacity, bool useTimingFunction)
+{
+ addOpacityTransition(controller, duration, startOpacity, endOpacity, useTimingFunction);
+}
+
+void addAnimatedTransformToController(WebCore::CCLayerAnimationController& controller, double duration, int deltaX, int deltaY)
+{
+ addAnimatedTransform(controller, duration, deltaX, deltaY);
+}
+
+void addOpacityTransitionToLayer(WebCore::LayerChromium& layer, double duration, float startOpacity, float endOpacity, bool useTimingFunction)
+{
+ addOpacityTransition(layer, duration, startOpacity, endOpacity, useTimingFunction);
+}
+
+void addOpacityTransitionToLayer(WebCore::CCLayerImpl& layer, double duration, float startOpacity, float endOpacity, bool useTimingFunction)
+{
+ addOpacityTransition(*layer.layerAnimationController(), duration, startOpacity, endOpacity, useTimingFunction);
+}
+
+void addAnimatedTransformToLayer(WebCore::LayerChromium& layer, double duration, int deltaX, int deltaY)
+{
+ addAnimatedTransform(layer, duration, deltaX, deltaY);
+}
+
+void addAnimatedTransformToLayer(WebCore::CCLayerImpl& layer, double duration, int deltaX, int deltaY)
+{
+ addAnimatedTransform(*layer.layerAnimationController(), duration, deltaX, deltaY);
+}
+
+} // namespace WebKitTests
diff --git a/cc/test/CCAnimationTestCommon.h b/cc/test/CCAnimationTestCommon.h
new file mode 100644
index 0000000..9f13643
--- /dev/null
+++ b/cc/test/CCAnimationTestCommon.h
@@ -0,0 +1,90 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCAnimationTestCommon_h
+#define CCAnimationTestCommon_h
+
+#include "CCActiveAnimation.h"
+#include "CCAnimationCurve.h"
+#include "CCLayerAnimationController.h"
+#include "IntSize.h"
+
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+class CCLayerImpl;
+class LayerChromium;
+}
+
+namespace WebKitTests {
+
+class FakeFloatAnimationCurve : public WebCore::CCFloatAnimationCurve {
+public:
+ FakeFloatAnimationCurve();
+ virtual ~FakeFloatAnimationCurve();
+
+ virtual double duration() const OVERRIDE { return 1; }
+ virtual float getValue(double now) const OVERRIDE { return 0; }
+ virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE;
+};
+
+class FakeTransformTransition : public WebCore::CCTransformAnimationCurve {
+public:
+ FakeTransformTransition(double duration);
+ virtual ~FakeTransformTransition();
+
+ virtual double duration() const OVERRIDE { return m_duration; }
+ virtual WebKit::WebTransformationMatrix getValue(double time) const OVERRIDE;
+
+ virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE;
+
+private:
+ double m_duration;
+};
+
+class FakeFloatTransition : public WebCore::CCFloatAnimationCurve {
+public:
+ FakeFloatTransition(double duration, float from, float to);
+ virtual ~FakeFloatTransition();
+
+ virtual double duration() const OVERRIDE { return m_duration; }
+ virtual float getValue(double time) const OVERRIDE;
+
+ virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE;
+
+private:
+ double m_duration;
+ float m_from;
+ float m_to;
+};
+
+class FakeLayerAnimationControllerClient : public WebCore::CCLayerAnimationControllerClient {
+public:
+ FakeLayerAnimationControllerClient();
+ virtual ~FakeLayerAnimationControllerClient();
+
+ // CCLayerAnimationControllerClient implementation
+ virtual int id() const OVERRIDE { return 0; }
+ virtual void setOpacityFromAnimation(float opacity) OVERRIDE { m_opacity = opacity; }
+ virtual float opacity() const OVERRIDE { return m_opacity; }
+ virtual void setTransformFromAnimation(const WebKit::WebTransformationMatrix& transform) OVERRIDE { m_transform = transform; }
+ virtual const WebKit::WebTransformationMatrix& transform() const OVERRIDE { return m_transform; }
+
+private:
+ float m_opacity;
+ WebKit::WebTransformationMatrix m_transform;
+};
+
+void addOpacityTransitionToController(WebCore::CCLayerAnimationController&, double duration, float startOpacity, float endOpacity, bool useTimingFunction);
+void addAnimatedTransformToController(WebCore::CCLayerAnimationController&, double duration, int deltaX, int deltaY);
+
+void addOpacityTransitionToLayer(WebCore::LayerChromium&, double duration, float startOpacity, float endOpacity, bool useTimingFunction);
+void addOpacityTransitionToLayer(WebCore::CCLayerImpl&, double duration, float startOpacity, float endOpacity, bool useTimingFunction);
+
+void addAnimatedTransformToLayer(WebCore::LayerChromium&, double duration, int deltaX, int deltaY);
+void addAnimatedTransformToLayer(WebCore::CCLayerImpl&, double duration, int deltaX, int deltaY);
+
+} // namespace WebKitTests
+
+#endif // CCAnimationTesctCommon_h
diff --git a/cc/test/CCLayerTestCommon.cpp b/cc/test/CCLayerTestCommon.cpp
new file mode 100644
index 0000000..1195012
--- /dev/null
+++ b/cc/test/CCLayerTestCommon.cpp
@@ -0,0 +1,36 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCLayerTestCommon.h"
+#include "CCDrawQuad.h"
+
+#include <gtest/gtest.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+
+namespace CCLayerTestCommon {
+
+// Align with expected and actual output
+const char* quadString = " Quad: ";
+
+void verifyQuadsExactlyCoverRect(const CCQuadList& quads, const IntRect& rect)
+{
+ Region remaining(rect);
+
+ for (size_t i = 0; i < quads.size(); ++i) {
+ CCDrawQuad* quad = quads[i].get();
+ IntRect quadRect = quad->quadRect();
+
+ EXPECT_TRUE(rect.contains(quadRect)) << quadString << i;
+ EXPECT_TRUE(remaining.contains(quadRect)) << quadString << i;
+ remaining.subtract(Region(quadRect));
+ }
+
+ EXPECT_TRUE(remaining.isEmpty());
+}
+
+} // namespace
diff --git a/cc/test/CCLayerTestCommon.h b/cc/test/CCLayerTestCommon.h
new file mode 100644
index 0000000..fc726fa
--- /dev/null
+++ b/cc/test/CCLayerTestCommon.h
@@ -0,0 +1,19 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerTestCommon_h
+#define CCLayerTestCommon_h
+
+#include "CCRenderPass.h"
+#include "IntRect.h"
+#include "Region.h"
+
+namespace CCLayerTestCommon {
+
+extern const char* quadString;
+
+void verifyQuadsExactlyCoverRect(const WebCore::CCQuadList&, const WebCore::IntRect&);
+
+} // namespace CCLayerTestCommon
+#endif // CCLayerTestCommon_h
diff --git a/cc/test/CCLayerTreeTestCommon.h b/cc/test/CCLayerTreeTestCommon.h
new file mode 100644
index 0000000..1e9a473
--- /dev/null
+++ b/cc/test/CCLayerTreeTestCommon.h
@@ -0,0 +1,40 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCLayerTreeTestCommon_h
+#define CCLayerTreeTestCommon_h
+
+#include <public/WebTransformationMatrix.h>
+
+namespace WebKitTests {
+
+// These are macros instead of functions so that we get useful line numbers where a test failed.
+#define EXPECT_FLOAT_RECT_EQ(expected, actual) \
+ EXPECT_FLOAT_EQ((expected).location().x(), (actual).location().x()); \
+ EXPECT_FLOAT_EQ((expected).location().y(), (actual).location().y()); \
+ EXPECT_FLOAT_EQ((expected).size().width(), (actual).size().width()); \
+ EXPECT_FLOAT_EQ((expected).size().height(), (actual).size().height())
+
+#define EXPECT_INT_RECT_EQ(expected, actual) \
+ EXPECT_EQ((expected).location().x(), (actual).location().x()); \
+ EXPECT_EQ((expected).location().y(), (actual).location().y()); \
+ EXPECT_EQ((expected).size().width(), (actual).size().width()); \
+ EXPECT_EQ((expected).size().height(), (actual).size().height())
+
+// This is a function rather than a macro because when this is included as a macro
+// in bulk, it causes a significant slow-down in compilation time. This problem
+// exists with both gcc and clang, and bugs have been filed at
+// http://llvm.org/bugs/show_bug.cgi?id=13651 and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54337
+void ExpectTransformationMatrixEq(WebKit::WebTransformationMatrix expected,
+ WebKit::WebTransformationMatrix actual);
+
+#define EXPECT_TRANSFORMATION_MATRIX_EQ(expected, actual) \
+ { \
+ SCOPED_TRACE(""); \
+ WebKitTests::ExpectTransformationMatrixEq(expected, actual); \
+ }
+
+} // namespace
+
+#endif // CCLayerTreeTestCommon_h
diff --git a/cc/test/CCOcclusionTrackerTestCommon.h b/cc/test/CCOcclusionTrackerTestCommon.h
new file mode 100644
index 0000000..90e0cc3
--- /dev/null
+++ b/cc/test/CCOcclusionTrackerTestCommon.h
@@ -0,0 +1,37 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCOcclusionTrackerTestCommon_h
+#define CCOcclusionTrackerTestCommon_h
+
+#include "CCOcclusionTracker.h"
+#include "CCRenderSurface.h"
+#include "IntRect.h"
+#include "Region.h"
+#include "RenderSurfaceChromium.h"
+
+namespace WebKitTests {
+
+// A subclass to expose the total current occlusion.
+template<typename LayerType, typename RenderSurfaceType>
+class TestCCOcclusionTrackerBase : public WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType> {
+public:
+ TestCCOcclusionTrackerBase(WebCore::IntRect screenScissorRect, bool recordMetricsForFrame = false)
+ : WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>(screenScissorRect, recordMetricsForFrame)
+ {
+ }
+
+ WebCore::Region occlusionInScreenSpace() const { return WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInScreen; }
+ WebCore::Region occlusionInTargetSurface() const { return WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInTarget; }
+
+ void setOcclusionInScreenSpace(const WebCore::Region& region) { WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInScreen = region; }
+ void setOcclusionInTargetSurface(const WebCore::Region& region) { WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInTarget = region; }
+};
+
+typedef TestCCOcclusionTrackerBase<WebCore::LayerChromium, WebCore::RenderSurfaceChromium> TestCCOcclusionTracker;
+typedef TestCCOcclusionTrackerBase<WebCore::CCLayerImpl, WebCore::CCRenderSurface> TestCCOcclusionTrackerImpl;
+
+}
+
+#endif // CCOcclusionTrackerTestCommon_h
diff --git a/cc/test/CCSchedulerTestCommon.h b/cc/test/CCSchedulerTestCommon.h
new file mode 100644
index 0000000..cb78510
--- /dev/null
+++ b/cc/test/CCSchedulerTestCommon.h
@@ -0,0 +1,133 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCSchedulerTestCommon_h
+#define CCSchedulerTestCommon_h
+
+#include "CCDelayBasedTimeSource.h"
+#include "CCFrameRateController.h"
+#include "CCThread.h"
+#include <gtest/gtest.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebKitTests {
+
+class FakeCCTimeSourceClient : public WebCore::CCTimeSourceClient {
+public:
+ FakeCCTimeSourceClient() { reset(); }
+ void reset() { m_tickCalled = false; }
+ bool tickCalled() const { return m_tickCalled; }
+
+ virtual void onTimerTick() OVERRIDE { m_tickCalled = true; }
+
+protected:
+ bool m_tickCalled;
+};
+
+class FakeCCThread : public WebCore::CCThread {
+public:
+ FakeCCThread() { reset(); }
+ void reset()
+ {
+ m_pendingTaskDelay = 0;
+ m_pendingTask.clear();
+ m_runPendingTaskOnOverwrite = false;
+ }
+
+ void runPendingTaskOnOverwrite(bool enable)
+ {
+ m_runPendingTaskOnOverwrite = enable;
+ }
+
+ bool hasPendingTask() const { return m_pendingTask; }
+ void runPendingTask()
+ {
+ ASSERT(m_pendingTask);
+ OwnPtr<Task> task = m_pendingTask.release();
+ task->performTask();
+ }
+
+ long long pendingDelayMs() const
+ {
+ EXPECT_TRUE(hasPendingTask());
+ return m_pendingTaskDelay;
+ }
+
+ virtual void postTask(PassOwnPtr<Task>) { ASSERT_NOT_REACHED(); }
+ virtual void postDelayedTask(PassOwnPtr<Task> task, long long delay)
+ {
+ if (m_runPendingTaskOnOverwrite && hasPendingTask())
+ runPendingTask();
+
+ EXPECT_TRUE(!hasPendingTask());
+ m_pendingTask = task;
+ m_pendingTaskDelay = delay;
+ }
+ virtual WTF::ThreadIdentifier threadID() const { return 0; }
+
+protected:
+ OwnPtr<Task> m_pendingTask;
+ long long m_pendingTaskDelay;
+ bool m_runPendingTaskOnOverwrite;
+};
+
+class FakeCCTimeSource : public WebCore::CCTimeSource {
+public:
+ FakeCCTimeSource()
+ : m_active(false)
+ , m_nextTickTime(0)
+ , m_client(0) { }
+
+ virtual ~FakeCCTimeSource() { }
+
+ virtual void setClient(WebCore::CCTimeSourceClient* client) OVERRIDE { m_client = client; }
+ virtual void setActive(bool b) OVERRIDE { m_active = b; }
+ virtual bool active() const OVERRIDE { return m_active; }
+ virtual void setTimebaseAndInterval(double timebase, double interval) OVERRIDE { }
+ virtual double lastTickTime() OVERRIDE { return 0; }
+ virtual double nextTickTimeIfActivated() OVERRIDE { return 0; }
+
+ void tick()
+ {
+ ASSERT(m_active);
+ if (m_client)
+ m_client->onTimerTick();
+ }
+
+ void setNextTickTime(double nextTickTime) { m_nextTickTime = nextTickTime; }
+
+protected:
+ bool m_active;
+ double m_nextTickTime;
+ WebCore::CCTimeSourceClient* m_client;
+};
+
+class FakeCCDelayBasedTimeSource : public WebCore::CCDelayBasedTimeSource {
+public:
+ static PassRefPtr<FakeCCDelayBasedTimeSource> create(double interval, WebCore::CCThread* thread)
+ {
+ return adoptRef(new FakeCCDelayBasedTimeSource(interval, thread));
+ }
+
+ void setMonotonicTimeNow(double time) { m_monotonicTimeNow = time; }
+ virtual double monotonicTimeNow() const OVERRIDE { return m_monotonicTimeNow; }
+
+protected:
+ FakeCCDelayBasedTimeSource(double interval, WebCore::CCThread* thread)
+ : CCDelayBasedTimeSource(interval, thread)
+ , m_monotonicTimeNow(0) { }
+
+ double m_monotonicTimeNow;
+};
+
+class FakeCCFrameRateController : public WebCore::CCFrameRateController {
+public:
+ FakeCCFrameRateController(PassRefPtr<WebCore::CCTimeSource> timer) : WebCore::CCFrameRateController(timer) { }
+
+ int numFramesPending() const { return m_numFramesPending; }
+};
+
+}
+
+#endif // CCSchedulerTestCommon_h
diff --git a/cc/test/CCTestCommon.h b/cc/test/CCTestCommon.h
new file mode 100644
index 0000000..c1ab34a
--- /dev/null
+++ b/cc/test/CCTestCommon.h
@@ -0,0 +1,22 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTestCommon_h
+#define CCTestCommon_h
+
+#include "CCSettings.h"
+
+namespace WebKitTests {
+
+// If you have a test that modifies or uses global settings, keep an instance
+// of this class to ensure that you start and end with a clean slate.
+class CCScopedSettings {
+public:
+ CCScopedSettings() { WebCore::CCSettings::reset(); }
+ ~CCScopedSettings() { WebCore::CCSettings::reset(); }
+};
+
+} // namespace WebKitTests
+
+#endif // CCTestCommon_h
diff --git a/cc/test/CCTiledLayerTestCommon.cpp b/cc/test/CCTiledLayerTestCommon.cpp
new file mode 100644
index 0000000..a1ba83c
--- /dev/null
+++ b/cc/test/CCTiledLayerTestCommon.cpp
@@ -0,0 +1,120 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "CCTiledLayerTestCommon.h"
+
+using namespace WebCore;
+
+namespace WebKitTests {
+
+FakeLayerTextureUpdater::Texture::Texture(FakeLayerTextureUpdater* layer, PassOwnPtr<CCPrioritizedTexture> texture)
+ : LayerTextureUpdater::Texture(texture)
+ , m_layer(layer)
+{
+}
+
+FakeLayerTextureUpdater::Texture::~Texture()
+{
+}
+
+void FakeLayerTextureUpdater::Texture::updateRect(CCResourceProvider* resourceProvider, const IntRect&, const IntSize&)
+{
+ texture()->acquireBackingTexture(resourceProvider);
+ m_layer->updateRect();
+}
+
+void FakeLayerTextureUpdater::Texture::prepareRect(const IntRect&, WebCore::CCRenderingStats&)
+{
+ m_layer->prepareRect();
+}
+
+FakeLayerTextureUpdater::FakeLayerTextureUpdater()
+ : m_prepareCount(0)
+ , m_updateCount(0)
+ , m_prepareRectCount(0)
+{
+}
+
+FakeLayerTextureUpdater::~FakeLayerTextureUpdater()
+{
+}
+
+void FakeLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, float, float, IntRect& resultingOpaqueRect, CCRenderingStats&)
+{
+ m_prepareCount++;
+ m_lastUpdateRect = contentRect;
+ if (!m_rectToInvalidate.isEmpty()) {
+ m_layer->invalidateContentRect(m_rectToInvalidate);
+ m_rectToInvalidate = IntRect();
+ m_layer = 0;
+ }
+ resultingOpaqueRect = m_opaquePaintRect;
+}
+
+void FakeLayerTextureUpdater::setRectToInvalidate(const IntRect& rect, FakeTiledLayerChromium* layer)
+{
+ m_rectToInvalidate = rect;
+ m_layer = layer;
+}
+
+PassOwnPtr<LayerTextureUpdater::Texture> FakeLayerTextureUpdater::createTexture(CCPrioritizedTextureManager* manager)
+{
+ return adoptPtr(new Texture(this, CCPrioritizedTexture::create(manager)));
+}
+
+FakeCCTiledLayerImpl::FakeCCTiledLayerImpl(int id)
+ : CCTiledLayerImpl(id)
+{
+}
+
+FakeCCTiledLayerImpl::~FakeCCTiledLayerImpl()
+{
+}
+
+FakeTiledLayerChromium::FakeTiledLayerChromium(CCPrioritizedTextureManager* textureManager)
+ : TiledLayerChromium()
+ , m_fakeTextureUpdater(adoptRef(new FakeLayerTextureUpdater))
+ , m_textureManager(textureManager)
+{
+ setTileSize(tileSize());
+ setTextureFormat(GraphicsContext3D::RGBA);
+ setBorderTexelOption(CCLayerTilingData::NoBorderTexels);
+ setIsDrawable(true); // So that we don't get false positives if any of these tests expect to return false from drawsContent() for other reasons.
+}
+
+FakeTiledLayerChromium::~FakeTiledLayerChromium()
+{
+}
+
+void FakeTiledLayerChromium::setNeedsDisplayRect(const FloatRect& rect)
+{
+ m_lastNeedsDisplayRect = rect;
+ TiledLayerChromium::setNeedsDisplayRect(rect);
+}
+
+void FakeTiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& calculator)
+{
+ // Ensure there is always a target render surface available. If none has been
+ // set (the layer is an orphan for the test), then just set a surface on itself.
+ bool missingTargetRenderSurface = !renderTarget();
+
+ if (missingTargetRenderSurface)
+ createRenderSurface();
+
+ TiledLayerChromium::setTexturePriorities(calculator);
+
+ if (missingTargetRenderSurface) {
+ clearRenderSurface();
+ setRenderTarget(0);
+ }
+}
+
+FakeTiledLayerWithScaledBounds::FakeTiledLayerWithScaledBounds(CCPrioritizedTextureManager* textureManager)
+ : FakeTiledLayerChromium(textureManager)
+{
+}
+
+} // namespace
diff --git a/cc/test/CCTiledLayerTestCommon.h b/cc/test/CCTiledLayerTestCommon.h
new file mode 100644
index 0000000..105a242
--- /dev/null
+++ b/cc/test/CCTiledLayerTestCommon.h
@@ -0,0 +1,145 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CCTiledLayerTestCommon_h
+#define CCTiledLayerTestCommon_h
+
+#include "CCGraphicsContext.h"
+#include "CCPrioritizedTexture.h"
+#include "CCResourceProvider.h"
+#include "CCTextureUpdateQueue.h"
+#include "CCTiledLayerImpl.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include "LayerTextureUpdater.h"
+#include "Region.h"
+#include "TextureCopier.h"
+#include "TextureUploader.h"
+#include "TiledLayerChromium.h"
+
+namespace WebKitTests {
+
+class FakeTiledLayerChromium;
+
+class FakeLayerTextureUpdater : public WebCore::LayerTextureUpdater {
+public:
+ class Texture : public WebCore::LayerTextureUpdater::Texture {
+ public:
+ Texture(FakeLayerTextureUpdater*, PassOwnPtr<WebCore::CCPrioritizedTexture>);
+ virtual ~Texture();
+
+ virtual void updateRect(WebCore::CCResourceProvider* , const WebCore::IntRect&, const WebCore::IntSize&) OVERRIDE;
+ virtual void prepareRect(const WebCore::IntRect&, WebCore::CCRenderingStats&) OVERRIDE;
+
+ private:
+ FakeLayerTextureUpdater* m_layer;
+ };
+
+ FakeLayerTextureUpdater();
+ virtual ~FakeLayerTextureUpdater();
+
+ virtual PassOwnPtr<WebCore::LayerTextureUpdater::Texture> createTexture(WebCore::CCPrioritizedTextureManager*) OVERRIDE;
+ virtual SampledTexelFormat sampledTexelFormat(GC3Denum) OVERRIDE { return SampledTexelFormatRGBA; }
+
+ virtual void prepareToUpdate(const WebCore::IntRect& contentRect, const WebCore::IntSize&, float, float, WebCore::IntRect& resultingOpaqueRect, WebCore::CCRenderingStats&) OVERRIDE;
+ // Sets the rect to invalidate during the next call to prepareToUpdate(). After the next
+ // call to prepareToUpdate() the rect is reset.
+ void setRectToInvalidate(const WebCore::IntRect&, FakeTiledLayerChromium*);
+ // Last rect passed to prepareToUpdate().
+ const WebCore::IntRect& lastUpdateRect() const { return m_lastUpdateRect; }
+
+ // Number of times prepareToUpdate has been invoked.
+ int prepareCount() const { return m_prepareCount; }
+ void clearPrepareCount() { m_prepareCount = 0; }
+
+ // Number of times updateRect has been invoked.
+ int updateCount() const { return m_updateCount; }
+ void clearUpdateCount() { m_updateCount = 0; }
+ void updateRect() { m_updateCount++; }
+
+ // Number of times prepareRect() has been invoked on a texture.
+ int prepareRectCount() const { return m_prepareRectCount; }
+ void clearPrepareRectCount() { m_prepareRectCount = 0; }
+ void prepareRect() { m_prepareRectCount++; }
+
+ void setOpaquePaintRect(const WebCore::IntRect& opaquePaintRect) { m_opaquePaintRect = opaquePaintRect; }
+
+private:
+ int m_prepareCount;
+ int m_updateCount;
+ int m_prepareRectCount;
+ WebCore::IntRect m_rectToInvalidate;
+ WebCore::IntRect m_lastUpdateRect;
+ WebCore::IntRect m_opaquePaintRect;
+ RefPtr<FakeTiledLayerChromium> m_layer;
+};
+
+class FakeCCTiledLayerImpl : public WebCore::CCTiledLayerImpl {
+public:
+ explicit FakeCCTiledLayerImpl(int id);
+ virtual ~FakeCCTiledLayerImpl();
+
+ using WebCore::CCTiledLayerImpl::hasTileAt;
+ using WebCore::CCTiledLayerImpl::hasTextureIdForTileAt;
+};
+
+class FakeTiledLayerChromium : public WebCore::TiledLayerChromium {
+public:
+ explicit FakeTiledLayerChromium(WebCore::CCPrioritizedTextureManager*);
+ virtual ~FakeTiledLayerChromium();
+
+ static WebCore::IntSize tileSize() { return WebCore::IntSize(100, 100); }
+
+ using WebCore::TiledLayerChromium::invalidateContentRect;
+ using WebCore::TiledLayerChromium::needsIdlePaint;
+ using WebCore::TiledLayerChromium::skipsDraw;
+ using WebCore::TiledLayerChromium::numPaintedTiles;
+ using WebCore::TiledLayerChromium::idlePaintRect;
+
+ virtual void setNeedsDisplayRect(const WebCore::FloatRect&) OVERRIDE;
+ const WebCore::FloatRect& lastNeedsDisplayRect() const { return m_lastNeedsDisplayRect; }
+
+ virtual void setTexturePriorities(const WebCore::CCPriorityCalculator&) OVERRIDE;
+
+ virtual WebCore::CCPrioritizedTextureManager* textureManager() const OVERRIDE { return m_textureManager; }
+ FakeLayerTextureUpdater* fakeLayerTextureUpdater() { return m_fakeTextureUpdater.get(); }
+ WebCore::FloatRect updateRect() { return m_updateRect; }
+
+protected:
+ virtual WebCore::LayerTextureUpdater* textureUpdater() const OVERRIDE { return m_fakeTextureUpdater.get(); }
+ virtual void createTextureUpdaterIfNeeded() OVERRIDE { }
+
+private:
+ RefPtr<FakeLayerTextureUpdater> m_fakeTextureUpdater;
+ WebCore::CCPrioritizedTextureManager* m_textureManager;
+ WebCore::FloatRect m_lastNeedsDisplayRect;
+};
+
+class FakeTiledLayerWithScaledBounds : public FakeTiledLayerChromium {
+public:
+ explicit FakeTiledLayerWithScaledBounds(WebCore::CCPrioritizedTextureManager*);
+
+ void setContentBounds(const WebCore::IntSize& contentBounds) { m_forcedContentBounds = contentBounds; }
+ virtual WebCore::IntSize contentBounds() const OVERRIDE { return m_forcedContentBounds; }
+
+protected:
+ WebCore::IntSize m_forcedContentBounds;
+};
+
+class FakeTextureCopier : public WebCore::TextureCopier {
+public:
+ virtual void copyTexture(Parameters) { }
+ virtual void flush() { }
+};
+
+class FakeTextureUploader : public WebCore::TextureUploader {
+public:
+ virtual bool isBusy() { return false; }
+ virtual void beginUploads() { }
+ virtual void endUploads() { }
+ virtual void uploadTexture(WebCore::CCResourceProvider* resourceProvider, Parameters upload) { upload.texture->updateRect(resourceProvider, upload.sourceRect, upload.destOffset); }
+};
+
+}
+#endif // CCTiledLayerTestCommon_h
diff --git a/cc/test/CompositorFakeWebGraphicsContext3D.h b/cc/test/CompositorFakeWebGraphicsContext3D.h
new file mode 100644
index 0000000..2a709a2
--- /dev/null
+++ b/cc/test/CompositorFakeWebGraphicsContext3D.h
@@ -0,0 +1,36 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CompositorFakeWebGraphicsContext3D_h
+#define CompositorFakeWebGraphicsContext3D_h
+
+#include "FakeWebGraphicsContext3D.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+
+// Test stub for WebGraphicsContext3D. Returns canned values needed for compositor initialization.
+class CompositorFakeWebGraphicsContext3D : public FakeWebGraphicsContext3D {
+public:
+ static PassOwnPtr<CompositorFakeWebGraphicsContext3D> create(Attributes attrs)
+ {
+ return adoptPtr(new CompositorFakeWebGraphicsContext3D(attrs));
+ }
+
+ virtual bool makeContextCurrent() { return true; }
+ virtual WebGLId createProgram() { return 1; }
+ virtual WebGLId createShader(WGC3Denum) { return 1; }
+ virtual void getShaderiv(WebGLId, WGC3Denum, WGC3Dint* value) { *value = 1; }
+ virtual void getProgramiv(WebGLId, WGC3Denum, WGC3Dint* value) { *value = 1; }
+
+protected:
+ explicit CompositorFakeWebGraphicsContext3D(Attributes attrs)
+ {
+ m_attrs = attrs;
+ }
+};
+
+}
+
+#endif
diff --git a/cc/test/FakeCCGraphicsContext.h b/cc/test/FakeCCGraphicsContext.h
new file mode 100644
index 0000000..0185eb3
--- /dev/null
+++ b/cc/test/FakeCCGraphicsContext.h
@@ -0,0 +1,22 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FakeCCGraphicsContext_h
+#define FakeCCGraphicsContext_h
+
+#include "CCGraphicsContext.h"
+#include "CompositorFakeWebGraphicsContext3D.h"
+#include "FakeWebCompositorOutputSurface.h"
+#include <public/WebCompositorOutputSurface.h>
+
+namespace WebKit {
+
+static inline PassOwnPtr<WebCore::CCGraphicsContext> createFakeCCGraphicsContext()
+{
+ return FakeWebCompositorOutputSurface::create(CompositorFakeWebGraphicsContext3D::create(WebGraphicsContext3D::Attributes()));
+}
+
+} // namespace WebKit
+
+#endif // FakeCCGraphicsContext_h
diff --git a/cc/test/FakeCCLayerTreeHostClient.h b/cc/test/FakeCCLayerTreeHostClient.h
new file mode 100644
index 0000000..1334d8b
--- /dev/null
+++ b/cc/test/FakeCCLayerTreeHostClient.h
@@ -0,0 +1,39 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef FakeCCLayerTreeHostClient_h
+#define FakeCCLayerTreeHostClient_h
+
+#include "config.h"
+
+#include "CCLayerTreeHost.h"
+#include "CompositorFakeWebGraphicsContext3D.h"
+#include "FakeWebCompositorOutputSurface.h"
+
+namespace WebCore {
+
+class FakeCCLayerTreeHostClient : public CCLayerTreeHostClient {
+public:
+ virtual void willBeginFrame() OVERRIDE { }
+ virtual void didBeginFrame() OVERRIDE { }
+ virtual void animate(double monotonicFrameBeginTime) OVERRIDE { }
+ virtual void layout() OVERRIDE { }
+ virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) OVERRIDE { }
+
+ virtual PassOwnPtr<WebKit::WebCompositorOutputSurface> createOutputSurface() OVERRIDE
+ {
+ WebKit::WebGraphicsContext3D::Attributes attrs;
+ return WebKit::FakeWebCompositorOutputSurface::create(WebKit::CompositorFakeWebGraphicsContext3D::create(attrs));
+ }
+ virtual void didRecreateOutputSurface(bool success) OVERRIDE { }
+ virtual void willCommit() OVERRIDE { }
+ virtual void didCommit() OVERRIDE { }
+ virtual void didCommitAndDrawFrame() OVERRIDE { }
+ virtual void didCompleteSwapBuffers() OVERRIDE { }
+
+ // Used only in the single-threaded path.
+ virtual void scheduleComposite() OVERRIDE { }
+};
+
+}
+#endif // FakeCCLayerTreeHostClient_h
diff --git a/cc/test/FakeGraphicsContext3DTest.cpp b/cc/test/FakeGraphicsContext3DTest.cpp
new file mode 100644
index 0000000..011da56
--- /dev/null
+++ b/cc/test/FakeGraphicsContext3DTest.cpp
@@ -0,0 +1,36 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "FakeWebGraphicsContext3D.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+class ContextThatCountsMakeCurrents : public FakeWebGraphicsContext3D {
+public:
+ ContextThatCountsMakeCurrents() : m_makeCurrentCount(0) { }
+ virtual bool makeContextCurrent()
+ {
+ m_makeCurrentCount++;
+ return true;
+ }
+ int makeCurrentCount() { return m_makeCurrentCount; }
+private:
+ int m_makeCurrentCount;
+};
+
+
+TEST(FakeGraphicsContext3DTest, ContextCreationShouldNotMakeCurrent)
+{
+ OwnPtr<ContextThatCountsMakeCurrents> context(adoptPtr(new ContextThatCountsMakeCurrents));
+ EXPECT_TRUE(context);
+ EXPECT_EQ(0, context->makeCurrentCount());
+}
+
diff --git a/cc/test/FakeWebCompositorOutputSurface.h b/cc/test/FakeWebCompositorOutputSurface.h
new file mode 100644
index 0000000..6a728fe
--- /dev/null
+++ b/cc/test/FakeWebCompositorOutputSurface.h
@@ -0,0 +1,57 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FakeWebCompositorOutputSurface_h
+#define FakeWebCompositorOutputSurface_h
+
+#include <public/WebCompositorOutputSurface.h>
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebKit {
+
+class FakeWebCompositorOutputSurface : public WebCompositorOutputSurface {
+public:
+ static inline PassOwnPtr<FakeWebCompositorOutputSurface> create(PassOwnPtr<WebGraphicsContext3D> context3D)
+ {
+ return adoptPtr(new FakeWebCompositorOutputSurface(context3D));
+ }
+
+
+ virtual bool bindToClient(WebCompositorOutputSurfaceClient* client) OVERRIDE
+ {
+ ASSERT(client);
+ if (!m_context3D->makeContextCurrent())
+ return false;
+ m_client = client;
+ return true;
+ }
+
+ virtual const Capabilities& capabilities() const OVERRIDE
+ {
+ return m_capabilities;
+ }
+
+ virtual WebGraphicsContext3D* context3D() const OVERRIDE
+ {
+ return m_context3D.get();
+ }
+
+ virtual void sendFrameToParentCompositor(const WebCompositorFrame&) OVERRIDE
+ {
+ }
+
+private:
+ explicit FakeWebCompositorOutputSurface(PassOwnPtr<WebGraphicsContext3D> context3D)
+ {
+ m_context3D = context3D;
+ }
+
+ OwnPtr<WebGraphicsContext3D> m_context3D;
+ Capabilities m_capabilities;
+ WebCompositorOutputSurfaceClient* m_client;
+};
+
+} // namespace WebKit
+
+#endif // FakeWebCompositorOutputSurface_h
diff --git a/cc/test/FakeWebGraphicsContext3D.h b/cc/test/FakeWebGraphicsContext3D.h
new file mode 100644
index 0000000..67ad922
--- /dev/null
+++ b/cc/test/FakeWebGraphicsContext3D.h
@@ -0,0 +1,260 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FakeWebGraphicsContext3D_h
+#define FakeWebGraphicsContext3D_h
+
+#include "GraphicsContext3D.h"
+#include <public/WebGraphicsContext3D.h>
+
+namespace WebKit {
+
+// WebGraphicsContext3D base class for use in WebKit unit tests.
+// All operations are no-ops (returning 0 if necessary).
+class FakeWebGraphicsContext3D : public WebGraphicsContext3D {
+public:
+ FakeWebGraphicsContext3D()
+ : m_nextTextureId(1)
+ {
+ }
+
+ virtual bool makeContextCurrent() { return true; }
+
+ virtual int width() { return 0; }
+ virtual int height() { return 0; }
+
+ virtual void reshape(int width, int height) { }
+
+ virtual bool isGLES2Compliant() { return false; }
+
+ virtual bool readBackFramebuffer(unsigned char* pixels, size_t bufferSize, WebGLId framebuffer, int width, int height) { return false; }
+
+ virtual WebGLId getPlatformTextureId() { return 0; }
+
+ virtual void prepareTexture() { }
+
+ virtual void postSubBufferCHROMIUM(int x, int y, int width, int height) { }
+
+ virtual void synthesizeGLError(WGC3Denum) { }
+
+ virtual bool isContextLost() { return false; }
+
+ virtual void* mapBufferSubDataCHROMIUM(WGC3Denum target, WGC3Dintptr offset, WGC3Dsizeiptr size, WGC3Denum access) { return 0; }
+ virtual void unmapBufferSubDataCHROMIUM(const void*) { }
+ virtual void* mapTexSubImage2DCHROMIUM(WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset, WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Denum type, WGC3Denum access) { return 0; }
+ virtual void unmapTexSubImage2DCHROMIUM(const void*) { }
+
+ virtual void setVisibilityCHROMIUM(bool visible) { }
+
+ virtual void discardFramebufferEXT(WGC3Denum target, WGC3Dsizei numAttachments, const WGC3Denum* attachments) { }
+ virtual void ensureFramebufferCHROMIUM() { }
+
+ virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { }
+
+ virtual WebString getRequestableExtensionsCHROMIUM() { return WebString(); }
+ virtual void requestExtensionCHROMIUM(const char*) { }
+
+ virtual void blitFramebufferCHROMIUM(WGC3Dint srcX0, WGC3Dint srcY0, WGC3Dint srcX1, WGC3Dint srcY1, WGC3Dint dstX0, WGC3Dint dstY0, WGC3Dint dstX1, WGC3Dint dstY1, WGC3Dbitfield mask, WGC3Denum filter) { }
+ virtual void renderbufferStorageMultisampleCHROMIUM(WGC3Denum target, WGC3Dsizei samples, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height) { }
+
+ virtual void activeTexture(WGC3Denum texture) { }
+ virtual void attachShader(WebGLId program, WebGLId shader) { }
+ virtual void bindAttribLocation(WebGLId program, WGC3Duint index, const WGC3Dchar* name) { }
+ virtual void bindBuffer(WGC3Denum target, WebGLId buffer) { }
+ virtual void bindFramebuffer(WGC3Denum target, WebGLId framebuffer) { }
+ virtual void bindRenderbuffer(WGC3Denum target, WebGLId renderbuffer) { }
+ virtual void bindTexture(WGC3Denum target, WebGLId texture) { }
+ virtual void blendColor(WGC3Dclampf red, WGC3Dclampf green, WGC3Dclampf blue, WGC3Dclampf alpha) { }
+ virtual void blendEquation(WGC3Denum mode) { }
+ virtual void blendEquationSeparate(WGC3Denum modeRGB, WGC3Denum modeAlpha) { }
+ virtual void blendFunc(WGC3Denum sfactor, WGC3Denum dfactor) { }
+ virtual void blendFuncSeparate(WGC3Denum srcRGB, WGC3Denum dstRGB, WGC3Denum srcAlpha, WGC3Denum dstAlpha) { }
+
+ virtual void bufferData(WGC3Denum target, WGC3Dsizeiptr size, const void* data, WGC3Denum usage) { }
+ virtual void bufferSubData(WGC3Denum target, WGC3Dintptr offset, WGC3Dsizeiptr size, const void* data) { }
+
+ virtual WGC3Denum checkFramebufferStatus(WGC3Denum target)
+ {
+ return WebCore::GraphicsContext3D::FRAMEBUFFER_COMPLETE;
+ }
+
+ virtual void clear(WGC3Dbitfield mask) { }
+ virtual void clearColor(WGC3Dclampf red, WGC3Dclampf green, WGC3Dclampf blue, WGC3Dclampf alpha) { }
+ virtual void clearDepth(WGC3Dclampf depth) { }
+ virtual void clearStencil(WGC3Dint s) { }
+ virtual void colorMask(WGC3Dboolean red, WGC3Dboolean green, WGC3Dboolean blue, WGC3Dboolean alpha) { }
+ virtual void compileShader(WebGLId shader) { }
+
+ virtual void compressedTexImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border, WGC3Dsizei imageSize, const void* data) { }
+ virtual void compressedTexSubImage2D(WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset, WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Dsizei imageSize, const void* data) { }
+ virtual void copyTexImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border) { }
+ virtual void copyTexSubImage2D(WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset, WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height) { }
+ virtual void cullFace(WGC3Denum mode) { }
+ virtual void depthFunc(WGC3Denum func) { }
+ virtual void depthMask(WGC3Dboolean flag) { }
+ virtual void depthRange(WGC3Dclampf zNear, WGC3Dclampf zFar) { }
+ virtual void detachShader(WebGLId program, WebGLId shader) { }
+ virtual void disable(WGC3Denum cap) { }
+ virtual void disableVertexAttribArray(WGC3Duint index) { }
+ virtual void drawArrays(WGC3Denum mode, WGC3Dint first, WGC3Dsizei count) { }
+ virtual void drawElements(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset) { }
+
+ virtual void enable(WGC3Denum cap) { }
+ virtual void enableVertexAttribArray(WGC3Duint index) { }
+ virtual void finish() { }
+ virtual void flush() { }
+ virtual void framebufferRenderbuffer(WGC3Denum target, WGC3Denum attachment, WGC3Denum renderbuffertarget, WebGLId renderbuffer) { }
+ virtual void framebufferTexture2D(WGC3Denum target, WGC3Denum attachment, WGC3Denum textarget, WebGLId texture, WGC3Dint level) { }
+ virtual void frontFace(WGC3Denum mode) { }
+ virtual void generateMipmap(WGC3Denum target) { }
+
+ virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&) { return false; }
+ virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&) { return false; }
+ virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Dsizei* count, WebGLId* shaders) { }
+ virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) { return 0; }
+ virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { }
+ virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { }
+ virtual Attributes getContextAttributes() { return m_attrs; }
+ virtual WGC3Denum getError() { return 0; }
+ virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { }
+ virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum attachment, WGC3Denum pname, WGC3Dint* value) { }
+
+ virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value)
+ {
+ if (pname == WebCore::GraphicsContext3D::MAX_TEXTURE_SIZE)
+ *value = 1024;
+ }
+
+ virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value)
+ {
+ if (pname == WebCore::GraphicsContext3D::LINK_STATUS)
+ *value = 1;
+ }
+
+ virtual WebString getProgramInfoLog(WebGLId program) { return WebString(); }
+ virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { }
+
+ virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value)
+ {
+ if (pname == WebCore::GraphicsContext3D::COMPILE_STATUS)
+ *value = 1;
+ }
+
+ virtual WebString getShaderInfoLog(WebGLId shader) { return WebString(); }
+ virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precisiontype, WGC3Dint* range, WGC3Dint* precision) { }
+ virtual WebString getShaderSource(WebGLId shader) { return WebString(); }
+ virtual WebString getString(WGC3Denum name) { return WebString(); }
+ virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat* value) { }
+ virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { }
+ virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* value) { }
+ virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* value) { }
+ virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name) { return 0; }
+ virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat* value) { }
+ virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* value) { }
+ virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname) { return 0; }
+
+ virtual void hint(WGC3Denum target, WGC3Denum mode) { }
+ virtual WGC3Dboolean isBuffer(WebGLId buffer) { return false; }
+ virtual WGC3Dboolean isEnabled(WGC3Denum cap) { return false; }
+ virtual WGC3Dboolean isFramebuffer(WebGLId framebuffer) { return false; }
+ virtual WGC3Dboolean isProgram(WebGLId program) { return false; }
+ virtual WGC3Dboolean isRenderbuffer(WebGLId renderbuffer) { return false; }
+ virtual WGC3Dboolean isShader(WebGLId shader) { return false; }
+ virtual WGC3Dboolean isTexture(WebGLId texture) { return false; }
+ virtual void lineWidth(WGC3Dfloat) { }
+ virtual void linkProgram(WebGLId program) { }
+ virtual void pixelStorei(WGC3Denum pname, WGC3Dint param) { }
+ virtual void polygonOffset(WGC3Dfloat factor, WGC3Dfloat units) { }
+
+ virtual void readPixels(WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Denum type, void* pixels) { }
+
+ virtual void releaseShaderCompiler() { }
+
+ virtual void renderbufferStorage(WGC3Denum target, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height) { }
+ virtual void sampleCoverage(WGC3Dclampf value, WGC3Dboolean invert) { }
+ virtual void scissor(WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height) { }
+ virtual void shaderSource(WebGLId shader, const WGC3Dchar* string) { }
+ virtual void stencilFunc(WGC3Denum func, WGC3Dint ref, WGC3Duint mask) { }
+ virtual void stencilFuncSeparate(WGC3Denum face, WGC3Denum func, WGC3Dint ref, WGC3Duint mask) { }
+ virtual void stencilMask(WGC3Duint mask) { }
+ virtual void stencilMaskSeparate(WGC3Denum face, WGC3Duint mask) { }
+ virtual void stencilOp(WGC3Denum fail, WGC3Denum zfail, WGC3Denum zpass) { }
+ virtual void stencilOpSeparate(WGC3Denum face, WGC3Denum fail, WGC3Denum zfail, WGC3Denum zpass) { }
+
+ virtual void texImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border, WGC3Denum format, WGC3Denum type, const void* pixels) { }
+
+ virtual void texParameterf(WGC3Denum target, WGC3Denum pname, WGC3Dfloat param) { }
+ virtual void texParameteri(WGC3Denum target, WGC3Denum pname, WGC3Dint param) { }
+
+ virtual void texSubImage2D(WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset, WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Denum type, const void* pixels) { }
+
+ virtual void uniform1f(WGC3Dint location, WGC3Dfloat x) { }
+ virtual void uniform1fv(WGC3Dint location, WGC3Dsizei count, const WGC3Dfloat* v) { }
+ virtual void uniform1i(WGC3Dint location, WGC3Dint x) { }
+ virtual void uniform1iv(WGC3Dint location, WGC3Dsizei count, const WGC3Dint* v) { }
+ virtual void uniform2f(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y) { }
+ virtual void uniform2fv(WGC3Dint location, WGC3Dsizei count, const WGC3Dfloat* v) { }
+ virtual void uniform2i(WGC3Dint location, WGC3Dint x, WGC3Dint y) { }
+ virtual void uniform2iv(WGC3Dint location, WGC3Dsizei count, const WGC3Dint* v) { }
+ virtual void uniform3f(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z) { }
+ virtual void uniform3fv(WGC3Dint location, WGC3Dsizei count, const WGC3Dfloat* v) { }
+ virtual void uniform3i(WGC3Dint location, WGC3Dint x, WGC3Dint y, WGC3Dint z) { }
+ virtual void uniform3iv(WGC3Dint location, WGC3Dsizei count, const WGC3Dint* v) { }
+ virtual void uniform4f(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z, WGC3Dfloat w) { }
+ virtual void uniform4fv(WGC3Dint location, WGC3Dsizei count, const WGC3Dfloat* v) { }
+ virtual void uniform4i(WGC3Dint location, WGC3Dint x, WGC3Dint y, WGC3Dint z, WGC3Dint w) { }
+ virtual void uniform4iv(WGC3Dint location, WGC3Dsizei count, const WGC3Dint* v) { }
+ virtual void uniformMatrix2fv(WGC3Dint location, WGC3Dsizei count, WGC3Dboolean transpose, const WGC3Dfloat* value) { }
+ virtual void uniformMatrix3fv(WGC3Dint location, WGC3Dsizei count, WGC3Dboolean transpose, const WGC3Dfloat* value) { }
+ virtual void uniformMatrix4fv(WGC3Dint location, WGC3Dsizei count, WGC3Dboolean transpose, const WGC3Dfloat* value) { }
+
+ virtual void useProgram(WebGLId program) { }
+ virtual void validateProgram(WebGLId program) { }
+
+ virtual void vertexAttrib1f(WGC3Duint index, WGC3Dfloat x) { }
+ virtual void vertexAttrib1fv(WGC3Duint index, const WGC3Dfloat* values) { }
+ virtual void vertexAttrib2f(WGC3Duint index, WGC3Dfloat x, WGC3Dfloat y) { }
+ virtual void vertexAttrib2fv(WGC3Duint index, const WGC3Dfloat* values) { }
+ virtual void vertexAttrib3f(WGC3Duint index, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z) { }
+ virtual void vertexAttrib3fv(WGC3Duint index, const WGC3Dfloat* values) { }
+ virtual void vertexAttrib4f(WGC3Duint index, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z, WGC3Dfloat w) { }
+ virtual void vertexAttrib4fv(WGC3Duint index, const WGC3Dfloat* values) { }
+ virtual void vertexAttribPointer(WGC3Duint index, WGC3Dint size, WGC3Denum type, WGC3Dboolean normalized,
+ WGC3Dsizei stride, WGC3Dintptr offset) { }
+
+ virtual void viewport(WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height) { }
+
+ virtual WebGLId createBuffer() { return 1; }
+ virtual WebGLId createFramebuffer() { return 1; }
+ virtual WebGLId createProgram() { return 1; }
+ virtual WebGLId createRenderbuffer() { return 1; }
+ virtual WebGLId createShader(WGC3Denum) { return 1; }
+ virtual WebGLId createTexture() { return m_nextTextureId++; }
+
+ virtual void deleteBuffer(WebGLId) { }
+ virtual void deleteFramebuffer(WebGLId) { }
+ virtual void deleteProgram(WebGLId) { }
+ virtual void deleteRenderbuffer(WebGLId) { }
+ virtual void deleteShader(WebGLId) { }
+ virtual void deleteTexture(WebGLId) { }
+
+ virtual void texStorage2DEXT(WGC3Denum target, WGC3Dint levels, WGC3Duint internalformat,
+ WGC3Dint width, WGC3Dint height) { }
+
+ virtual WebGLId createQueryEXT() { return 1; }
+ virtual void deleteQueryEXT(WebGLId) { }
+ virtual GC3Dboolean isQueryEXT(WebGLId) { return true; }
+ virtual void beginQueryEXT(GC3Denum, WebGLId) { }
+ virtual void endQueryEXT(GC3Denum) { }
+ virtual void getQueryivEXT(GC3Denum, GC3Denum, GC3Dint*) { }
+ virtual void getQueryObjectuivEXT(WebGLId, GC3Denum, GC3Duint*) { }
+
+protected:
+ unsigned m_nextTextureId;
+ Attributes m_attrs;
+};
+
+} // namespace WebKit
+
+#endif // FakeWebGraphicsContext3D_h
diff --git a/cc/test/FakeWebScrollbarThemeGeometry.h b/cc/test/FakeWebScrollbarThemeGeometry.h
new file mode 100644
index 0000000..640da26
--- /dev/null
+++ b/cc/test/FakeWebScrollbarThemeGeometry.h
@@ -0,0 +1,48 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FakeWebScrollbarThemeGeometry_h
+#define FakeWebScrollbarThemeGeometry_h
+
+#include <public/WebScrollbarThemeGeometry.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+
+class FakeWebScrollbarThemeGeometry : public WebKit::WebScrollbarThemeGeometry {
+public:
+ static PassOwnPtr<WebKit::WebScrollbarThemeGeometry> create() { return adoptPtr(new WebKit::FakeWebScrollbarThemeGeometry()); }
+
+ virtual WebKit::WebScrollbarThemeGeometry* clone() const OVERRIDE
+ {
+ return new FakeWebScrollbarThemeGeometry();
+ }
+
+ virtual int thumbPosition(WebScrollbar*) OVERRIDE { return 0; }
+ virtual int thumbLength(WebScrollbar*) OVERRIDE { return 0; }
+ virtual int trackPosition(WebScrollbar*) OVERRIDE { return 0; }
+ virtual int trackLength(WebScrollbar*) OVERRIDE { return 0; }
+ virtual bool hasButtons(WebScrollbar*) OVERRIDE { return false; }
+ virtual bool hasThumb(WebScrollbar*) OVERRIDE { return false; }
+ virtual WebRect trackRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual WebRect thumbRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual int minimumThumbLength(WebScrollbar*) OVERRIDE { return 0; }
+ virtual int scrollbarThickness(WebScrollbar*) OVERRIDE { return 0; }
+ virtual WebRect backButtonStartRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual WebRect backButtonEndRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual WebRect forwardButtonStartRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual WebRect forwardButtonEndRect(WebScrollbar*) OVERRIDE { return WebRect(); }
+ virtual WebRect constrainTrackRectToTrackPieces(WebScrollbar*, const WebRect&) OVERRIDE { return WebRect(); }
+
+ virtual void splitTrack(WebScrollbar*, const WebRect& track, WebRect& startTrack, WebRect& thumb, WebRect& endTrack) OVERRIDE
+ {
+ startTrack = WebRect();
+ thumb = WebRect();
+ endTrack = WebRect();
+ }
+};
+
+} // namespace WebKit
+
+#endif // FakeWebScrollbarThemeGeometry_h
diff --git a/cc/test/MockCCQuadCuller.h b/cc/test/MockCCQuadCuller.h
new file mode 100644
index 0000000..56288d4
--- /dev/null
+++ b/cc/test/MockCCQuadCuller.h
@@ -0,0 +1,58 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MockCCQuadCuller_h
+#define MockCCQuadCuller_h
+
+#include "CCDrawQuad.h"
+#include "CCQuadSink.h"
+#include "IntRect.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class MockCCQuadCuller : public CCQuadSink {
+public:
+ MockCCQuadCuller()
+ : m_activeQuadList(m_quadListStorage)
+ , m_activeSharedQuadStateList(m_sharedQuadStateStorage)
+ { }
+
+ explicit MockCCQuadCuller(CCQuadList& externalQuadList, CCSharedQuadStateList& externalSharedQuadStateList)
+ : m_activeQuadList(externalQuadList)
+ , m_activeSharedQuadStateList(externalSharedQuadStateList)
+ { }
+
+ virtual bool append(WTF::PassOwnPtr<CCDrawQuad> newQuad) OVERRIDE
+ {
+ OwnPtr<CCDrawQuad> drawQuad = newQuad;
+ if (!drawQuad->quadRect().isEmpty()) {
+ m_activeQuadList.append(drawQuad.release());
+ return true;
+ }
+ return false;
+ }
+
+ virtual CCSharedQuadState* useSharedQuadState(PassOwnPtr<CCSharedQuadState> passSharedQuadState) OVERRIDE
+ {
+ OwnPtr<CCSharedQuadState> sharedQuadState(passSharedQuadState);
+ sharedQuadState->id = m_activeSharedQuadStateList.size();
+
+ CCSharedQuadState* rawPtr = sharedQuadState.get();
+ m_activeSharedQuadStateList.append(sharedQuadState.release());
+ return rawPtr;
+ }
+
+ const CCQuadList& quadList() const { return m_activeQuadList; };
+ const CCSharedQuadStateList& sharedQuadStateList() const { return m_activeSharedQuadStateList; };
+
+private:
+ CCQuadList& m_activeQuadList;
+ CCQuadList m_quadListStorage;
+ CCSharedQuadStateList& m_activeSharedQuadStateList;
+ CCSharedQuadStateList m_sharedQuadStateStorage;
+};
+
+} // namespace WebCore
+#endif // MockCCQuadCuller_h
diff --git a/cc/test/run_all_unittests.cc b/cc/test/run_all_unittests.cc
new file mode 100644
index 0000000..de5b1c6
--- /dev/null
+++ b/cc/test/run_all_unittests.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <base/test/test_suite.h>
+#include <gmock/gmock.h>
+#include <webkit/support/webkit_support.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleMock(&argc, argv);
+ TestSuite testSuite(argc, argv);
+ webkit_support::SetUpTestEnvironmentForUnitTests();
+ int result = testSuite.Run();
+ webkit_support::TearDownTestEnvironment();
+
+ return result;
+}
+
diff --git a/webkit/compositor/CCThreadImpl.cpp b/webkit/compositor/CCThreadImpl.cpp
new file mode 100644
index 0000000..18db0ad
--- /dev/null
+++ b/webkit/compositor/CCThreadImpl.cpp
@@ -0,0 +1,98 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "CCThreadImpl.h"
+
+#include "CCCompletionEvent.h"
+#include <public/Platform.h>
+#include <public/WebThread.h>
+
+using WebCore::CCThread;
+using WebCore::CCCompletionEvent;
+
+namespace WebKit {
+
+// Task that, when runs, places the current thread ID into the provided
+// pointer and signals a completion event.
+//
+// Does not provide a PassOwnPtr<GetThreadIDTask>::create method because
+// the resulting object is always handed into a WebThread, which does not understand
+// PassOwnPtrs.
+class GetThreadIDTask : public WebThread::Task {
+public:
+ GetThreadIDTask(ThreadIdentifier* result, CCCompletionEvent* completion)
+ : m_completion(completion)
+ , m_result(result) { }
+
+ virtual ~GetThreadIDTask() { }
+
+ virtual void run()
+ {
+ *m_result = currentThread();
+ m_completion->signal();
+ }
+
+private:
+ CCCompletionEvent* m_completion;
+ ThreadIdentifier* m_result;
+};
+
+// General adapter from a CCThread::Task to a WebThread::Task.
+class CCThreadTaskAdapter : public WebThread::Task {
+public:
+ CCThreadTaskAdapter(PassOwnPtr<CCThread::Task> task) : m_task(task) { }
+
+ virtual ~CCThreadTaskAdapter() { }
+
+ virtual void run()
+ {
+ m_task->performTask();
+ }
+
+private:
+ OwnPtr<CCThread::Task> m_task;
+};
+
+PassOwnPtr<CCThread> CCThreadImpl::create(WebThread* thread)
+{
+ return adoptPtr(new CCThreadImpl(thread));
+}
+
+CCThreadImpl::~CCThreadImpl()
+{
+}
+
+void CCThreadImpl::postTask(PassOwnPtr<CCThread::Task> task)
+{
+ m_thread->postTask(new CCThreadTaskAdapter(task));
+}
+
+void CCThreadImpl::postDelayedTask(PassOwnPtr<CCThread::Task> task, long long delayMs)
+{
+ m_thread->postDelayedTask(new CCThreadTaskAdapter(task), delayMs);
+}
+
+ThreadIdentifier CCThreadImpl::threadID() const
+{
+ return m_threadID;
+}
+
+CCThreadImpl::CCThreadImpl(WebThread* thread)
+ : m_thread(thread)
+{
+ if (thread == WebKit::Platform::current()->currentThread()) {
+ m_threadID = currentThread();
+ return;
+ }
+
+ // Get the threadId for the newly-created thread by running a task
+ // on that thread, blocking on the result.
+ m_threadID = currentThread();
+ CCCompletionEvent completion;
+ m_thread->postTask(new GetThreadIDTask(&m_threadID, &completion));
+ completion.wait();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/CCThreadImpl.h b/webkit/compositor/CCThreadImpl.h
new file mode 100644
index 0000000..094d5b5
--- /dev/null
+++ b/webkit/compositor/CCThreadImpl.h
@@ -0,0 +1,34 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "CCThread.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/Threading.h>
+
+#ifndef CCThreadImpl_h
+#define CCThreadImpl_h
+
+namespace WebKit {
+
+class WebThread;
+
+// Implements CCThread in terms of WebThread.
+class CCThreadImpl : public WebCore::CCThread {
+public:
+ static PassOwnPtr<WebCore::CCThread> create(WebThread*);
+ virtual ~CCThreadImpl();
+ virtual void postTask(PassOwnPtr<WebCore::CCThread::Task>);
+ virtual void postDelayedTask(PassOwnPtr<WebCore::CCThread::Task>, long long delayMs);
+ WTF::ThreadIdentifier threadID() const;
+
+private:
+ explicit CCThreadImpl(WebThread*);
+
+ WebThread* m_thread;
+ WTF::ThreadIdentifier m_threadID;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/webkit/compositor/PlatformGestureCurve.h b/webkit/compositor/PlatformGestureCurve.h
new file mode 100644
index 0000000..5669742
--- /dev/null
+++ b/webkit/compositor/PlatformGestureCurve.h
@@ -0,0 +1,29 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PlatformGestureCurve_h
+#define PlatformGestureCurve_h
+
+namespace WebCore {
+
+class PlatformGestureCurveTarget;
+
+// Abstract interface for curves used by ActivePlatformGestureAnimation. A
+// PlatformGestureCurve defines the animation parameters as a function of time
+// (zero-based), and applies the parameters directly to the target of the
+// animation.
+class PlatformGestureCurve {
+public:
+ virtual ~PlatformGestureCurve() { }
+
+ // Returns a name of the curve for use in debugging.
+ virtual const char* debugName() const = 0;
+
+ // Returns false if curve has finished and can no longer be applied.
+ virtual bool apply(double time, PlatformGestureCurveTarget*) = 0;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/webkit/compositor/PlatformGestureCurveTarget.h b/webkit/compositor/PlatformGestureCurveTarget.h
new file mode 100644
index 0000000..41e96d6
--- /dev/null
+++ b/webkit/compositor/PlatformGestureCurveTarget.h
@@ -0,0 +1,23 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PlatformGestureCurveTarget_h
+#define PlatformGestureCurveTarget_h
+
+namespace WebCore {
+
+class IntPoint;
+
+class PlatformGestureCurveTarget {
+public:
+ virtual void scrollBy(const IntPoint&) = 0;
+ // FIXME: add interfaces for scroll(), etc.
+
+protected:
+ virtual ~PlatformGestureCurveTarget() { }
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/webkit/compositor/TouchpadFlingPlatformGestureCurve.h b/webkit/compositor/TouchpadFlingPlatformGestureCurve.h
new file mode 100644
index 0000000..072e914
--- /dev/null
+++ b/webkit/compositor/TouchpadFlingPlatformGestureCurve.h
@@ -0,0 +1,45 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TouchpadFlingPlatformGestureCurve_h
+#define TouchpadFlingPlatformGestureCurve_h
+
+#include "FloatPoint.h"
+#include "PlatformGestureCurve.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class PlatformGestureCurveTarget;
+
+// Implementation of PlatformGestureCurve suitable for touch pad/screen-based
+// fling scroll. Starts with a flat velocity profile based on 'velocity', which
+// tails off to zero. Time is scaled to that duration of the fling is proportional
+// the initial velocity.
+class TouchpadFlingPlatformGestureCurve : public PlatformGestureCurve {
+public:
+ static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity, IntPoint cumulativeScroll = IntPoint());
+ static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity, float p0, float p1, float p2, float p3, float p4, float curveDuration, IntPoint cumulativeScroll = IntPoint());
+ virtual ~TouchpadFlingPlatformGestureCurve();
+
+ virtual const char* debugName() const OVERRIDE;
+ virtual bool apply(double monotonicTime, PlatformGestureCurveTarget*) OVERRIDE;
+
+private:
+ TouchpadFlingPlatformGestureCurve(const FloatPoint& velocity, float p0, float p1, float p2, float p3, float p4, float curveDuration, const IntPoint& cumulativeScroll);
+
+ FloatPoint m_displacementRatio;
+ IntPoint m_cumulativeScroll;
+ float m_coeffs[5];
+ float m_timeOffset;
+ float m_curveDuration;
+ float m_positionOffset;
+
+ static const int m_maxSearchIterations;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/webkit/compositor/WebAnimationCurveCommon.cpp b/webkit/compositor/WebAnimationCurveCommon.cpp
new file mode 100644
index 0000000..2e45dc5
--- /dev/null
+++ b/webkit/compositor/WebAnimationCurveCommon.cpp
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebAnimationCurveCommon.h"
+
+#include "CCTimingFunction.h"
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+
+PassOwnPtr<WebCore::CCTimingFunction> createTimingFunction(WebAnimationCurve::TimingFunctionType type)
+{
+ switch (type) {
+ case WebAnimationCurve::TimingFunctionTypeEase:
+ return WebCore::CCEaseTimingFunction::create();
+ case WebAnimationCurve::TimingFunctionTypeEaseIn:
+ return WebCore::CCEaseInTimingFunction::create();
+ case WebAnimationCurve::TimingFunctionTypeEaseOut:
+ return WebCore::CCEaseOutTimingFunction::create();
+ case WebAnimationCurve::TimingFunctionTypeEaseInOut:
+ return WebCore::CCEaseInOutTimingFunction::create();
+ case WebAnimationCurve::TimingFunctionTypeLinear:
+ return nullptr;
+ }
+ return nullptr;
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebAnimationCurveCommon.h b/webkit/compositor/WebAnimationCurveCommon.h
new file mode 100644
index 0000000..b911e82
--- /dev/null
+++ b/webkit/compositor/WebAnimationCurveCommon.h
@@ -0,0 +1,19 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebAnimationCurveCommon_h
+#define WebAnimationCurveCommon_h
+
+#include <public/WebAnimationCurve.h>
+#include <wtf/Forward.h>
+
+namespace WebCore {
+class CCTimingFunction;
+}
+
+namespace WebKit {
+PassOwnPtr<WebCore::CCTimingFunction> createTimingFunction(WebAnimationCurve::TimingFunctionType);
+}
+
+#endif // WebAnimationCurveCommon_h
diff --git a/webkit/compositor/WebAnimationImpl.cpp b/webkit/compositor/WebAnimationImpl.cpp
new file mode 100644
index 0000000..e7cc459
--- /dev/null
+++ b/webkit/compositor/WebAnimationImpl.cpp
@@ -0,0 +1,109 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebAnimationImpl.h"
+
+#include "CCActiveAnimation.h"
+#include "CCAnimationCurve.h"
+#include "WebFloatAnimationCurveImpl.h"
+#include "WebTransformAnimationCurveImpl.h"
+#include <public/WebAnimation.h>
+#include <public/WebAnimationCurve.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+using WebCore::CCActiveAnimation;
+
+namespace WebKit {
+
+WebAnimation* WebAnimation::create(const WebAnimationCurve& curve, TargetProperty targetProperty, int animationId)
+{
+ static int nextGroupId = 1;
+ static int nextAnimationId = 1;
+ return new WebAnimationImpl(curve, targetProperty, animationId ? animationId : nextAnimationId++, nextGroupId++);
+}
+
+WebAnimationImpl::WebAnimationImpl(const WebAnimationCurve& webCurve, TargetProperty targetProperty, int animationId, int groupId)
+{
+ WebAnimationCurve::AnimationCurveType curveType = webCurve.type();
+ OwnPtr<WebCore::CCAnimationCurve> curve;
+ switch (curveType) {
+ case WebAnimationCurve::AnimationCurveTypeFloat: {
+ const WebFloatAnimationCurveImpl* floatCurveImpl = static_cast<const WebFloatAnimationCurveImpl*>(&webCurve);
+ curve = floatCurveImpl->cloneToCCAnimationCurve();
+ break;
+ }
+ case WebAnimationCurve::AnimationCurveTypeTransform: {
+ const WebTransformAnimationCurveImpl* transformCurveImpl = static_cast<const WebTransformAnimationCurveImpl*>(&webCurve);
+ curve = transformCurveImpl->cloneToCCAnimationCurve();
+ break;
+ }
+ }
+ m_animation = CCActiveAnimation::create(curve.release(), animationId, groupId, static_cast<WebCore::CCActiveAnimation::TargetProperty>(targetProperty));
+}
+
+WebAnimationImpl::~WebAnimationImpl()
+{
+}
+
+int WebAnimationImpl::id()
+{
+ return m_animation->id();
+}
+
+WebAnimation::TargetProperty WebAnimationImpl::targetProperty() const
+{
+ return static_cast<WebAnimationImpl::TargetProperty>(m_animation->targetProperty());
+}
+
+int WebAnimationImpl::iterations() const
+{
+ return m_animation->iterations();
+}
+
+void WebAnimationImpl::setIterations(int n)
+{
+ m_animation->setIterations(n);
+}
+
+double WebAnimationImpl::startTime() const
+{
+ return m_animation->startTime();
+}
+
+void WebAnimationImpl::setStartTime(double monotonicTime)
+{
+ m_animation->setStartTime(monotonicTime);
+}
+
+double WebAnimationImpl::timeOffset() const
+{
+ return m_animation->timeOffset();
+}
+
+void WebAnimationImpl::setTimeOffset(double monotonicTime)
+{
+ m_animation->setTimeOffset(monotonicTime);
+}
+
+bool WebAnimationImpl::alternatesDirection() const
+{
+ return m_animation->alternatesDirection();
+}
+
+void WebAnimationImpl::setAlternatesDirection(bool alternates)
+{
+ m_animation->setAlternatesDirection(alternates);
+}
+
+PassOwnPtr<WebCore::CCActiveAnimation> WebAnimationImpl::cloneToCCAnimation()
+{
+ OwnPtr<WebCore::CCActiveAnimation> toReturn(m_animation->clone(WebCore::CCActiveAnimation::NonControllingInstance));
+ toReturn->setNeedsSynchronizedStartTime(true);
+ return toReturn.release();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebAnimationImpl.h b/webkit/compositor/WebAnimationImpl.h
new file mode 100644
index 0000000..a80fe18
--- /dev/null
+++ b/webkit/compositor/WebAnimationImpl.h
@@ -0,0 +1,43 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebAnimationImpl_h
+#define WebAnimationImpl_h
+
+#include <public/WebAnimation.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+class CCActiveAnimation;
+}
+
+namespace WebKit {
+
+class WebAnimationImpl : public WebAnimation {
+public:
+ WebAnimationImpl(const WebAnimationCurve&, TargetProperty, int animationId, int groupId);
+ virtual ~WebAnimationImpl();
+
+ // WebAnimation implementation
+ virtual int id() OVERRIDE;
+ virtual TargetProperty targetProperty() const OVERRIDE;
+ virtual int iterations() const OVERRIDE;
+ virtual void setIterations(int) OVERRIDE;
+ virtual double startTime() const OVERRIDE;
+ virtual void setStartTime(double monotonicTime) OVERRIDE;
+ virtual double timeOffset() const OVERRIDE;
+ virtual void setTimeOffset(double monotonicTime) OVERRIDE;
+ virtual bool alternatesDirection() const OVERRIDE;
+ virtual void setAlternatesDirection(bool) OVERRIDE;
+
+ PassOwnPtr<WebCore::CCActiveAnimation> cloneToCCAnimation();
+private:
+ OwnPtr<WebCore::CCActiveAnimation> m_animation;
+};
+
+}
+
+#endif // WebAnimationImpl_h
+
diff --git a/webkit/compositor/WebCompositorImpl.cpp b/webkit/compositor/WebCompositorImpl.cpp
new file mode 100644
index 0000000..530fa9b
--- /dev/null
+++ b/webkit/compositor/WebCompositorImpl.cpp
@@ -0,0 +1,98 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebCompositorImpl.h"
+
+#include "CCLayerTreeHost.h"
+#include "CCProxy.h"
+#include "CCSettings.h"
+#include "CCThreadImpl.h"
+#include <public/Platform.h>
+#include <wtf/ThreadingPrimitives.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+bool WebCompositorImpl::s_initialized = false;
+CCThread* WebCompositorImpl::s_mainThread = 0;
+CCThread* WebCompositorImpl::s_implThread = 0;
+
+void WebCompositor::initialize(WebThread* implThread)
+{
+ WebCompositorImpl::initialize(implThread);
+}
+
+bool WebCompositor::threadingEnabled()
+{
+ return WebCompositorImpl::threadingEnabled();
+}
+
+void WebCompositor::shutdown()
+{
+ WebCompositorImpl::shutdown();
+ CCSettings::reset();
+}
+
+void WebCompositor::setPerTilePaintingEnabled(bool enabled)
+{
+ ASSERT(!WebCompositorImpl::initialized());
+ CCSettings::setPerTilePaintingEnabled(enabled);
+}
+
+void WebCompositor::setPartialSwapEnabled(bool enabled)
+{
+ ASSERT(!WebCompositorImpl::initialized());
+ CCSettings::setPartialSwapEnabled(enabled);
+}
+
+void WebCompositor::setAcceleratedAnimationEnabled(bool enabled)
+{
+ ASSERT(!WebCompositorImpl::initialized());
+ CCSettings::setAcceleratedAnimationEnabled(enabled);
+}
+
+void WebCompositorImpl::initialize(WebThread* implThread)
+{
+ ASSERT(!s_initialized);
+ s_initialized = true;
+
+ s_mainThread = CCThreadImpl::create(WebKit::Platform::current()->currentThread()).leakPtr();
+ CCProxy::setMainThread(s_mainThread);
+ if (implThread) {
+ s_implThread = CCThreadImpl::create(implThread).leakPtr();
+ CCProxy::setImplThread(s_implThread);
+ } else
+ CCProxy::setImplThread(0);
+}
+
+bool WebCompositorImpl::threadingEnabled()
+{
+ return s_implThread;
+}
+
+bool WebCompositorImpl::initialized()
+{
+ return s_initialized;
+}
+
+void WebCompositorImpl::shutdown()
+{
+ ASSERT(s_initialized);
+ ASSERT(!CCLayerTreeHost::anyLayerTreeHostInstanceExists());
+
+ if (s_implThread) {
+ delete s_implThread;
+ s_implThread = 0;
+ }
+ delete s_mainThread;
+ s_mainThread = 0;
+ CCProxy::setImplThread(0);
+ CCProxy::setMainThread(0);
+ s_initialized = false;
+}
+
+}
diff --git a/webkit/compositor/WebCompositorImpl.h b/webkit/compositor/WebCompositorImpl.h
new file mode 100644
index 0000000..8c386bc
--- /dev/null
+++ b/webkit/compositor/WebCompositorImpl.h
@@ -0,0 +1,41 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebCompositorImpl_h
+#define WebCompositorImpl_h
+
+#include <public/WebCompositor.h>
+
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+class CCThread;
+}
+
+namespace WebKit {
+
+class WebThread;
+
+class WebCompositorImpl : public WebCompositor {
+ WTF_MAKE_NONCOPYABLE(WebCompositorImpl);
+public:
+ static bool initialized();
+
+private:
+
+ friend class WebCompositor;
+ static void initialize(WebThread* implThread);
+ static bool threadingEnabled();
+ static void shutdown();
+
+ static bool s_initialized;
+ static WebCore::CCThread* s_mainThread;
+ static WebCore::CCThread* s_implThread;
+};
+
+}
+
+#endif // WebCompositorImpl_h
diff --git a/webkit/compositor/WebCompositorInputHandlerImpl.cpp b/webkit/compositor/WebCompositorInputHandlerImpl.cpp
new file mode 100644
index 0000000..20c1e96c
--- /dev/null
+++ b/webkit/compositor/WebCompositorInputHandlerImpl.cpp
@@ -0,0 +1,341 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebCompositorInputHandlerImpl.h"
+
+#include "CCActiveGestureAnimation.h"
+#include "CCProxy.h"
+#include "PlatformGestureCurveTarget.h"
+#include "TouchpadFlingPlatformGestureCurve.h"
+#include "TraceEvent.h"
+#include "WebCompositorImpl.h"
+#include "WebCompositorInputHandlerClient.h"
+#include "WebInputEvent.h"
+#include <wtf/ThreadingPrimitives.h>
+
+using namespace WebCore;
+
+namespace WebCore {
+
+PassOwnPtr<CCInputHandler> CCInputHandler::create(CCInputHandlerClient* inputHandlerClient)
+{
+ return WebKit::WebCompositorInputHandlerImpl::create(inputHandlerClient);
+}
+
+class PlatformGestureToCCGestureAdapter : public CCGestureCurve, public PlatformGestureCurveTarget {
+public:
+ static PassOwnPtr<CCGestureCurve> create(PassOwnPtr<PlatformGestureCurve> platformCurve)
+ {
+ return adoptPtr(new PlatformGestureToCCGestureAdapter(platformCurve));
+ }
+
+ virtual const char* debugName() const
+ {
+ return m_curve->debugName();
+ }
+
+ virtual bool apply(double time, CCGestureCurveTarget* target)
+ {
+ ASSERT(target);
+ m_target = target;
+ return m_curve->apply(time, this);
+ }
+
+ virtual void scrollBy(const IntPoint& scrollDelta)
+ {
+ ASSERT(m_target);
+ m_target->scrollBy(scrollDelta);
+ }
+
+private:
+ PlatformGestureToCCGestureAdapter(PassOwnPtr<PlatformGestureCurve> curve)
+ : m_curve(curve)
+ , m_target(0)
+ {
+ }
+
+ OwnPtr<PlatformGestureCurve> m_curve;
+ CCGestureCurveTarget* m_target;
+};
+
+}
+
+namespace WebKit {
+
+// These statics may only be accessed from the compositor thread.
+int WebCompositorInputHandlerImpl::s_nextAvailableIdentifier = 1;
+HashSet<WebCompositorInputHandlerImpl*>* WebCompositorInputHandlerImpl::s_compositors = 0;
+
+WebCompositorInputHandler* WebCompositorInputHandler::fromIdentifier(int identifier)
+{
+ return WebCompositorInputHandlerImpl::fromIdentifier(identifier);
+}
+
+PassOwnPtr<WebCompositorInputHandlerImpl> WebCompositorInputHandlerImpl::create(WebCore::CCInputHandlerClient* inputHandlerClient)
+{
+ return adoptPtr(new WebCompositorInputHandlerImpl(inputHandlerClient));
+}
+
+WebCompositorInputHandler* WebCompositorInputHandlerImpl::fromIdentifier(int identifier)
+{
+ ASSERT(WebCompositorImpl::initialized());
+ ASSERT(CCProxy::isImplThread());
+
+ if (!s_compositors)
+ return 0;
+
+ for (HashSet<WebCompositorInputHandlerImpl*>::iterator it = s_compositors->begin(); it != s_compositors->end(); ++it) {
+ if ((*it)->identifier() == identifier)
+ return *it;
+ }
+ return 0;
+}
+
+WebCompositorInputHandlerImpl::WebCompositorInputHandlerImpl(CCInputHandlerClient* inputHandlerClient)
+ : m_client(0)
+ , m_identifier(s_nextAvailableIdentifier++)
+ , m_inputHandlerClient(inputHandlerClient)
+#ifndef NDEBUG
+ , m_expectScrollUpdateEnd(false)
+ , m_expectPinchUpdateEnd(false)
+#endif
+ , m_gestureScrollStarted(false)
+{
+ ASSERT(CCProxy::isImplThread());
+
+ if (!s_compositors)
+ s_compositors = new HashSet<WebCompositorInputHandlerImpl*>;
+ s_compositors->add(this);
+}
+
+WebCompositorInputHandlerImpl::~WebCompositorInputHandlerImpl()
+{
+ ASSERT(CCProxy::isImplThread());
+ if (m_client)
+ m_client->willShutdown();
+
+ ASSERT(s_compositors);
+ s_compositors->remove(this);
+ if (!s_compositors->size()) {
+ delete s_compositors;
+ s_compositors = 0;
+ }
+}
+
+void WebCompositorInputHandlerImpl::setClient(WebCompositorInputHandlerClient* client)
+{
+ ASSERT(CCProxy::isImplThread());
+ // It's valid to set a new client if we've never had one or to clear the client, but it's not valid to change from having one client to a different one.
+ ASSERT(!m_client || !client);
+ m_client = client;
+}
+
+void WebCompositorInputHandlerImpl::handleInputEvent(const WebInputEvent& event)
+{
+ ASSERT(CCProxy::isImplThread());
+ ASSERT(m_client);
+
+ WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(event);
+ switch (disposition) {
+ case DidHandle:
+ m_client->didHandleInputEvent();
+ break;
+ case DidNotHandle:
+ m_client->didNotHandleInputEvent(true /* sendToWidget */);
+ break;
+ case DropEvent:
+ m_client->didNotHandleInputEvent(false /* sendToWidget */);
+ break;
+ }
+}
+
+WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleInputEventInternal(const WebInputEvent& event)
+{
+ if (event.type == WebInputEvent::MouseWheel) {
+ const WebMouseWheelEvent& wheelEvent = *static_cast<const WebMouseWheelEvent*>(&event);
+ CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(wheelEvent.x, wheelEvent.y), CCInputHandlerClient::Wheel);
+ switch (scrollStatus) {
+ case CCInputHandlerClient::ScrollStarted: {
+ TRACE_EVENT_INSTANT2("cc", "WebCompositorInputHandlerImpl::handleInput wheel scroll", "deltaX", -wheelEvent.deltaX, "deltaY", -wheelEvent.deltaY);
+ m_inputHandlerClient->scrollBy(IntPoint(wheelEvent.x, wheelEvent.y), IntSize(-wheelEvent.deltaX, -wheelEvent.deltaY));
+ m_inputHandlerClient->scrollEnd();
+ return DidHandle;
+ }
+ case CCInputHandlerClient::ScrollIgnored:
+ // FIXME: This should be DropEvent, but in cases where we fail to properly sync scrollability it's safer to send the
+ // event to the main thread. Change back to DropEvent once we have synchronization bugs sorted out.
+ return DidNotHandle;
+ case CCInputHandlerClient::ScrollOnMainThread:
+ return DidNotHandle;
+ }
+ } else if (event.type == WebInputEvent::GestureScrollBegin) {
+ ASSERT(!m_gestureScrollStarted);
+ ASSERT(!m_expectScrollUpdateEnd);
+#ifndef NDEBUG
+ m_expectScrollUpdateEnd = true;
+#endif
+ const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
+ CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(gestureEvent.x, gestureEvent.y), CCInputHandlerClient::Gesture);
+ switch (scrollStatus) {
+ case CCInputHandlerClient::ScrollStarted:
+ m_gestureScrollStarted = true;
+ return DidHandle;
+ case CCInputHandlerClient::ScrollOnMainThread:
+ return DidNotHandle;
+ case CCInputHandlerClient::ScrollIgnored:
+ return DropEvent;
+ }
+ } else if (event.type == WebInputEvent::GestureScrollUpdate) {
+ ASSERT(m_expectScrollUpdateEnd);
+
+ if (!m_gestureScrollStarted)
+ return DidNotHandle;
+
+ const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
+ m_inputHandlerClient->scrollBy(IntPoint(gestureEvent.x, gestureEvent.y), IntSize(-gestureEvent.deltaX, -gestureEvent.deltaY));
+ return DidHandle;
+ } else if (event.type == WebInputEvent::GestureScrollEnd) {
+ ASSERT(m_expectScrollUpdateEnd);
+#ifndef NDEBUG
+ m_expectScrollUpdateEnd = false;
+#endif
+ if (!m_gestureScrollStarted)
+ return DidNotHandle;
+
+ m_inputHandlerClient->scrollEnd();
+ m_gestureScrollStarted = false;
+ return DidHandle;
+ } else if (event.type == WebInputEvent::GesturePinchBegin) {
+ ASSERT(!m_expectPinchUpdateEnd);
+#ifndef NDEBUG
+ m_expectPinchUpdateEnd = true;
+#endif
+ m_inputHandlerClient->pinchGestureBegin();
+ return DidHandle;
+ } else if (event.type == WebInputEvent::GesturePinchEnd) {
+ ASSERT(m_expectPinchUpdateEnd);
+#ifndef NDEBUG
+ m_expectPinchUpdateEnd = false;
+#endif
+ m_inputHandlerClient->pinchGestureEnd();
+ return DidHandle;
+ } else if (event.type == WebInputEvent::GesturePinchUpdate) {
+ ASSERT(m_expectPinchUpdateEnd);
+ const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
+ m_inputHandlerClient->pinchGestureUpdate(gestureEvent.deltaX, IntPoint(gestureEvent.x, gestureEvent.y));
+ return DidHandle;
+ } else if (event.type == WebInputEvent::GestureFlingStart) {
+ const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
+ return handleGestureFling(gestureEvent);
+ } else if (event.type == WebInputEvent::GestureFlingCancel) {
+ if (cancelCurrentFling())
+ return DidHandle;
+ } else if (WebInputEvent::isKeyboardEventType(event.type)) {
+ cancelCurrentFling();
+ }
+
+ return DidNotHandle;
+}
+
+WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleGestureFling(const WebGestureEvent& gestureEvent)
+{
+ CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(gestureEvent.x, gestureEvent.y), CCInputHandlerClient::Gesture);
+ switch (scrollStatus) {
+ case CCInputHandlerClient::ScrollStarted: {
+ TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::started");
+ OwnPtr<PlatformGestureCurve> flingCurve = TouchpadFlingPlatformGestureCurve::create(FloatPoint(gestureEvent.deltaX, gestureEvent.deltaY));
+ m_wheelFlingAnimation = CCActiveGestureAnimation::create(PlatformGestureToCCGestureAdapter::create(flingCurve.release()), this);
+ m_wheelFlingParameters.delta = WebFloatPoint(gestureEvent.deltaX, gestureEvent.deltaY);
+ m_wheelFlingParameters.point = WebPoint(gestureEvent.x, gestureEvent.y);
+ m_wheelFlingParameters.globalPoint = WebPoint(gestureEvent.globalX, gestureEvent.globalY);
+ m_wheelFlingParameters.modifiers = gestureEvent.modifiers;
+ m_inputHandlerClient->scheduleAnimation();
+ return DidHandle;
+ }
+ case CCInputHandlerClient::ScrollOnMainThread: {
+ TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::scrollOnMainThread");
+ return DidNotHandle;
+ }
+ case CCInputHandlerClient::ScrollIgnored: {
+ TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::ignored");
+ // We still pass the curve to the main thread if there's nothing scrollable, in case something
+ // registers a handler before the curve is over.
+ return DidNotHandle;
+ }
+ }
+ return DidNotHandle;
+}
+
+int WebCompositorInputHandlerImpl::identifier() const
+{
+ ASSERT(CCProxy::isImplThread());
+ return m_identifier;
+}
+
+void WebCompositorInputHandlerImpl::animate(double monotonicTime)
+{
+ if (!m_wheelFlingAnimation)
+ return;
+
+ if (!m_wheelFlingParameters.startTime)
+ m_wheelFlingParameters.startTime = monotonicTime;
+
+ if (m_wheelFlingAnimation->animate(monotonicTime))
+ m_inputHandlerClient->scheduleAnimation();
+ else {
+ TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::animate::flingOver");
+ cancelCurrentFling();
+ }
+}
+
+bool WebCompositorInputHandlerImpl::cancelCurrentFling()
+{
+ bool hadFlingAnimation = m_wheelFlingAnimation;
+ TRACE_EVENT_INSTANT1("cc", "WebCompositorInputHandlerImpl::cancelCurrentFling", "hadFlingAnimation", hadFlingAnimation);
+ m_wheelFlingAnimation.clear();
+ m_wheelFlingParameters = WebActiveWheelFlingParameters();
+ return hadFlingAnimation;
+}
+
+void WebCompositorInputHandlerImpl::scrollBy(const IntPoint& increment)
+{
+ if (increment == IntPoint::zero())
+ return;
+
+ TRACE_EVENT2("cc", "WebCompositorInputHandlerImpl::scrollBy", "x", increment.x(), "y", increment.y());
+ WebMouseWheelEvent syntheticWheel;
+ syntheticWheel.type = WebInputEvent::MouseWheel;
+ syntheticWheel.deltaX = increment.x();
+ syntheticWheel.deltaY = increment.y();
+ syntheticWheel.hasPreciseScrollingDeltas = true;
+ syntheticWheel.x = m_wheelFlingParameters.point.x;
+ syntheticWheel.y = m_wheelFlingParameters.point.y;
+ syntheticWheel.globalX = m_wheelFlingParameters.globalPoint.x;
+ syntheticWheel.globalY = m_wheelFlingParameters.globalPoint.y;
+ syntheticWheel.modifiers = m_wheelFlingParameters.modifiers;
+
+ WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(syntheticWheel);
+ switch (disposition) {
+ case DidHandle:
+ m_wheelFlingParameters.cumulativeScroll.width += increment.x();
+ m_wheelFlingParameters.cumulativeScroll.height += increment.y();
+ case DropEvent:
+ break;
+ case DidNotHandle:
+ TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::scrollBy::AbortFling");
+ // If we got a DidNotHandle, that means we need to deliver wheels on the main thread.
+ // In this case we need to schedule a commit and transfer the fling curve over to the main
+ // thread and run the rest of the wheels from there.
+ // This can happen when flinging a page that contains a scrollable subarea that we can't
+ // scroll on the thread if the fling starts outside the subarea but then is flung "under" the
+ // pointer.
+ m_client->transferActiveWheelFlingAnimation(m_wheelFlingParameters);
+ cancelCurrentFling();
+ break;
+ }
+}
+
+}
diff --git a/webkit/compositor/WebCompositorInputHandlerImpl.h b/webkit/compositor/WebCompositorInputHandlerImpl.h
new file mode 100644
index 0000000..6f1c3e1
--- /dev/null
+++ b/webkit/compositor/WebCompositorInputHandlerImpl.h
@@ -0,0 +1,85 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebCompositorInputHandlerImpl_h
+#define WebCompositorInputHandlerImpl_h
+
+#include "CCGestureCurve.h"
+#include "CCInputHandler.h"
+#include "WebActiveWheelFlingParameters.h"
+#include "WebCompositorInputHandler.h"
+#include "WebInputEvent.h"
+#include <public/WebCompositor.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WTF {
+class Mutex;
+}
+
+namespace WebCore {
+class IntPoint;
+class CCGestureCurveTarget;
+class CCInputHandlerClient;
+class CCThread;
+}
+
+namespace WebKit {
+
+class WebCompositorInputHandlerClient;
+
+class WebCompositorInputHandlerImpl : public WebCompositorInputHandler, public WebCore::CCInputHandler, public WebCore::CCGestureCurveTarget {
+ WTF_MAKE_NONCOPYABLE(WebCompositorInputHandlerImpl);
+public:
+ static PassOwnPtr<WebCompositorInputHandlerImpl> create(WebCore::CCInputHandlerClient*);
+ static WebCompositorInputHandler* fromIdentifier(int identifier);
+
+ virtual ~WebCompositorInputHandlerImpl();
+
+ // WebCompositorInputHandler implementation.
+ virtual void setClient(WebCompositorInputHandlerClient*);
+ virtual void handleInputEvent(const WebInputEvent&);
+
+ // WebCore::CCInputHandler implementation.
+ virtual int identifier() const;
+ virtual void animate(double monotonicTime);
+
+ // WebCore::CCGestureCurveTarget implementation.
+ virtual void scrollBy(const WebCore::IntPoint&);
+
+private:
+ explicit WebCompositorInputHandlerImpl(WebCore::CCInputHandlerClient*);
+
+ enum EventDisposition { DidHandle, DidNotHandle, DropEvent };
+ // This function processes the input event and determines the disposition, but does not make
+ // any calls out to the WebCompositorInputHandlerClient. Some input types defer to helpers.
+ EventDisposition handleInputEventInternal(const WebInputEvent&);
+
+ EventDisposition handleGestureFling(const WebGestureEvent&);
+
+ // Returns true if we actually had an active fling to cancel.
+ bool cancelCurrentFling();
+
+ OwnPtr<WebCore::CCActiveGestureAnimation> m_wheelFlingAnimation;
+ // Parameters for the active fling animation, stored in case we need to transfer it out later.
+ WebActiveWheelFlingParameters m_wheelFlingParameters;
+
+ WebCompositorInputHandlerClient* m_client;
+ int m_identifier;
+ WebCore::CCInputHandlerClient* m_inputHandlerClient;
+
+#ifndef NDEBUG
+ bool m_expectScrollUpdateEnd;
+ bool m_expectPinchUpdateEnd;
+#endif
+ bool m_gestureScrollStarted;
+
+ static int s_nextAvailableIdentifier;
+ static HashSet<WebCompositorInputHandlerImpl*>* s_compositors;
+};
+
+}
+
+#endif // WebCompositorImpl_h
diff --git a/webkit/compositor/WebContentLayerImpl.cpp b/webkit/compositor/WebContentLayerImpl.cpp
new file mode 100644
index 0000000..c1f3a1a
--- /dev/null
+++ b/webkit/compositor/WebContentLayerImpl.cpp
@@ -0,0 +1,71 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebContentLayerImpl.h"
+
+#include "SkMatrix44.h"
+#include <public/WebContentLayerClient.h>
+#include <public/WebFloatPoint.h>
+#include <public/WebFloatRect.h>
+#include <public/WebRect.h>
+#include <public/WebSize.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+WebContentLayer* WebContentLayer::create(WebContentLayerClient* client)
+{
+ return new WebContentLayerImpl(client);
+}
+
+WebContentLayerImpl::WebContentLayerImpl(WebContentLayerClient* client)
+ : m_webLayerImpl(adoptPtr(new WebLayerImpl(ContentLayerChromium::create(this))))
+ , m_client(client)
+{
+ m_webLayerImpl->layer()->setIsDrawable(true);
+}
+
+WebContentLayerImpl::~WebContentLayerImpl()
+{
+ static_cast<ContentLayerChromium*>(m_webLayerImpl->layer())->clearDelegate();
+}
+
+WebLayer* WebContentLayerImpl::layer()
+{
+ return m_webLayerImpl.get();
+}
+
+void WebContentLayerImpl::setDoubleSided(bool doubleSided)
+{
+ m_webLayerImpl->layer()->setDoubleSided(doubleSided);
+}
+
+void WebContentLayerImpl::setContentsScale(float scale)
+{
+ m_webLayerImpl->layer()->setContentsScale(scale);
+}
+
+void WebContentLayerImpl::setUseLCDText(bool enable)
+{
+ m_webLayerImpl->layer()->setUseLCDText(enable);
+}
+
+void WebContentLayerImpl::setDrawCheckerboardForMissingTiles(bool enable)
+{
+ m_webLayerImpl->layer()->setDrawCheckerboardForMissingTiles(enable);
+}
+
+
+void WebContentLayerImpl::paintContents(SkCanvas* canvas, const IntRect& clip, FloatRect& opaque)
+{
+ if (!m_client)
+ return;
+ WebFloatRect webOpaque;
+ m_client->paintContents(canvas, WebRect(clip), webOpaque);
+ opaque = webOpaque;
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebContentLayerImpl.h b/webkit/compositor/WebContentLayerImpl.h
new file mode 100644
index 0000000..ee75b16
--- /dev/null
+++ b/webkit/compositor/WebContentLayerImpl.h
@@ -0,0 +1,41 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebContentLayerImpl_h
+#define WebContentLayerImpl_h
+
+#include "ContentLayerChromium.h"
+#include "WebLayerImpl.h"
+#include <public/WebContentLayer.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebKit {
+class WebContentLayerClient;
+
+class WebContentLayerImpl : public WebContentLayer,
+ public WebCore::ContentLayerDelegate {
+public:
+ explicit WebContentLayerImpl(WebContentLayerClient*);
+
+ // WebContentLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual void setDoubleSided(bool) OVERRIDE;
+ virtual void setContentsScale(float) OVERRIDE;
+ virtual void setUseLCDText(bool) OVERRIDE;
+ virtual void setDrawCheckerboardForMissingTiles(bool) OVERRIDE;
+
+protected:
+ virtual ~WebContentLayerImpl();
+
+ // ContentLayerDelegate implementation.
+ virtual void paintContents(SkCanvas*, const WebCore::IntRect& clip, WebCore::FloatRect& opaque) OVERRIDE;
+
+ OwnPtr<WebLayerImpl> m_webLayerImpl;
+ WebContentLayerClient* m_client;
+ bool m_drawsContent;
+};
+
+} // namespace WebKit
+
+#endif // WebContentLayerImpl_h
diff --git a/webkit/compositor/WebExternalTextureLayerImpl.cpp b/webkit/compositor/WebExternalTextureLayerImpl.cpp
new file mode 100644
index 0000000..0537fc2
--- /dev/null
+++ b/webkit/compositor/WebExternalTextureLayerImpl.cpp
@@ -0,0 +1,111 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebExternalTextureLayerImpl.h"
+
+#include "CCTextureUpdateQueue.h"
+#include "TextureLayerChromium.h"
+#include "WebLayerImpl.h"
+#include <public/WebExternalTextureLayerClient.h>
+#include <public/WebFloatRect.h>
+#include <public/WebSize.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+WebExternalTextureLayer* WebExternalTextureLayer::create(WebExternalTextureLayerClient* client)
+{
+ return new WebExternalTextureLayerImpl(client);
+}
+
+WebExternalTextureLayerImpl::WebExternalTextureLayerImpl(WebExternalTextureLayerClient* client)
+ : m_client(client)
+{
+ RefPtr<TextureLayerChromium> layer;
+ if (m_client)
+ layer = TextureLayerChromium::create(this);
+ else
+ layer = TextureLayerChromium::create(0);
+ layer->setIsDrawable(true);
+ m_layer = adoptPtr(new WebLayerImpl(layer.release()));
+}
+
+WebExternalTextureLayerImpl::~WebExternalTextureLayerImpl()
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->clearClient();
+}
+
+WebLayer* WebExternalTextureLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+void WebExternalTextureLayerImpl::setTextureId(unsigned id)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setTextureId(id);
+}
+
+void WebExternalTextureLayerImpl::setFlipped(bool flipped)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setFlipped(flipped);
+}
+
+void WebExternalTextureLayerImpl::setUVRect(const WebFloatRect& rect)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setUVRect(rect);
+}
+
+void WebExternalTextureLayerImpl::setOpaque(bool opaque)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setOpaque(opaque);
+}
+
+void WebExternalTextureLayerImpl::setPremultipliedAlpha(bool premultipliedAlpha)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setPremultipliedAlpha(premultipliedAlpha);
+}
+
+void WebExternalTextureLayerImpl::willModifyTexture()
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->willModifyTexture();
+}
+
+void WebExternalTextureLayerImpl::setRateLimitContext(bool rateLimit)
+{
+ static_cast<TextureLayerChromium*>(m_layer->layer())->setRateLimitContext(rateLimit);
+}
+
+class WebTextureUpdaterImpl : public WebTextureUpdater {
+public:
+ explicit WebTextureUpdaterImpl(CCTextureUpdateQueue& queue)
+ : m_queue(queue)
+ {
+ }
+
+ virtual void appendCopy(unsigned sourceTexture, unsigned destinationTexture, WebSize size) OVERRIDE
+ {
+ TextureCopier::Parameters copy = { sourceTexture, destinationTexture, size };
+ m_queue.appendCopy(copy);
+ }
+
+private:
+ CCTextureUpdateQueue& m_queue;
+};
+
+unsigned WebExternalTextureLayerImpl::prepareTexture(CCTextureUpdateQueue& queue)
+{
+ ASSERT(m_client);
+ WebTextureUpdaterImpl updaterImpl(queue);
+ return m_client->prepareTexture(updaterImpl);
+}
+
+WebGraphicsContext3D* WebExternalTextureLayerImpl::context()
+{
+ ASSERT(m_client);
+ return m_client->context();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebExternalTextureLayerImpl.h b/webkit/compositor/WebExternalTextureLayerImpl.h
new file mode 100644
index 0000000..848ef0c
--- /dev/null
+++ b/webkit/compositor/WebExternalTextureLayerImpl.h
@@ -0,0 +1,43 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebExternalTextureLayerImpl_h
+#define WebExternalTextureLayerImpl_h
+
+#include "TextureLayerChromium.h"
+#include <public/WebExternalTextureLayer.h>
+
+namespace WebKit {
+
+class WebLayerImpl;
+
+class WebExternalTextureLayerImpl : public WebExternalTextureLayer,
+ public WebCore::TextureLayerChromiumClient {
+public:
+ explicit WebExternalTextureLayerImpl(WebExternalTextureLayerClient*);
+ virtual ~WebExternalTextureLayerImpl();
+
+ // WebExternalTextureLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual void setTextureId(unsigned) OVERRIDE;
+ virtual void setFlipped(bool) OVERRIDE;
+ virtual void setUVRect(const WebFloatRect&) OVERRIDE;
+ virtual void setOpaque(bool) OVERRIDE;
+ virtual void setPremultipliedAlpha(bool) OVERRIDE;
+ virtual void willModifyTexture() OVERRIDE;
+ virtual void setRateLimitContext(bool) OVERRIDE;
+
+ // TextureLayerChromiumClient implementation.
+ virtual unsigned prepareTexture(WebCore::CCTextureUpdateQueue&) OVERRIDE;
+ virtual WebGraphicsContext3D* context() OVERRIDE;
+
+private:
+ WebExternalTextureLayerClient* m_client;
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+}
+
+#endif // WebExternalTextureLayerImpl_h
+
diff --git a/webkit/compositor/WebFloatAnimationCurveImpl.cpp b/webkit/compositor/WebFloatAnimationCurveImpl.cpp
new file mode 100644
index 0000000..7ea8a89
--- /dev/null
+++ b/webkit/compositor/WebFloatAnimationCurveImpl.cpp
@@ -0,0 +1,62 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebFloatAnimationCurveImpl.h"
+
+#include "CCAnimationCurve.h"
+#include "CCKeyframedAnimationCurve.h"
+#include "CCTimingFunction.h"
+#include "WebAnimationCurveCommon.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+
+WebFloatAnimationCurve* WebFloatAnimationCurve::create()
+{
+ return new WebFloatAnimationCurveImpl(WebCore::CCKeyframedFloatAnimationCurve::create());
+}
+
+WebFloatAnimationCurveImpl::WebFloatAnimationCurveImpl(PassOwnPtr<WebCore::CCKeyframedFloatAnimationCurve> curve)
+ : m_curve(curve)
+{
+}
+
+WebFloatAnimationCurveImpl::~WebFloatAnimationCurveImpl()
+{
+}
+
+WebAnimationCurve::AnimationCurveType WebFloatAnimationCurveImpl::type() const
+{
+ return WebAnimationCurve::AnimationCurveTypeFloat;
+}
+
+void WebFloatAnimationCurveImpl::add(const WebFloatKeyframe& keyframe)
+{
+ add(keyframe, TimingFunctionTypeEase);
+}
+
+void WebFloatAnimationCurveImpl::add(const WebFloatKeyframe& keyframe, TimingFunctionType type)
+{
+ m_curve->addKeyframe(WebCore::CCFloatKeyframe::create(keyframe.time, keyframe.value, createTimingFunction(type)));
+}
+
+void WebFloatAnimationCurveImpl::add(const WebFloatKeyframe& keyframe, double x1, double y1, double x2, double y2)
+{
+ m_curve->addKeyframe(WebCore::CCFloatKeyframe::create(keyframe.time, keyframe.value, WebCore::CCCubicBezierTimingFunction::create(x1, y1, x2, y2)));
+}
+
+float WebFloatAnimationCurveImpl::getValue(double time) const
+{
+ return m_curve->getValue(time);
+}
+
+PassOwnPtr<WebCore::CCAnimationCurve> WebFloatAnimationCurveImpl::cloneToCCAnimationCurve() const
+{
+ return m_curve->clone();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebFloatAnimationCurveImpl.h b/webkit/compositor/WebFloatAnimationCurveImpl.h
new file mode 100644
index 0000000..6a05608
--- /dev/null
+++ b/webkit/compositor/WebFloatAnimationCurveImpl.h
@@ -0,0 +1,42 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebFloatAnimationCurveImpl_h
+#define WebFloatAnimationCurveImpl_h
+
+#include <public/WebFloatAnimationCurve.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+class CCAnimationCurve;
+class CCKeyframedFloatAnimationCurve;
+}
+
+namespace WebKit {
+
+class WebFloatAnimationCurveImpl : public WebFloatAnimationCurve {
+public:
+ explicit WebFloatAnimationCurveImpl(PassOwnPtr<WebCore::CCKeyframedFloatAnimationCurve>);
+ virtual ~WebFloatAnimationCurveImpl();
+
+ // WebAnimationCurve implementation.
+ virtual AnimationCurveType type() const OVERRIDE;
+
+ // WebFloatAnimationCurve implementation.
+ virtual void add(const WebFloatKeyframe&) OVERRIDE;
+ virtual void add(const WebFloatKeyframe&, TimingFunctionType) OVERRIDE;
+ virtual void add(const WebFloatKeyframe&, double x1, double y1, double x2, double y2) OVERRIDE;
+
+ virtual float getValue(double time) const OVERRIDE;
+
+ PassOwnPtr<WebCore::CCAnimationCurve> cloneToCCAnimationCurve() const;
+
+private:
+ OwnPtr<WebCore::CCKeyframedFloatAnimationCurve> m_curve;
+};
+
+}
+
+#endif // WebFloatAnimationCurveImpl_h
diff --git a/webkit/compositor/WebIOSurfaceLayerImpl.cpp b/webkit/compositor/WebIOSurfaceLayerImpl.cpp
new file mode 100644
index 0000000..1ce29ef
--- /dev/null
+++ b/webkit/compositor/WebIOSurfaceLayerImpl.cpp
@@ -0,0 +1,41 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebIOSurfaceLayerImpl.h"
+
+#include "IOSurfaceLayerChromium.h"
+#include "WebLayerImpl.h"
+
+using WebCore::IOSurfaceLayerChromium;
+
+namespace WebKit {
+
+WebIOSurfaceLayer* WebIOSurfaceLayer::create()
+{
+ RefPtr<IOSurfaceLayerChromium> layer = IOSurfaceLayerChromium::create();
+ layer->setIsDrawable(true);
+ return new WebIOSurfaceLayerImpl(layer.release());
+}
+
+WebIOSurfaceLayerImpl::WebIOSurfaceLayerImpl(PassRefPtr<IOSurfaceLayerChromium> layer)
+ : m_layer(adoptPtr(new WebLayerImpl(layer)))
+{
+}
+
+WebIOSurfaceLayerImpl::~WebIOSurfaceLayerImpl()
+{
+}
+
+void WebIOSurfaceLayerImpl::setIOSurfaceProperties(unsigned ioSurfaceId, WebSize size)
+{
+ static_cast<IOSurfaceLayerChromium*>(m_layer->layer())->setIOSurfaceProperties(ioSurfaceId, size);
+}
+
+WebLayer* WebIOSurfaceLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebIOSurfaceLayerImpl.h b/webkit/compositor/WebIOSurfaceLayerImpl.h
new file mode 100644
index 0000000..c7dc3d7
--- /dev/null
+++ b/webkit/compositor/WebIOSurfaceLayerImpl.h
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebIOSurfaceLayerImpl_h
+#define WebIOSurfaceLayerImpl_h
+
+#include <public/WebIOSurfaceLayer.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+class IOSurfaceLayerChromium;
+}
+
+namespace WebKit {
+
+class WebIOSurfaceLayerImpl : public WebIOSurfaceLayer {
+public:
+ explicit WebIOSurfaceLayerImpl(PassRefPtr<WebCore::IOSurfaceLayerChromium>);
+ virtual ~WebIOSurfaceLayerImpl();
+
+ // WebIOSurfaceLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual void setIOSurfaceProperties(unsigned ioSurfaceId, WebSize) OVERRIDE;
+
+private:
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+}
+
+#endif // WebIOSurfaceLayerImpl_h
+
diff --git a/webkit/compositor/WebImageLayerImpl.cpp b/webkit/compositor/WebImageLayerImpl.cpp
new file mode 100644
index 0000000..e77b972
--- /dev/null
+++ b/webkit/compositor/WebImageLayerImpl.cpp
@@ -0,0 +1,39 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebImageLayerImpl.h"
+
+#include "ImageLayerChromium.h"
+#include "WebLayerImpl.h"
+
+using WebCore::ImageLayerChromium;
+
+namespace WebKit {
+
+WebImageLayer* WebImageLayer::create()
+{
+ return new WebImageLayerImpl(WebCore::ImageLayerChromium::create());
+}
+
+WebImageLayerImpl::WebImageLayerImpl(PassRefPtr<WebCore::ImageLayerChromium> layer)
+ : m_layer(adoptPtr(new WebLayerImpl(layer)))
+{
+}
+
+WebImageLayerImpl::~WebImageLayerImpl()
+{
+}
+
+WebLayer* WebImageLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+void WebImageLayerImpl::setBitmap(SkBitmap bitmap)
+{
+ static_cast<ImageLayerChromium*>(m_layer->layer())->setBitmap(bitmap);
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebImageLayerImpl.h b/webkit/compositor/WebImageLayerImpl.h
new file mode 100644
index 0000000..3c16b0e
--- /dev/null
+++ b/webkit/compositor/WebImageLayerImpl.h
@@ -0,0 +1,33 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebImageLayerImpl_h
+#define WebImageLayerImpl_h
+
+#include <public/WebImageLayer.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+class ImageLayerChromium;
+}
+
+namespace WebKit {
+class WebLayerImpl;
+
+class WebImageLayerImpl : public WebImageLayer {
+public:
+ explicit WebImageLayerImpl(PassRefPtr<WebCore::ImageLayerChromium>);
+ virtual ~WebImageLayerImpl();
+
+ // WebImageLayer implementation.
+ WebLayer* layer() OVERRIDE;
+ virtual void setBitmap(SkBitmap) OVERRIDE;
+
+private:
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+}
+
+#endif // WebImageLayerImpl_h
diff --git a/webkit/compositor/WebLayerImpl.cpp b/webkit/compositor/WebLayerImpl.cpp
new file mode 100644
index 0000000..0c913f9
--- /dev/null
+++ b/webkit/compositor/WebLayerImpl.cpp
@@ -0,0 +1,372 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebLayerImpl.h"
+
+#include "CCActiveAnimation.h"
+#include "LayerChromium.h"
+#include "SkMatrix44.h"
+#include "WebAnimationImpl.h"
+#include <public/WebFloatPoint.h>
+#include <public/WebFloatRect.h>
+#include <public/WebSize.h>
+#include <public/WebTransformationMatrix.h>
+
+using WebCore::CCActiveAnimation;
+using WebCore::LayerChromium;
+
+namespace WebKit {
+
+namespace {
+
+WebTransformationMatrix transformationMatrixFromSkMatrix44(const SkMatrix44& matrix)
+{
+ double data[16];
+ matrix.asColMajord(data);
+ return WebTransformationMatrix(data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7],
+ data[8], data[9], data[10], data[11],
+ data[12], data[13], data[14], data[15]);
+}
+
+SkMatrix44 skMatrix44FromTransformationMatrix(const WebTransformationMatrix& matrix)
+{
+ SkMatrix44 skMatrix;
+ skMatrix.set(0, 0, SkDoubleToMScalar(matrix.m11()));
+ skMatrix.set(1, 0, SkDoubleToMScalar(matrix.m12()));
+ skMatrix.set(2, 0, SkDoubleToMScalar(matrix.m13()));
+ skMatrix.set(3, 0, SkDoubleToMScalar(matrix.m14()));
+ skMatrix.set(0, 1, SkDoubleToMScalar(matrix.m21()));
+ skMatrix.set(1, 1, SkDoubleToMScalar(matrix.m22()));
+ skMatrix.set(2, 1, SkDoubleToMScalar(matrix.m23()));
+ skMatrix.set(3, 1, SkDoubleToMScalar(matrix.m24()));
+ skMatrix.set(0, 2, SkDoubleToMScalar(matrix.m31()));
+ skMatrix.set(1, 2, SkDoubleToMScalar(matrix.m32()));
+ skMatrix.set(2, 2, SkDoubleToMScalar(matrix.m33()));
+ skMatrix.set(3, 2, SkDoubleToMScalar(matrix.m34()));
+ skMatrix.set(0, 3, SkDoubleToMScalar(matrix.m41()));
+ skMatrix.set(1, 3, SkDoubleToMScalar(matrix.m42()));
+ skMatrix.set(2, 3, SkDoubleToMScalar(matrix.m43()));
+ skMatrix.set(3, 3, SkDoubleToMScalar(matrix.m44()));
+ return skMatrix;
+}
+
+} // anonymous namespace
+
+
+WebLayer* WebLayer::create()
+{
+ return new WebLayerImpl(LayerChromium::create());
+}
+
+WebLayerImpl::WebLayerImpl(PassRefPtr<LayerChromium> layer)
+ : m_layer(layer)
+{
+}
+
+WebLayerImpl::~WebLayerImpl()
+{
+ m_layer->clearRenderSurface();
+ m_layer->setLayerAnimationDelegate(0);
+}
+
+int WebLayerImpl::id() const
+{
+ return m_layer->id();
+}
+
+void WebLayerImpl::invalidateRect(const WebFloatRect& rect)
+{
+ m_layer->setNeedsDisplayRect(rect);
+}
+
+void WebLayerImpl::invalidate()
+{
+ m_layer->setNeedsDisplay();
+}
+
+void WebLayerImpl::addChild(WebLayer* child)
+{
+ m_layer->addChild(static_cast<WebLayerImpl*>(child)->layer());
+}
+
+void WebLayerImpl::insertChild(WebLayer* child, size_t index)
+{
+ m_layer->insertChild(static_cast<WebLayerImpl*>(child)->layer(), index);
+}
+
+void WebLayerImpl::replaceChild(WebLayer* reference, WebLayer* newLayer)
+{
+ m_layer->replaceChild(static_cast<WebLayerImpl*>(reference)->layer(), static_cast<WebLayerImpl*>(newLayer)->layer());
+}
+
+void WebLayerImpl::removeFromParent()
+{
+ m_layer->removeFromParent();
+}
+
+void WebLayerImpl::removeAllChildren()
+{
+ m_layer->removeAllChildren();
+}
+
+void WebLayerImpl::setAnchorPoint(const WebFloatPoint& anchorPoint)
+{
+ m_layer->setAnchorPoint(anchorPoint);
+}
+
+WebFloatPoint WebLayerImpl::anchorPoint() const
+{
+ return WebFloatPoint(m_layer->anchorPoint());
+}
+
+void WebLayerImpl::setAnchorPointZ(float anchorPointZ)
+{
+ m_layer->setAnchorPointZ(anchorPointZ);
+}
+
+float WebLayerImpl::anchorPointZ() const
+{
+ return m_layer->anchorPointZ();
+}
+
+void WebLayerImpl::setBounds(const WebSize& size)
+{
+ m_layer->setBounds(size);
+}
+
+WebSize WebLayerImpl::bounds() const
+{
+ return WebSize(m_layer->bounds());
+}
+
+void WebLayerImpl::setMasksToBounds(bool masksToBounds)
+{
+ m_layer->setMasksToBounds(masksToBounds);
+}
+
+bool WebLayerImpl::masksToBounds() const
+{
+ return m_layer->masksToBounds();
+}
+
+void WebLayerImpl::setMaskLayer(WebLayer* maskLayer)
+{
+ m_layer->setMaskLayer(maskLayer ? static_cast<WebLayerImpl*>(maskLayer)->layer() : 0);
+}
+
+void WebLayerImpl::setReplicaLayer(WebLayer* replicaLayer)
+{
+ m_layer->setReplicaLayer(replicaLayer ? static_cast<WebLayerImpl*>(replicaLayer)->layer() : 0);
+}
+
+void WebLayerImpl::setOpacity(float opacity)
+{
+ m_layer->setOpacity(opacity);
+}
+
+float WebLayerImpl::opacity() const
+{
+ return m_layer->opacity();
+}
+
+void WebLayerImpl::setOpaque(bool opaque)
+{
+ m_layer->setOpaque(opaque);
+}
+
+bool WebLayerImpl::opaque() const
+{
+ return m_layer->opaque();
+}
+
+void WebLayerImpl::setPosition(const WebFloatPoint& position)
+{
+ m_layer->setPosition(position);
+}
+
+WebFloatPoint WebLayerImpl::position() const
+{
+ return WebFloatPoint(m_layer->position());
+}
+
+void WebLayerImpl::setSublayerTransform(const SkMatrix44& matrix)
+{
+ m_layer->setSublayerTransform(transformationMatrixFromSkMatrix44(matrix));
+}
+
+void WebLayerImpl::setSublayerTransform(const WebTransformationMatrix& matrix)
+{
+ m_layer->setSublayerTransform(matrix);
+}
+
+SkMatrix44 WebLayerImpl::sublayerTransform() const
+{
+ return skMatrix44FromTransformationMatrix(m_layer->sublayerTransform());
+}
+
+void WebLayerImpl::setTransform(const SkMatrix44& matrix)
+{
+ m_layer->setTransform(transformationMatrixFromSkMatrix44(matrix));
+}
+
+void WebLayerImpl::setTransform(const WebTransformationMatrix& matrix)
+{
+ m_layer->setTransform(matrix);
+}
+
+SkMatrix44 WebLayerImpl::transform() const
+{
+ return skMatrix44FromTransformationMatrix(m_layer->transform());
+}
+
+void WebLayerImpl::setDrawsContent(bool drawsContent)
+{
+ m_layer->setIsDrawable(drawsContent);
+}
+
+bool WebLayerImpl::drawsContent() const
+{
+ return m_layer->drawsContent();
+}
+
+void WebLayerImpl::setPreserves3D(bool preserve3D)
+{
+ m_layer->setPreserves3D(preserve3D);
+}
+
+void WebLayerImpl::setUseParentBackfaceVisibility(bool useParentBackfaceVisibility)
+{
+ m_layer->setUseParentBackfaceVisibility(useParentBackfaceVisibility);
+}
+
+void WebLayerImpl::setBackgroundColor(WebColor color)
+{
+ m_layer->setBackgroundColor(color);
+}
+
+void WebLayerImpl::setFilters(const WebFilterOperations& filters)
+{
+ m_layer->setFilters(filters);
+}
+
+void WebLayerImpl::setBackgroundFilters(const WebFilterOperations& filters)
+{
+ m_layer->setBackgroundFilters(filters);
+}
+
+void WebLayerImpl::setDebugBorderColor(const WebColor& color)
+{
+ m_layer->setDebugBorderColor(color);
+}
+
+void WebLayerImpl::setDebugBorderWidth(float width)
+{
+ m_layer->setDebugBorderWidth(width);
+}
+
+void WebLayerImpl::setDebugName(WebString name)
+{
+ m_layer->setDebugName(name);
+}
+
+void WebLayerImpl::setAnimationDelegate(WebAnimationDelegate* delegate)
+{
+ m_layer->setLayerAnimationDelegate(delegate);
+}
+
+bool WebLayerImpl::addAnimation(WebAnimation* animation)
+{
+ return m_layer->addAnimation(static_cast<WebAnimationImpl*>(animation)->cloneToCCAnimation());
+}
+
+void WebLayerImpl::removeAnimation(int animationId)
+{
+ m_layer->removeAnimation(animationId);
+}
+
+void WebLayerImpl::removeAnimation(int animationId, WebAnimation::TargetProperty targetProperty)
+{
+ m_layer->layerAnimationController()->removeAnimation(animationId, static_cast<CCActiveAnimation::TargetProperty>(targetProperty));
+}
+
+void WebLayerImpl::pauseAnimation(int animationId, double timeOffset)
+{
+ m_layer->pauseAnimation(animationId, timeOffset);
+}
+
+void WebLayerImpl::suspendAnimations(double monotonicTime)
+{
+ m_layer->suspendAnimations(monotonicTime);
+}
+
+void WebLayerImpl::resumeAnimations(double monotonicTime)
+{
+ m_layer->resumeAnimations(monotonicTime);
+}
+
+bool WebLayerImpl::hasActiveAnimation()
+{
+ return m_layer->hasActiveAnimation();
+}
+
+void WebLayerImpl::transferAnimationsTo(WebLayer* other)
+{
+ ASSERT(other);
+ static_cast<WebLayerImpl*>(other)->m_layer->setLayerAnimationController(m_layer->releaseLayerAnimationController());
+}
+
+void WebLayerImpl::setForceRenderSurface(bool forceRenderSurface)
+{
+ m_layer->setForceRenderSurface(forceRenderSurface);
+}
+
+void WebLayerImpl::setScrollPosition(WebPoint position)
+{
+ m_layer->setScrollPosition(position);
+}
+
+void WebLayerImpl::setScrollable(bool scrollable)
+{
+ m_layer->setScrollable(scrollable);
+}
+
+void WebLayerImpl::setHaveWheelEventHandlers(bool haveWheelEventHandlers)
+{
+ m_layer->setHaveWheelEventHandlers(haveWheelEventHandlers);
+}
+
+void WebLayerImpl::setShouldScrollOnMainThread(bool shouldScrollOnMainThread)
+{
+ m_layer->setShouldScrollOnMainThread(shouldScrollOnMainThread);
+}
+
+void WebLayerImpl::setNonFastScrollableRegion(const WebVector<WebRect>& rects)
+{
+ WebCore::Region region;
+ for (size_t i = 0; i < rects.size(); ++i) {
+ WebCore::IntRect rect = rects[i];
+ region.unite(rect);
+ }
+ m_layer->setNonFastScrollableRegion(region);
+
+}
+
+void WebLayerImpl::setIsContainerForFixedPositionLayers(bool enable)
+{
+ m_layer->setIsContainerForFixedPositionLayers(enable);
+}
+
+void WebLayerImpl::setFixedToContainerLayer(bool enable)
+{
+ m_layer->setFixedToContainerLayer(enable);
+}
+
+LayerChromium* WebLayerImpl::layer() const
+{
+ return m_layer.get();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebLayerImpl.h b/webkit/compositor/WebLayerImpl.h
new file mode 100644
index 0000000..b499a30
--- /dev/null
+++ b/webkit/compositor/WebLayerImpl.h
@@ -0,0 +1,89 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebLayerImpl_h
+#define WebLayerImpl_h
+
+#include <public/WebLayer.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+class LayerChromium;
+}
+
+namespace WebKit {
+
+class WebLayerImpl : public WebLayer {
+public:
+ explicit WebLayerImpl(PassRefPtr<WebCore::LayerChromium>);
+ virtual ~WebLayerImpl();
+
+ // WebLayer implementation.
+ virtual int id() const OVERRIDE;
+ virtual void invalidateRect(const WebFloatRect&) OVERRIDE;
+ virtual void invalidate() OVERRIDE;
+ virtual void addChild(WebLayer*) OVERRIDE;
+ virtual void insertChild(WebLayer*, size_t index) OVERRIDE;
+ virtual void replaceChild(WebLayer* reference, WebLayer* newLayer) OVERRIDE;
+ virtual void removeFromParent() OVERRIDE;
+ virtual void removeAllChildren() OVERRIDE;
+ virtual void setAnchorPoint(const WebFloatPoint&) OVERRIDE;
+ virtual WebFloatPoint anchorPoint() const OVERRIDE;
+ virtual void setAnchorPointZ(float) OVERRIDE;
+ virtual float anchorPointZ() const OVERRIDE;
+ virtual void setBounds(const WebSize&) OVERRIDE;
+ virtual WebSize bounds() const OVERRIDE;
+ virtual void setMasksToBounds(bool) OVERRIDE;
+ virtual bool masksToBounds() const OVERRIDE;
+ virtual void setMaskLayer(WebLayer*) OVERRIDE;
+ virtual void setReplicaLayer(WebLayer*) OVERRIDE;
+ virtual void setOpacity(float) OVERRIDE;
+ virtual float opacity() const OVERRIDE;
+ virtual void setOpaque(bool) OVERRIDE;
+ virtual bool opaque() const OVERRIDE;
+ virtual void setPosition(const WebFloatPoint&) OVERRIDE;
+ virtual WebFloatPoint position() const OVERRIDE;
+ virtual void setSublayerTransform(const SkMatrix44&) OVERRIDE;
+ virtual void setSublayerTransform(const WebTransformationMatrix&) OVERRIDE;
+ virtual SkMatrix44 sublayerTransform() const OVERRIDE;
+ virtual void setTransform(const SkMatrix44&) OVERRIDE;
+ virtual void setTransform(const WebTransformationMatrix&) OVERRIDE;
+ virtual SkMatrix44 transform() const OVERRIDE;
+ virtual void setDrawsContent(bool) OVERRIDE;
+ virtual bool drawsContent() const OVERRIDE;
+ virtual void setPreserves3D(bool) OVERRIDE;
+ virtual void setUseParentBackfaceVisibility(bool) OVERRIDE;
+ virtual void setBackgroundColor(WebColor) OVERRIDE;
+ virtual void setFilters(const WebFilterOperations&) OVERRIDE;
+ virtual void setBackgroundFilters(const WebFilterOperations&) OVERRIDE;
+ virtual void setDebugBorderColor(const WebColor&) OVERRIDE;
+ virtual void setDebugBorderWidth(float) OVERRIDE;
+ virtual void setDebugName(WebString) OVERRIDE;
+ virtual void setAnimationDelegate(WebAnimationDelegate*) OVERRIDE;
+ virtual bool addAnimation(WebAnimation*) OVERRIDE;
+ virtual void removeAnimation(int animationId) OVERRIDE;
+ virtual void removeAnimation(int animationId, WebAnimation::TargetProperty) OVERRIDE;
+ virtual void pauseAnimation(int animationId, double timeOffset) OVERRIDE;
+ virtual void suspendAnimations(double monotonicTime) OVERRIDE;
+ virtual void resumeAnimations(double monotonicTime) OVERRIDE;
+ virtual bool hasActiveAnimation() OVERRIDE;
+ virtual void transferAnimationsTo(WebLayer*) OVERRIDE;
+ virtual void setForceRenderSurface(bool) OVERRIDE;
+ virtual void setScrollPosition(WebPoint) OVERRIDE;
+ virtual void setScrollable(bool) OVERRIDE;
+ virtual void setHaveWheelEventHandlers(bool) OVERRIDE;
+ virtual void setShouldScrollOnMainThread(bool) OVERRIDE;
+ virtual void setNonFastScrollableRegion(const WebVector<WebRect>&) OVERRIDE;
+ virtual void setIsContainerForFixedPositionLayers(bool) OVERRIDE;
+ virtual void setFixedToContainerLayer(bool) OVERRIDE;
+
+ WebCore::LayerChromium* layer() const;
+
+protected:
+ RefPtr<WebCore::LayerChromium> m_layer;
+};
+
+} // namespace WebKit
+
+#endif // WebLayerImpl_h
diff --git a/webkit/compositor/WebLayerTreeViewImpl.cpp b/webkit/compositor/WebLayerTreeViewImpl.cpp
new file mode 100644
index 0000000..7bfcff1
--- /dev/null
+++ b/webkit/compositor/WebLayerTreeViewImpl.cpp
@@ -0,0 +1,255 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebLayerTreeViewImpl.h"
+
+#include "CCFontAtlas.h"
+#include "CCLayerTreeHost.h"
+#include "LayerChromium.h"
+#include "WebLayerImpl.h"
+#include <public/WebGraphicsContext3D.h>
+#include <public/WebLayer.h>
+#include <public/WebLayerTreeView.h>
+#include <public/WebLayerTreeViewClient.h>
+#include <public/WebRenderingStats.h>
+#include <public/WebSize.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+WebLayerTreeView* WebLayerTreeView::create(WebLayerTreeViewClient* client, const WebLayer& root, const WebLayerTreeView::Settings& settings)
+{
+ OwnPtr<WebLayerTreeViewImpl> layerTreeViewImpl = adoptPtr(new WebLayerTreeViewImpl(client));
+ if (!layerTreeViewImpl->initialize(settings))
+ return 0;
+ layerTreeViewImpl->setRootLayer(root);
+ return layerTreeViewImpl.leakPtr();
+}
+
+WebLayerTreeViewImpl::WebLayerTreeViewImpl(WebLayerTreeViewClient* client)
+ : m_client(client)
+{
+}
+
+WebLayerTreeViewImpl::~WebLayerTreeViewImpl()
+{
+}
+
+bool WebLayerTreeViewImpl::initialize(const WebLayerTreeView::Settings& webSettings)
+{
+ CCLayerTreeSettings settings;
+ settings.acceleratePainting = webSettings.acceleratePainting;
+ settings.showFPSCounter = webSettings.showFPSCounter;
+ settings.showPlatformLayerTree = webSettings.showPlatformLayerTree;
+ settings.showPaintRects = webSettings.showPaintRects;
+ settings.renderVSyncEnabled = webSettings.renderVSyncEnabled;
+ settings.refreshRate = webSettings.refreshRate;
+ settings.defaultTileSize = webSettings.defaultTileSize;
+ settings.maxUntiledLayerSize = webSettings.maxUntiledLayerSize;
+ m_layerTreeHost = CCLayerTreeHost::create(this, settings);
+ if (!m_layerTreeHost)
+ return false;
+ return true;
+}
+
+void WebLayerTreeViewImpl::setSurfaceReady()
+{
+ m_layerTreeHost->setSurfaceReady();
+}
+
+void WebLayerTreeViewImpl::setRootLayer(const WebLayer& root)
+{
+ m_layerTreeHost->setRootLayer(static_cast<const WebLayerImpl*>(&root)->layer());
+}
+
+void WebLayerTreeViewImpl::clearRootLayer()
+{
+ m_layerTreeHost->setRootLayer(PassRefPtr<LayerChromium>());
+}
+
+int WebLayerTreeViewImpl::compositorIdentifier()
+{
+ return m_layerTreeHost->compositorIdentifier();
+}
+
+void WebLayerTreeViewImpl::setViewportSize(const WebSize& layoutViewportSize, const WebSize& deviceViewportSize)
+{
+ if (!deviceViewportSize.isEmpty())
+ m_layerTreeHost->setViewportSize(layoutViewportSize, deviceViewportSize);
+ else
+ m_layerTreeHost->setViewportSize(layoutViewportSize, layoutViewportSize);
+}
+
+WebSize WebLayerTreeViewImpl::layoutViewportSize() const
+{
+ return WebSize(m_layerTreeHost->layoutViewportSize());
+}
+
+WebSize WebLayerTreeViewImpl::deviceViewportSize() const
+{
+ return WebSize(m_layerTreeHost->deviceViewportSize());
+}
+
+void WebLayerTreeViewImpl::setDeviceScaleFactor(const float deviceScaleFactor)
+{
+ m_layerTreeHost->setDeviceScaleFactor(deviceScaleFactor);
+}
+
+float WebLayerTreeViewImpl::deviceScaleFactor() const
+{
+ return m_layerTreeHost->deviceScaleFactor();
+}
+
+void WebLayerTreeViewImpl::setBackgroundColor(WebColor color)
+{
+ m_layerTreeHost->setBackgroundColor(color);
+}
+
+void WebLayerTreeViewImpl::setHasTransparentBackground(bool transparent)
+{
+ m_layerTreeHost->setHasTransparentBackground(transparent);
+}
+
+void WebLayerTreeViewImpl::setVisible(bool visible)
+{
+ m_layerTreeHost->setVisible(visible);
+}
+
+void WebLayerTreeViewImpl::setPageScaleFactorAndLimits(float pageScaleFactor, float minimum, float maximum)
+{
+ m_layerTreeHost->setPageScaleFactorAndLimits(pageScaleFactor, minimum, maximum);
+}
+
+void WebLayerTreeViewImpl::startPageScaleAnimation(const WebPoint& scroll, bool useAnchor, float newPageScale, double durationSec)
+{
+ m_layerTreeHost->startPageScaleAnimation(IntSize(scroll.x, scroll.y), useAnchor, newPageScale, durationSec);
+}
+
+void WebLayerTreeViewImpl::setNeedsAnimate()
+{
+ m_layerTreeHost->setNeedsAnimate();
+}
+
+void WebLayerTreeViewImpl::setNeedsRedraw()
+{
+ m_layerTreeHost->setNeedsRedraw();
+}
+
+bool WebLayerTreeViewImpl::commitRequested() const
+{
+ return m_layerTreeHost->commitRequested();
+}
+
+void WebLayerTreeViewImpl::composite()
+{
+ if (CCProxy::hasImplThread())
+ m_layerTreeHost->setNeedsCommit();
+ else
+ m_layerTreeHost->composite();
+}
+
+void WebLayerTreeViewImpl::updateAnimations(double frameBeginTime)
+{
+ m_layerTreeHost->updateAnimations(frameBeginTime);
+}
+
+bool WebLayerTreeViewImpl::compositeAndReadback(void *pixels, const WebRect& rect)
+{
+ return m_layerTreeHost->compositeAndReadback(pixels, rect);
+}
+
+void WebLayerTreeViewImpl::finishAllRendering()
+{
+ m_layerTreeHost->finishAllRendering();
+}
+
+void WebLayerTreeViewImpl::renderingStats(WebRenderingStats& stats) const
+{
+ CCRenderingStats ccStats;
+ m_layerTreeHost->renderingStats(ccStats);
+
+ stats.numAnimationFrames = ccStats.numAnimationFrames;
+ stats.numFramesSentToScreen = ccStats.numFramesSentToScreen;
+ stats.droppedFrameCount = ccStats.droppedFrameCount;
+ stats.totalPaintTimeInSeconds = ccStats.totalPaintTimeInSeconds;
+ stats.totalRasterizeTimeInSeconds = ccStats.totalRasterizeTimeInSeconds;
+}
+
+void WebLayerTreeViewImpl::setFontAtlas(SkBitmap bitmap, WebRect asciiToWebRectTable[128], int fontHeight)
+{
+ IntRect asciiToRectTable[128];
+ for (int i = 0; i < 128; ++i)
+ asciiToRectTable[i] = asciiToWebRectTable[i];
+ OwnPtr<CCFontAtlas> fontAtlas = CCFontAtlas::create(bitmap, asciiToRectTable, fontHeight);
+ m_layerTreeHost->setFontAtlas(fontAtlas.release());
+}
+
+void WebLayerTreeViewImpl::loseCompositorContext(int numTimes)
+{
+ m_layerTreeHost->loseContext(numTimes);
+}
+
+void WebLayerTreeViewImpl::willBeginFrame()
+{
+ m_client->willBeginFrame();
+}
+
+void WebLayerTreeViewImpl::didBeginFrame()
+{
+ m_client->didBeginFrame();
+}
+
+void WebLayerTreeViewImpl::animate(double monotonicFrameBeginTime)
+{
+ m_client->updateAnimations(monotonicFrameBeginTime);
+}
+
+void WebLayerTreeViewImpl::layout()
+{
+ m_client->layout();
+}
+
+void WebLayerTreeViewImpl::applyScrollAndScale(const WebCore::IntSize& scrollDelta, float pageScale)
+{
+ m_client->applyScrollAndScale(scrollDelta, pageScale);
+}
+
+PassOwnPtr<WebCompositorOutputSurface> WebLayerTreeViewImpl::createOutputSurface()
+{
+ return adoptPtr(m_client->createOutputSurface());
+}
+
+void WebLayerTreeViewImpl::didRecreateOutputSurface(bool success)
+{
+ m_client->didRecreateOutputSurface(success);
+}
+
+void WebLayerTreeViewImpl::willCommit()
+{
+ m_client->willCommit();
+}
+
+void WebLayerTreeViewImpl::didCommit()
+{
+ m_client->didCommit();
+}
+
+void WebLayerTreeViewImpl::didCommitAndDrawFrame()
+{
+ m_client->didCommitAndDrawFrame();
+}
+
+void WebLayerTreeViewImpl::didCompleteSwapBuffers()
+{
+ m_client->didCompleteSwapBuffers();
+}
+
+void WebLayerTreeViewImpl::scheduleComposite()
+{
+ m_client->scheduleComposite();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebLayerTreeViewImpl.h b/webkit/compositor/WebLayerTreeViewImpl.h
new file mode 100644
index 0000000..ac13759
--- /dev/null
+++ b/webkit/compositor/WebLayerTreeViewImpl.h
@@ -0,0 +1,72 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebLayerTreeViewImpl_h
+#define WebLayerTreeViewImpl_h
+
+#include "CCLayerTreeHost.h"
+#include <public/WebLayerTreeView.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+class WebLayer;
+class WebLayerTreeViewClient;
+class WebLayerTreeViewClientAdapter;
+
+class WebLayerTreeViewImpl : public WebLayerTreeView, public WebCore::CCLayerTreeHostClient {
+public:
+ explicit WebLayerTreeViewImpl(WebLayerTreeViewClient*);
+ virtual ~WebLayerTreeViewImpl();
+
+ bool initialize(const Settings&);
+
+ // WebLayerTreeView implementation.
+ virtual void setSurfaceReady() OVERRIDE;
+ virtual void setRootLayer(const WebLayer&) OVERRIDE;
+ virtual void clearRootLayer() OVERRIDE;
+ virtual int compositorIdentifier() OVERRIDE;
+ virtual void setViewportSize(const WebSize& layoutViewportSize, const WebSize& deviceViewportSize = WebSize()) OVERRIDE;
+ virtual WebSize layoutViewportSize() const OVERRIDE;
+ virtual WebSize deviceViewportSize() const OVERRIDE;
+ virtual void setDeviceScaleFactor(float) OVERRIDE;
+ virtual float deviceScaleFactor() const OVERRIDE;
+ virtual void setBackgroundColor(WebColor) OVERRIDE;
+ virtual void setHasTransparentBackground(bool) OVERRIDE;
+ virtual void setVisible(bool) OVERRIDE;
+ virtual void setPageScaleFactorAndLimits(float pageScaleFactor, float minimum, float maximum) OVERRIDE;
+ virtual void startPageScaleAnimation(const WebPoint& destination, bool useAnchor, float newPageScale, double durationSec) OVERRIDE;
+ virtual void setNeedsAnimate() OVERRIDE;
+ virtual void setNeedsRedraw() OVERRIDE;
+ virtual bool commitRequested() const OVERRIDE;
+ virtual void composite() OVERRIDE;
+ virtual void updateAnimations(double frameBeginTime) OVERRIDE;
+ virtual bool compositeAndReadback(void *pixels, const WebRect&) OVERRIDE;
+ virtual void finishAllRendering() OVERRIDE;
+ virtual void renderingStats(WebRenderingStats&) const OVERRIDE;
+ virtual void setFontAtlas(SkBitmap, WebRect asciiToRectTable[128], int fontHeight) OVERRIDE;
+ virtual void loseCompositorContext(int numTimes) OVERRIDE;
+
+ // WebCore::CCLayerTreeHostClient implementation.
+ virtual void willBeginFrame() OVERRIDE;
+ virtual void didBeginFrame() OVERRIDE;
+ virtual void animate(double monotonicFrameBeginTime) OVERRIDE;
+ virtual void layout() OVERRIDE;
+ virtual void applyScrollAndScale(const WebCore::IntSize& scrollDelta, float pageScale) OVERRIDE;
+ virtual PassOwnPtr<WebCompositorOutputSurface> createOutputSurface() OVERRIDE;
+ virtual void didRecreateOutputSurface(bool success) OVERRIDE;
+ virtual void willCommit() OVERRIDE;
+ virtual void didCommit() OVERRIDE;
+ virtual void didCommitAndDrawFrame() OVERRIDE;
+ virtual void didCompleteSwapBuffers() OVERRIDE;
+ virtual void scheduleComposite() OVERRIDE;
+
+private:
+ WebLayerTreeViewClient* m_client;
+ OwnPtr<WebCore::CCLayerTreeHost> m_layerTreeHost;
+};
+
+} // namespace WebKit
+
+#endif // WebLayerTreeViewImpl_h
diff --git a/webkit/compositor/WebScrollbarLayerImpl.cpp b/webkit/compositor/WebScrollbarLayerImpl.cpp
new file mode 100644
index 0000000..c4f2071
--- /dev/null
+++ b/webkit/compositor/WebScrollbarLayerImpl.cpp
@@ -0,0 +1,44 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebScrollbarLayerImpl.h"
+
+#include "ScrollbarLayerChromium.h"
+#include "WebLayerImpl.h"
+
+using WebCore::Scrollbar;
+using WebCore::ScrollbarLayerChromium;
+
+namespace WebKit {
+
+WebScrollbarLayer* WebScrollbarLayer::create(WebCore::Scrollbar* scrollbar, WebScrollbarThemePainter painter, PassOwnPtr<WebScrollbarThemeGeometry> geometry)
+{
+ return new WebScrollbarLayerImpl(ScrollbarLayerChromium::create(WebScrollbar::create(scrollbar), painter, geometry, 0));
+}
+
+
+WebScrollbarLayerImpl::WebScrollbarLayerImpl(PassRefPtr<WebCore::ScrollbarLayerChromium> layer)
+ : m_layer(adoptPtr(new WebLayerImpl(layer)))
+{
+}
+
+WebScrollbarLayerImpl::~WebScrollbarLayerImpl()
+{
+}
+
+WebLayer* WebScrollbarLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+void WebScrollbarLayerImpl::setScrollLayer(WebLayer* layer)
+{
+ int id = layer ? static_cast<WebLayerImpl*>(layer)->layer()->id() : 0;
+ static_cast<ScrollbarLayerChromium*>(m_layer->layer())->setScrollLayerId(id);
+}
+
+
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebScrollbarLayerImpl.h b/webkit/compositor/WebScrollbarLayerImpl.h
new file mode 100644
index 0000000..3af38f2
--- /dev/null
+++ b/webkit/compositor/WebScrollbarLayerImpl.h
@@ -0,0 +1,34 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebScrollbarLayerImpl_h
+#define WebScrollbarLayerImpl_h
+
+#include <public/WebScrollbarLayer.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+class ScrollbarLayerChromium;
+}
+
+namespace WebKit {
+class WebLayerImpl;
+
+class WebScrollbarLayerImpl : public WebScrollbarLayer {
+public:
+ explicit WebScrollbarLayerImpl(PassRefPtr<WebCore::ScrollbarLayerChromium>);
+ virtual ~WebScrollbarLayerImpl();
+
+ // WebScrollbarLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual void setScrollLayer(WebLayer*) OVERRIDE;
+
+private:
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+}
+
+#endif // WebScrollbarLayerImpl_h
diff --git a/webkit/compositor/WebSolidColorLayerImpl.cpp b/webkit/compositor/WebSolidColorLayerImpl.cpp
new file mode 100644
index 0000000..51be974
--- /dev/null
+++ b/webkit/compositor/WebSolidColorLayerImpl.cpp
@@ -0,0 +1,41 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebSolidColorLayerImpl.h"
+
+#include "SolidColorLayerChromium.h"
+#include "WebLayerImpl.h"
+
+using WebCore::SolidColorLayerChromium;
+
+namespace WebKit {
+
+WebSolidColorLayer* WebSolidColorLayer::create()
+{
+ return new WebSolidColorLayerImpl(SolidColorLayerChromium::create());
+}
+
+WebSolidColorLayerImpl::WebSolidColorLayerImpl(PassRefPtr<SolidColorLayerChromium> layer)
+ : m_layer(adoptPtr(new WebLayerImpl(layer)))
+{
+ m_layer->layer()->setIsDrawable(true);
+}
+
+WebSolidColorLayerImpl::~WebSolidColorLayerImpl()
+{
+}
+
+WebLayer* WebSolidColorLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+void WebSolidColorLayerImpl::setBackgroundColor(WebColor color)
+{
+ m_layer->setBackgroundColor(color);
+}
+
+} // namespace WebKit
+
diff --git a/webkit/compositor/WebSolidColorLayerImpl.h b/webkit/compositor/WebSolidColorLayerImpl.h
new file mode 100644
index 0000000..b4a83dc
--- /dev/null
+++ b/webkit/compositor/WebSolidColorLayerImpl.h
@@ -0,0 +1,35 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebSolidColorLayerImpl_h
+#define WebSolidColorLayerImpl_h
+
+#include <public/WebSolidColorLayer.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+class SolidColorLayerChromium;
+}
+
+namespace WebKit {
+class WebLayerImpl;
+
+class WebSolidColorLayerImpl : public WebSolidColorLayer {
+public:
+ explicit WebSolidColorLayerImpl(PassRefPtr<WebCore::SolidColorLayerChromium>);
+ virtual ~WebSolidColorLayerImpl();
+
+ // WebSolidColorLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual void setBackgroundColor(WebColor) OVERRIDE;
+
+private:
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+} // namespace WebKit
+
+#endif // WebSolidColorLayerImpl_h
+
diff --git a/webkit/compositor/WebTransformAnimationCurveImpl.cpp b/webkit/compositor/WebTransformAnimationCurveImpl.cpp
new file mode 100644
index 0000000..f1efd1f
--- /dev/null
+++ b/webkit/compositor/WebTransformAnimationCurveImpl.cpp
@@ -0,0 +1,61 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+
+#include "WebTransformAnimationCurveImpl.h"
+
+#include "CCKeyframedAnimationCurve.h"
+#include "CCTimingFunction.h"
+#include "WebAnimationCurveCommon.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebKit {
+
+WebTransformAnimationCurve* WebTransformAnimationCurve::create()
+{
+ return new WebTransformAnimationCurveImpl(WebCore::CCKeyframedTransformAnimationCurve::create());
+}
+
+WebTransformAnimationCurveImpl::WebTransformAnimationCurveImpl(PassOwnPtr<WebCore::CCKeyframedTransformAnimationCurve> curve)
+ : m_curve(curve)
+{
+}
+
+WebTransformAnimationCurveImpl::~WebTransformAnimationCurveImpl()
+{
+}
+
+WebAnimationCurve::AnimationCurveType WebTransformAnimationCurveImpl::type() const
+{
+ return WebAnimationCurve::AnimationCurveTypeTransform;
+}
+
+void WebTransformAnimationCurveImpl::add(const WebTransformKeyframe& keyframe)
+{
+ add(keyframe, TimingFunctionTypeEase);
+}
+
+void WebTransformAnimationCurveImpl::add(const WebTransformKeyframe& keyframe, TimingFunctionType type)
+{
+ m_curve->addKeyframe(WebCore::CCTransformKeyframe::create(keyframe.time, keyframe.value, createTimingFunction(type)));
+}
+
+void WebTransformAnimationCurveImpl::add(const WebTransformKeyframe& keyframe, double x1, double y1, double x2, double y2)
+{
+ m_curve->addKeyframe(WebCore::CCTransformKeyframe::create(keyframe.time, keyframe.value, WebCore::CCCubicBezierTimingFunction::create(x1, y1, x2, y2)));
+}
+
+WebTransformationMatrix WebTransformAnimationCurveImpl::getValue(double time) const
+{
+ return m_curve->getValue(time);
+}
+
+PassOwnPtr<WebCore::CCAnimationCurve> WebTransformAnimationCurveImpl::cloneToCCAnimationCurve() const
+{
+ return m_curve->clone();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebTransformAnimationCurveImpl.h b/webkit/compositor/WebTransformAnimationCurveImpl.h
new file mode 100644
index 0000000..e65e163
--- /dev/null
+++ b/webkit/compositor/WebTransformAnimationCurveImpl.h
@@ -0,0 +1,42 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebTransformAnimationCurveImpl_h
+#define WebTransformAnimationCurveImpl_h
+
+#include <public/WebTransformAnimationCurve.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+class CCAnimationCurve;
+class CCKeyframedTransformAnimationCurve;
+}
+
+namespace WebKit {
+
+class WebTransformAnimationCurveImpl : public WebTransformAnimationCurve {
+public:
+ explicit WebTransformAnimationCurveImpl(PassOwnPtr<WebCore::CCKeyframedTransformAnimationCurve>);
+ virtual ~WebTransformAnimationCurveImpl();
+
+ // WebAnimationCurve implementation.
+ virtual AnimationCurveType type() const OVERRIDE;
+
+ // WebTransformAnimationCurve implementation.
+ virtual void add(const WebTransformKeyframe&) OVERRIDE;
+ virtual void add(const WebTransformKeyframe&, TimingFunctionType) OVERRIDE;
+ virtual void add(const WebTransformKeyframe&, double x1, double y1, double x2, double y2) OVERRIDE;
+
+ virtual WebTransformationMatrix getValue(double time) const OVERRIDE;
+
+ PassOwnPtr<WebCore::CCAnimationCurve> cloneToCCAnimationCurve() const;
+
+private:
+ OwnPtr<WebCore::CCKeyframedTransformAnimationCurve> m_curve;
+};
+
+}
+
+#endif // WebTransformAnimationCurveImpl_h
diff --git a/webkit/compositor/WebVideoLayerImpl.cpp b/webkit/compositor/WebVideoLayerImpl.cpp
new file mode 100644
index 0000000..bddba88
--- /dev/null
+++ b/webkit/compositor/WebVideoLayerImpl.cpp
@@ -0,0 +1,37 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "WebVideoLayerImpl.h"
+
+#include "VideoLayerChromium.h"
+#include "WebLayerImpl.h"
+
+namespace WebKit {
+
+WebVideoLayer* WebVideoLayer::create(WebVideoFrameProvider* provider)
+{
+ return new WebVideoLayerImpl(WebCore::VideoLayerChromium::create(provider));
+}
+
+WebVideoLayerImpl::WebVideoLayerImpl(PassRefPtr<WebCore::VideoLayerChromium> layer)
+ : m_layer(adoptPtr(new WebLayerImpl(layer)))
+{
+}
+
+WebVideoLayerImpl::~WebVideoLayerImpl()
+{
+}
+
+WebLayer* WebVideoLayerImpl::layer()
+{
+ return m_layer.get();
+}
+
+bool WebVideoLayerImpl::active() const
+{
+ return m_layer->layer()->layerTreeHost();
+}
+
+} // namespace WebKit
diff --git a/webkit/compositor/WebVideoLayerImpl.h b/webkit/compositor/WebVideoLayerImpl.h
new file mode 100644
index 0000000..d8935bd5
--- /dev/null
+++ b/webkit/compositor/WebVideoLayerImpl.h
@@ -0,0 +1,33 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebVideoLayerImpl_h
+#define WebVideoLayerImpl_h
+
+#include <public/WebVideoLayer.h>
+
+namespace WebCore {
+class VideoLayerChromium;
+}
+
+namespace WebKit {
+class WebLayerImpl;
+
+class WebVideoLayerImpl : public WebVideoLayer {
+public:
+ explicit WebVideoLayerImpl(PassRefPtr<WebCore::VideoLayerChromium>);
+ virtual ~WebVideoLayerImpl();
+
+ // WebVideoLayer implementation.
+ virtual WebLayer* layer() OVERRIDE;
+ virtual bool active() const OVERRIDE;
+
+private:
+ OwnPtr<WebLayerImpl> m_layer;
+};
+
+}
+
+#endif // WebVideoLayerImpl_h
+
diff --git a/webkit/compositor/WheelFlingPlatformGestureCurve.h b/webkit/compositor/WheelFlingPlatformGestureCurve.h
new file mode 100644
index 0000000..2ff1f46
--- /dev/null
+++ b/webkit/compositor/WheelFlingPlatformGestureCurve.h
@@ -0,0 +1,38 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WheelFlingPlatformGestureCurve_h
+#define WheelFlingPlatformGestureCurve_h
+
+#include "FloatPoint.h"
+#include "PlatformGestureCurve.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class PlatformGestureCurveTarget;
+
+// Implementation of PlatformGestureCurve suitable for mouse wheel-based fling
+// scroll. A Rayleigh distribtution curve is used to define the velocity profile,
+// so velocity starts at zero, accelerates to a maximum proportional to 'velocity',
+// then gently tails off to zero again.
+class WheelFlingPlatformGestureCurve : public PlatformGestureCurve {
+public:
+ static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity);
+ virtual ~WheelFlingPlatformGestureCurve();
+
+ virtual const char* debugName() const OVERRIDE;
+ virtual bool apply(double time, PlatformGestureCurveTarget*) OVERRIDE;
+
+private:
+ explicit WheelFlingPlatformGestureCurve(const FloatPoint& velocity);
+
+ FloatPoint m_velocity;
+ IntPoint m_cumulativeScroll;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/webkit/compositor/compositor.gyp b/webkit/compositor/compositor.gyp
new file mode 100644
index 0000000..c6f4d4e
--- /dev/null
+++ b/webkit/compositor/compositor.gyp
@@ -0,0 +1,79 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 0,
+ 'webkit_compositor_sources': [
+ 'CCThreadImpl.cpp',
+ 'CCThreadImpl.h',
+ 'PlatformGestureCurve.h',
+ 'PlatformGestureCurveTarget.h',
+ 'TouchpadFlingPlatformGestureCurve.h',
+ 'WebAnimationCurveCommon.cpp',
+ 'WebAnimationCurveCommon.h',
+ 'WebAnimationImpl.cpp',
+ 'WebAnimationImpl.h',
+ 'WebCompositorImpl.cpp',
+ 'WebCompositorImpl.h',
+ 'WebCompositorInputHandlerImpl.cpp',
+ 'WebCompositorInputHandlerImpl.h',
+ 'WebContentLayerImpl.cpp',
+ 'WebContentLayerImpl.h',
+ 'WebExternalTextureLayerImpl.cpp',
+ 'WebExternalTextureLayerImpl.h',
+ 'WebFloatAnimationCurveImpl.cpp',
+ 'WebFloatAnimationCurveImpl.h',
+ 'WebIOSurfaceLayerImpl.cpp',
+ 'WebIOSurfaceLayerImpl.h',
+ 'WebImageLayerImpl.cpp',
+ 'WebImageLayerImpl.h',
+ 'WebLayerImpl.cpp',
+ 'WebLayerImpl.h',
+ 'WebLayerTreeViewImpl.cpp',
+ 'WebLayerTreeViewImpl.h',
+ 'WebScrollbarLayerImpl.cpp',
+ 'WebScrollbarLayerImpl.h',
+ 'WebSolidColorLayerImpl.cpp',
+ 'WebSolidColorLayerImpl.h',
+ 'WebTransformAnimationCurveImpl.cpp',
+ 'WebTransformAnimationCurveImpl.h',
+ 'WebVideoLayerImpl.cpp',
+ 'WebVideoLayerImpl.h',
+ 'WheelFlingPlatformGestureCurve.h',
+ ],
+ },
+ 'conditions': [
+ ['use_libcc_for_compositor==1', {
+ 'targets': [
+ {
+ 'target_name': 'webkit_compositor',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/cc/cc.gyp:cc',
+ '<(DEPTH)/skia/skia.gyp:skia',
+ '<(DEPTH)/third_party/WebKit/Source/Platform/Platform.gyp/Platform.gyp:webkit_platform',
+ # We have to depend on WTF directly to pick up the correct defines for WTF headers - for instance USE_SYSTEM_MALLOC.
+ '<(DEPTH)/third_party/WebKit/Source/WTF/WTF.gyp/WTF.gyp:wtf',
+ ],
+ 'defines': [
+ 'WEBKIT_IMPLEMENTATION=1',
+ ],
+ 'include_dirs': [
+ 'stubs',
+ '<(DEPTH)/cc',
+ '<(DEPTH)/cc/stubs',
+ '<(DEPTH)/third_party/WebKit/Source/WebKit/chromium/public',
+ ],
+ 'sources': [
+ '<@(webkit_compositor_sources)',
+ 'stubs/AnimationIdVendor.h',
+ 'stubs/public/WebTransformationMatrix',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/webkit/compositor/copyfiles.py b/webkit/compositor/copyfiles.py
new file mode 100644
index 0000000..6b94a19
--- /dev/null
+++ b/webkit/compositor/copyfiles.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import shutil
+import os
+import re
+
+prefixes = ["../../third_party/WebKit/Source/WebCore/platform/chromium/support",
+ "../../third_party/WebKit/Source/WebKit/chromium/src",
+ "../../third_party/WebKit/Source/WebCore/platform"]
+
+def Copy(name):
+ src = name
+ dst = name
+ fullsrc = ""
+ for prefix in prefixes:
+ candidate = "%s/%s" % (prefix, src)
+ if os.path.exists(candidate):
+ fullsrc = candidate
+ break
+ assert fullsrc != ""
+ shutil.copyfile(fullsrc, dst)
+ print "copying from %s to %s" % (fullsrc, dst)
+ return dst
+
+def Readfile(gypfile):
+ cc_gyp = open(gypfile, "r")
+ obj = eval(cc_gyp.read())
+ cc_gyp.close()
+ return obj
+
+def FixCopyrightHeaderText(text, year):
+ header_template = """// Copyright %s The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+"""
+
+ while text[0].find(" */") == -1:
+ text = text[1:]
+ text = text[1:]
+
+ return (header_template % year) + "".join(text)
+
+def FixCopyrightHeader(filepath):
+ with open(filepath, "r") as f:
+ text = f.readlines()
+
+ pattern = ".*Copyright \(C\) (20[01][0-9])"
+ m = re.match(pattern, text[0])
+ if m == None:
+ m = re.match(pattern, text[1])
+ assert m
+ year = m.group(1)
+
+ fixed_text = FixCopyrightHeaderText(text, year)
+ with open(filepath, "w") as f:
+ f.write(fixed_text)
+
+def Main():
+ files = Readfile("compositor.gyp")['variables']['webkit_compositor_sources']
+ for f in files:
+ dst =Copy(f)
+ FixCopyrightHeader(dst)
+
+if __name__ == '__main__':
+ import sys
+ os.chdir(os.path.dirname(__file__))
+ sys.exit(Main())
diff --git a/webkit/compositor/stubs/public/WebTransformationMatrix.h b/webkit/compositor/stubs/public/WebTransformationMatrix.h
new file mode 100644
index 0000000..2c6ed7c
--- /dev/null
+++ b/webkit/compositor/stubs/public/WebTransformationMatrix.h
@@ -0,0 +1,13 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_COMPOSITOR_STUBS_WEBTRANSFORMATIONMATRIX_H_
+#define WEBKIT_COMPOSITOR_STUBS_WEBTRANSFORMATIONMATRIX_H_
+
+#undef WEBKIT_IMPLEMENTATION
+#include "third_party/WebKit/Source/Platform/chromium/public/WebTransformationMatrix.h"
+#undef WEBKIT_IMPLEMENTATION
+#define WEBKIT_IMPLEMENTATION 1
+
+#endif // WEBKIT_COMPOSITOR_STUBS_WEBTRANSFORMATIONMATRIX_H_