summaryrefslogtreecommitdiffstats
path: root/gfx/layers
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/layers
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/layers')
-rw-r--r--gfx/layers/AsyncCanvasRenderer.cpp290
-rw-r--r--gfx/layers/AsyncCanvasRenderer.h168
-rw-r--r--gfx/layers/AtomicRefCountedWithFinalize.h196
-rw-r--r--gfx/layers/AxisPhysicsMSDModel.cpp93
-rw-r--r--gfx/layers/AxisPhysicsMSDModel.h86
-rw-r--r--gfx/layers/AxisPhysicsModel.cpp121
-rw-r--r--gfx/layers/AxisPhysicsModel.h131
-rw-r--r--gfx/layers/BSPTree.cpp107
-rw-r--r--gfx/layers/BSPTree.h106
-rw-r--r--gfx/layers/BufferTexture.cpp594
-rw-r--r--gfx/layers/BufferTexture.h99
-rw-r--r--gfx/layers/BufferUnrotate.cpp64
-rw-r--r--gfx/layers/BufferUnrotate.h14
-rw-r--r--gfx/layers/Compositor.cpp609
-rw-r--r--gfx/layers/Compositor.h726
-rw-r--r--gfx/layers/CompositorTypes.h249
-rw-r--r--gfx/layers/CopyableCanvasLayer.cpp102
-rw-r--r--gfx/layers/CopyableCanvasLayer.h67
-rw-r--r--gfx/layers/D3D11ShareHandleImage.cpp163
-rw-r--r--gfx/layers/D3D11ShareHandleImage.h72
-rw-r--r--gfx/layers/D3D9SurfaceImage.cpp223
-rw-r--r--gfx/layers/D3D9SurfaceImage.h86
-rw-r--r--gfx/layers/DirectedGraph.h116
-rw-r--r--gfx/layers/Effects.cpp67
-rw-r--r--gfx/layers/Effects.h331
-rw-r--r--gfx/layers/FrameMetrics.cpp23
-rw-r--r--gfx/layers/FrameMetrics.h1112
-rw-r--r--gfx/layers/GLImages.cpp113
-rw-r--r--gfx/layers/GLImages.h94
-rw-r--r--gfx/layers/GPUVideoImage.h71
-rw-r--r--gfx/layers/IMFYCbCrImage.cpp306
-rw-r--r--gfx/layers/IMFYCbCrImage.h39
-rw-r--r--gfx/layers/IPDLActor.h62
-rw-r--r--gfx/layers/ImageContainer.cpp797
-rw-r--r--gfx/layers/ImageContainer.h903
-rw-r--r--gfx/layers/ImageDataSerializer.cpp261
-rw-r--r--gfx/layers/ImageDataSerializer.h88
-rw-r--r--gfx/layers/ImageLayers.cpp64
-rw-r--r--gfx/layers/ImageLayers.h94
-rw-r--r--gfx/layers/ImageTypes.h127
-rw-r--r--gfx/layers/LayerMetricsWrapper.h516
-rw-r--r--gfx/layers/LayerScope.cpp1833
-rw-r--r--gfx/layers/LayerScope.h68
-rw-r--r--gfx/layers/LayerSorter.cpp361
-rw-r--r--gfx/layers/LayerSorter.h21
-rw-r--r--gfx/layers/LayerTreeInvalidation.cpp679
-rw-r--r--gfx/layers/LayerTreeInvalidation.h77
-rw-r--r--gfx/layers/LayerUserData.h28
-rw-r--r--gfx/layers/Layers.cpp2505
-rw-r--r--gfx/layers/Layers.h2619
-rw-r--r--gfx/layers/LayersLogging.cpp421
-rw-r--r--gfx/layers/LayersLogging.h267
-rw-r--r--gfx/layers/LayersTypes.cpp29
-rw-r--r--gfx/layers/LayersTypes.h250
-rw-r--r--gfx/layers/MacIOSurfaceHelpers.cpp176
-rw-r--r--gfx/layers/MacIOSurfaceHelpers.h28
-rw-r--r--gfx/layers/MacIOSurfaceImage.cpp36
-rw-r--r--gfx/layers/MacIOSurfaceImage.h48
-rw-r--r--gfx/layers/PersistentBufferProvider.cpp445
-rw-r--r--gfx/layers/PersistentBufferProvider.h187
-rw-r--r--gfx/layers/ReadbackLayer.h204
-rw-r--r--gfx/layers/ReadbackProcessor.cpp186
-rw-r--r--gfx/layers/ReadbackProcessor.h80
-rw-r--r--gfx/layers/RenderTrace.cpp82
-rw-r--r--gfx/layers/RenderTrace.h75
-rw-r--r--gfx/layers/RotatedBuffer.cpp798
-rw-r--r--gfx/layers/RotatedBuffer.h430
-rw-r--r--gfx/layers/TextureDIB.cpp505
-rw-r--r--gfx/layers/TextureDIB.h132
-rw-r--r--gfx/layers/TextureWrapperImage.cpp58
-rw-r--r--gfx/layers/TextureWrapperImage.h37
-rw-r--r--gfx/layers/TiledLayerBuffer.h220
-rw-r--r--gfx/layers/TransactionIdAllocator.h66
-rw-r--r--gfx/layers/TreeTraversal.h281
-rw-r--r--gfx/layers/apz/public/CompositorController.h33
-rw-r--r--gfx/layers/apz/public/GeckoContentController.h179
-rw-r--r--gfx/layers/apz/public/IAPZCTreeManager.cpp164
-rw-r--r--gfx/layers/apz/public/IAPZCTreeManager.h223
-rw-r--r--gfx/layers/apz/public/MetricsSharingController.h40
-rw-r--r--gfx/layers/apz/src/APZCTreeManager.cpp2099
-rw-r--r--gfx/layers/apz/src/APZCTreeManager.h531
-rw-r--r--gfx/layers/apz/src/APZUtils.h83
-rw-r--r--gfx/layers/apz/src/AndroidAPZ.cpp274
-rw-r--r--gfx/layers/apz/src/AndroidAPZ.h61
-rw-r--r--gfx/layers/apz/src/AsyncDragMetrics.h65
-rw-r--r--gfx/layers/apz/src/AsyncPanZoomAnimation.h80
-rw-r--r--gfx/layers/apz/src/AsyncPanZoomController.cpp4030
-rw-r--r--gfx/layers/apz/src/AsyncPanZoomController.h1224
-rw-r--r--gfx/layers/apz/src/Axis.cpp681
-rw-r--r--gfx/layers/apz/src/Axis.h336
-rw-r--r--gfx/layers/apz/src/CheckerboardEvent.cpp230
-rw-r--r--gfx/layers/apz/src/CheckerboardEvent.h221
-rw-r--r--gfx/layers/apz/src/DragTracker.cpp70
-rw-r--r--gfx/layers/apz/src/DragTracker.h39
-rw-r--r--gfx/layers/apz/src/GenericFlingAnimation.h207
-rw-r--r--gfx/layers/apz/src/GestureEventListener.cpp552
-rw-r--r--gfx/layers/apz/src/GestureEventListener.h252
-rw-r--r--gfx/layers/apz/src/HitTestingTreeNode.cpp336
-rw-r--r--gfx/layers/apz/src/HitTestingTreeNode.h166
-rw-r--r--gfx/layers/apz/src/InputBlockState.cpp868
-rw-r--r--gfx/layers/apz/src/InputBlockState.h483
-rw-r--r--gfx/layers/apz/src/InputQueue.cpp731
-rw-r--r--gfx/layers/apz/src/InputQueue.h217
-rw-r--r--gfx/layers/apz/src/Overscroll.h137
-rw-r--r--gfx/layers/apz/src/OverscrollHandoffState.cpp175
-rw-r--r--gfx/layers/apz/src/OverscrollHandoffState.h159
-rw-r--r--gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp79
-rw-r--r--gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h60
-rw-r--r--gfx/layers/apz/src/QueuedInput.cpp54
-rw-r--r--gfx/layers/apz/src/QueuedInput.h58
-rw-r--r--gfx/layers/apz/src/TouchCounter.cpp50
-rw-r--r--gfx/layers/apz/src/TouchCounter.h33
-rw-r--r--gfx/layers/apz/src/WheelScrollAnimation.cpp119
-rw-r--r--gfx/layers/apz/src/WheelScrollAnimation.h51
-rw-r--r--gfx/layers/apz/test/gtest/APZCBasicTester.h120
-rw-r--r--gfx/layers/apz/test/gtest/APZCTreeManagerTester.h194
-rw-r--r--gfx/layers/apz/test/gtest/APZTestCommon.h609
-rw-r--r--gfx/layers/apz/test/gtest/InputUtils.h297
-rw-r--r--gfx/layers/apz/test/gtest/TestBasic.cpp356
-rw-r--r--gfx/layers/apz/test/gtest/TestEventRegions.cpp272
-rw-r--r--gfx/layers/apz/test/gtest/TestGestureDetector.cpp638
-rw-r--r--gfx/layers/apz/test/gtest/TestHitTesting.cpp578
-rw-r--r--gfx/layers/apz/test/gtest/TestInputQueue.cpp44
-rw-r--r--gfx/layers/apz/test/gtest/TestPanning.cpp128
-rw-r--r--gfx/layers/apz/test/gtest/TestPinching.cpp294
-rw-r--r--gfx/layers/apz/test/gtest/TestScrollHandoff.cpp521
-rw-r--r--gfx/layers/apz/test/gtest/TestSnapping.cpp64
-rw-r--r--gfx/layers/apz/test/gtest/TestTreeManager.cpp112
-rw-r--r--gfx/layers/apz/test/gtest/moz.build33
-rw-r--r--gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js261
-rw-r--r--gfx/layers/apz/test/mochitest/apz_test_utils.js403
-rw-r--r--gfx/layers/apz/test/mochitest/chrome.ini9
-rw-r--r--gfx/layers/apz/test/mochitest/helper_basic_pan.html38
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1151663.html83
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1162771.html104
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1271432.html574
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1280013.html74
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1285070.html44
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1299195.html47
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug982141.html149
-rw-r--r--gfx/layers/apz/test/mochitest/helper_click.html41
-rw-r--r--gfx/layers/apz/test/mochitest/helper_div_pan.html44
-rw-r--r--gfx/layers/apz/test/mochitest/helper_drag_click.html43
-rw-r--r--gfx/layers/apz/test/mochitest/helper_drag_scroll.html603
-rw-r--r--gfx/layers/apz/test/mochitest/helper_iframe1.html14
-rw-r--r--gfx/layers/apz/test/mochitest/helper_iframe2.html14
-rw-r--r--gfx/layers/apz/test/mochitest/helper_iframe_pan.html41
-rw-r--r--gfx/layers/apz/test/mochitest/helper_long_tap.html81
-rw-r--r--gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html46
-rw-r--r--gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html47
-rw-r--r--gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html62
-rw-r--r--gfx/layers/apz/test/mochitest/helper_scrollto_tap.html61
-rw-r--r--gfx/layers/apz/test/mochitest/helper_subframe_style.css15
-rw-r--r--gfx/layers/apz/test/mochitest/helper_tall.html504
-rw-r--r--gfx/layers/apz/test/mochitest/helper_tap.html32
-rw-r--r--gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html33
-rw-r--r--gfx/layers/apz/test/mochitest/helper_tap_passive.html64
-rw-r--r--gfx/layers/apz/test/mochitest/helper_touch_action.html115
-rw-r--r--gfx/layers/apz/test/mochitest/helper_touch_action_complex.html143
-rw-r--r--gfx/layers/apz/test/mochitest/helper_touch_action_regions.html246
-rw-r--r--gfx/layers/apz/test/mochitest/mochitest.ini67
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1151663.html38
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1151667.html65
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1253683.html59
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1277814.html106
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1304689-2.html131
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug1304689.html135
-rw-r--r--gfx/layers/apz/test/mochitest/test_bug982141.html38
-rw-r--r--gfx/layers/apz/test/mochitest/test_frame_reconstruction.html218
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_mouseevents.html35
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_pointerevents.html31
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_touchevents.html104
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_wheelevents.html41
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_zoom.html44
-rw-r--r--gfx/layers/apz/test/mochitest/test_interrupted_reflow.html719
-rw-r--r--gfx/layers/apz/test/mochitest/test_layerization.html214
-rw-r--r--gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html541
-rw-r--r--gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html49
-rw-r--r--gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html117
-rw-r--r--gfx/layers/apz/test/mochitest/test_smoothness.html77
-rw-r--r--gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html114
-rw-r--r--gfx/layers/apz/test/mochitest/test_wheel_scroll.html106
-rw-r--r--gfx/layers/apz/test/mochitest/test_wheel_transactions.html137
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html9
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html10
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html14
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-h.html13
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html9
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html10
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html14
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-v.html13
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html9
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html10
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html14
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html13
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html9
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html14
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html9
-rw-r--r--gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html14
-rw-r--r--gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html27
-rw-r--r--gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html53
-rw-r--r--gfx/layers/apz/test/reftest/initial-scale-1-ref.html10
-rw-r--r--gfx/layers/apz/test/reftest/initial-scale-1.html10
-rw-r--r--gfx/layers/apz/test/reftest/reftest-stylo.list20
-rw-r--r--gfx/layers/apz/test/reftest/reftest.list19
-rw-r--r--gfx/layers/apz/testutil/APZTestData.cpp66
-rw-r--r--gfx/layers/apz/testutil/APZTestData.h168
-rw-r--r--gfx/layers/apz/util/APZCCallbackHelper.cpp942
-rw-r--r--gfx/layers/apz/util/APZCCallbackHelper.h209
-rw-r--r--gfx/layers/apz/util/APZEventState.cpp512
-rw-r--r--gfx/layers/apz/util/APZEventState.h103
-rw-r--r--gfx/layers/apz/util/APZThreadUtils.cpp96
-rw-r--r--gfx/layers/apz/util/APZThreadUtils.h104
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.cpp237
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.h104
-rw-r--r--gfx/layers/apz/util/CheckerboardReportService.cpp228
-rw-r--r--gfx/layers/apz/util/CheckerboardReportService.h144
-rw-r--r--gfx/layers/apz/util/ChromeProcessController.cpp276
-rw-r--r--gfx/layers/apz/util/ChromeProcessController.h83
-rw-r--r--gfx/layers/apz/util/ContentProcessController.cpp207
-rw-r--r--gfx/layers/apz/util/ContentProcessController.h90
-rw-r--r--gfx/layers/apz/util/DoubleTapToZoom.cpp178
-rw-r--r--gfx/layers/apz/util/DoubleTapToZoom.h29
-rw-r--r--gfx/layers/apz/util/InputAPZContext.cpp69
-rw-r--r--gfx/layers/apz/util/InputAPZContext.h50
-rw-r--r--gfx/layers/apz/util/ScrollInputMethods.h62
-rw-r--r--gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp49
-rw-r--r--gfx/layers/apz/util/ScrollLinkedEffectDetector.h43
-rw-r--r--gfx/layers/apz/util/TouchActionHelper.cpp96
-rw-r--r--gfx/layers/apz/util/TouchActionHelper.h42
-rw-r--r--gfx/layers/basic/AutoMaskData.h60
-rw-r--r--gfx/layers/basic/BasicCanvasLayer.cpp141
-rw-r--r--gfx/layers/basic/BasicCanvasLayer.h51
-rw-r--r--gfx/layers/basic/BasicColorLayer.cpp83
-rw-r--r--gfx/layers/basic/BasicCompositor.cpp996
-rw-r--r--gfx/layers/basic/BasicCompositor.h162
-rw-r--r--gfx/layers/basic/BasicContainerLayer.cpp172
-rw-r--r--gfx/layers/basic/BasicContainerLayer.h106
-rw-r--r--gfx/layers/basic/BasicImageLayer.cpp119
-rw-r--r--gfx/layers/basic/BasicImages.cpp178
-rw-r--r--gfx/layers/basic/BasicImplData.h132
-rw-r--r--gfx/layers/basic/BasicLayerManager.cpp991
-rw-r--r--gfx/layers/basic/BasicLayers.h217
-rw-r--r--gfx/layers/basic/BasicLayersImpl.cpp211
-rw-r--r--gfx/layers/basic/BasicLayersImpl.h151
-rw-r--r--gfx/layers/basic/BasicPaintedLayer.cpp245
-rw-r--r--gfx/layers/basic/BasicPaintedLayer.h133
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp107
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.h97
-rw-r--r--gfx/layers/basic/TextureClientX11.cpp156
-rw-r--r--gfx/layers/basic/TextureClientX11.h57
-rw-r--r--gfx/layers/basic/TextureHostBasic.cpp33
-rw-r--r--gfx/layers/basic/TextureHostBasic.h34
-rw-r--r--gfx/layers/basic/X11BasicCompositor.cpp136
-rw-r--r--gfx/layers/basic/X11BasicCompositor.h65
-rw-r--r--gfx/layers/basic/X11TextureSourceBasic.cpp67
-rw-r--r--gfx/layers/basic/X11TextureSourceBasic.h54
-rw-r--r--gfx/layers/client/CanvasClient.cpp531
-rw-r--r--gfx/layers/client/CanvasClient.h216
-rw-r--r--gfx/layers/client/ClientCanvasLayer.cpp261
-rw-r--r--gfx/layers/client/ClientCanvasLayer.h116
-rw-r--r--gfx/layers/client/ClientColorLayer.cpp79
-rw-r--r--gfx/layers/client/ClientContainerLayer.cpp36
-rw-r--r--gfx/layers/client/ClientContainerLayer.h189
-rw-r--r--gfx/layers/client/ClientImageLayer.cpp170
-rw-r--r--gfx/layers/client/ClientLayerManager.cpp907
-rw-r--r--gfx/layers/client/ClientLayerManager.h422
-rw-r--r--gfx/layers/client/ClientPaintedLayer.cpp175
-rw-r--r--gfx/layers/client/ClientPaintedLayer.h127
-rw-r--r--gfx/layers/client/ClientReadbackLayer.h34
-rw-r--r--gfx/layers/client/ClientTiledPaintedLayer.cpp615
-rw-r--r--gfx/layers/client/ClientTiledPaintedLayer.h153
-rw-r--r--gfx/layers/client/CompositableChild.cpp119
-rw-r--r--gfx/layers/client/CompositableChild.h91
-rw-r--r--gfx/layers/client/CompositableClient.cpp274
-rw-r--r--gfx/layers/client/CompositableClient.h209
-rw-r--r--gfx/layers/client/ContentClient.cpp668
-rw-r--r--gfx/layers/client/ContentClient.h412
-rw-r--r--gfx/layers/client/GPUVideoTextureClient.cpp72
-rw-r--r--gfx/layers/client/GPUVideoTextureClient.h56
-rw-r--r--gfx/layers/client/ImageClient.cpp300
-rw-r--r--gfx/layers/client/ImageClient.h138
-rw-r--r--gfx/layers/client/SingleTiledContentClient.cpp263
-rw-r--r--gfx/layers/client/SingleTiledContentClient.h135
-rw-r--r--gfx/layers/client/TextureClient.cpp1707
-rw-r--r--gfx/layers/client/TextureClient.h826
-rw-r--r--gfx/layers/client/TextureClientPool.cpp339
-rw-r--r--gfx/layers/client/TextureClientPool.h180
-rw-r--r--gfx/layers/client/TextureClientRecycleAllocator.cpp279
-rw-r--r--gfx/layers/client/TextureClientRecycleAllocator.h140
-rw-r--r--gfx/layers/client/TextureClientSharedSurface.cpp96
-rw-r--r--gfx/layers/client/TextureClientSharedSurface.h77
-rw-r--r--gfx/layers/client/TiledContentClient.cpp1430
-rw-r--r--gfx/layers/client/TiledContentClient.h545
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.cpp1495
-rw-r--r--gfx/layers/composite/AsyncCompositionManager.h283
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.cpp166
-rw-r--r--gfx/layers/composite/CanvasLayerComposite.h81
-rw-r--r--gfx/layers/composite/ColorLayerComposite.cpp47
-rw-r--r--gfx/layers/composite/ColorLayerComposite.h65
-rw-r--r--gfx/layers/composite/CompositableHost.cpp292
-rw-r--r--gfx/layers/composite/CompositableHost.h316
-rwxr-xr-xgfx/layers/composite/ContainerLayerComposite.cpp698
-rw-r--r--gfx/layers/composite/ContainerLayerComposite.h191
-rw-r--r--gfx/layers/composite/ContentHost.cpp491
-rw-r--r--gfx/layers/composite/ContentHost.h228
-rw-r--r--gfx/layers/composite/FPSCounter.cpp450
-rw-r--r--gfx/layers/composite/FPSCounter.h114
-rw-r--r--gfx/layers/composite/FontData.h13
-rw-r--r--gfx/layers/composite/FrameUniformityData.cpp152
-rw-r--r--gfx/layers/composite/FrameUniformityData.h73
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.cpp90
-rw-r--r--gfx/layers/composite/GPUVideoTextureHost.h53
-rw-r--r--gfx/layers/composite/ImageHost.cpp739
-rw-r--r--gfx/layers/composite/ImageHost.h201
-rw-r--r--gfx/layers/composite/ImageLayerComposite.cpp236
-rw-r--r--gfx/layers/composite/ImageLayerComposite.h79
-rw-r--r--gfx/layers/composite/LayerManagerComposite.cpp1451
-rw-r--r--gfx/layers/composite/LayerManagerComposite.h687
-rw-r--r--gfx/layers/composite/PaintCounter.cpp79
-rw-r--r--gfx/layers/composite/PaintCounter.h49
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.cpp194
-rw-r--r--gfx/layers/composite/PaintedLayerComposite.h94
-rw-r--r--gfx/layers/composite/TextRenderer.cpp173
-rw-r--r--gfx/layers/composite/TextRenderer.h50
-rw-r--r--gfx/layers/composite/TextureHost.cpp1142
-rw-r--r--gfx/layers/composite/TextureHost.h900
-rw-r--r--gfx/layers/composite/TiledContentHost.cpp645
-rw-r--r--gfx/layers/composite/TiledContentHost.h292
-rw-r--r--gfx/layers/composite/X11TextureHost.cpp107
-rw-r--r--gfx/layers/composite/X11TextureHost.h72
-rw-r--r--gfx/layers/composite/qrcode_table.h259
-rw-r--r--gfx/layers/d3d11/BlendShaderConstants.h59
-rw-r--r--gfx/layers/d3d11/BlendingHelpers.hlslh184
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.cpp1586
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.h208
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.hlsl421
-rwxr-xr-xgfx/layers/d3d11/CompositorD3D11Shaders.h9434
-rw-r--r--gfx/layers/d3d11/ReadbackManagerD3D11.cpp160
-rw-r--r--gfx/layers/d3d11/ReadbackManagerD3D11.h65
-rw-r--r--gfx/layers/d3d11/TextureD3D11.cpp1291
-rw-r--r--gfx/layers/d3d11/TextureD3D11.h455
-rw-r--r--gfx/layers/d3d11/genshaders.sh50
-rw-r--r--gfx/layers/d3d9/CompositorD3D9.cpp1045
-rw-r--r--gfx/layers/d3d9/CompositorD3D9.h189
-rw-r--r--gfx/layers/d3d9/DeviceManagerD3D9.cpp966
-rw-r--r--gfx/layers/d3d9/DeviceManagerD3D9.h353
-rwxr-xr-xgfx/layers/d3d9/LayerManagerD3D9Shaders.h1631
-rw-r--r--gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl242
-rw-r--r--gfx/layers/d3d9/Nv3DVUtils.cpp148
-rw-r--r--gfx/layers/d3d9/Nv3DVUtils.h86
-rw-r--r--gfx/layers/d3d9/ReadbackLayerD3D9.h34
-rw-r--r--gfx/layers/d3d9/TextureD3D9.cpp1218
-rw-r--r--gfx/layers/d3d9/TextureD3D9.h433
-rw-r--r--gfx/layers/d3d9/genshaders.sh35
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.cpp266
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.h111
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.cpp313
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.h151
-rw-r--r--gfx/layers/ipc/APZChild.cpp98
-rw-r--r--gfx/layers/ipc/APZChild.h55
-rw-r--r--gfx/layers/ipc/CompositableForwarder.cpp28
-rw-r--r--gfx/layers/ipc/CompositableForwarder.h127
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.cpp245
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.h55
-rw-r--r--gfx/layers/ipc/CompositorBench.cpp345
-rw-r--r--gfx/layers/ipc/CompositorBench.h31
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.cpp1150
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.h333
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.cpp2441
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.h688
-rw-r--r--gfx/layers/ipc/CompositorThread.cpp155
-rw-r--r--gfx/layers/ipc/CompositorThread.h68
-rw-r--r--gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp578
-rw-r--r--gfx/layers/ipc/CrossProcessCompositorBridgeParent.h177
-rw-r--r--gfx/layers/ipc/GonkNativeHandle.cpp80
-rw-r--r--gfx/layers/ipc/GonkNativeHandle.h24
-rw-r--r--gfx/layers/ipc/GonkNativeHandleUtils.cpp93
-rw-r--r--gfx/layers/ipc/GonkNativeHandleUtils.h26
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.cpp235
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.h318
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.cpp1239
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.h400
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.cpp449
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.h155
-rw-r--r--gfx/layers/ipc/ImageContainerChild.cpp70
-rw-r--r--gfx/layers/ipc/ImageContainerChild.h61
-rw-r--r--gfx/layers/ipc/ImageContainerParent.cpp31
-rw-r--r--gfx/layers/ipc/ImageContainerParent.h37
-rwxr-xr-xgfx/layers/ipc/KnowsCompositor.h90
-rw-r--r--gfx/layers/ipc/LayerAnimationUtils.cpp45
-rw-r--r--gfx/layers/ipc/LayerAnimationUtils.h30
-rw-r--r--gfx/layers/ipc/LayerTransactionChild.cpp87
-rw-r--r--gfx/layers/ipc/LayerTransactionChild.h90
-rw-r--r--gfx/layers/ipc/LayerTransactionParent.cpp1098
-rw-r--r--gfx/layers/ipc/LayerTransactionParent.h240
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.cpp83
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.h71
-rw-r--r--gfx/layers/ipc/LayersMessages.ipdlh499
-rw-r--r--gfx/layers/ipc/LayersSurfaces.ipdlh142
-rw-r--r--gfx/layers/ipc/PAPZ.ipdl71
-rw-r--r--gfx/layers/ipc/PAPZCTreeManager.ipdl139
-rw-r--r--gfx/layers/ipc/PCompositable.ipdl28
-rw-r--r--gfx/layers/ipc/PCompositorBridge.ipdl237
-rw-r--r--gfx/layers/ipc/PImageBridge.ipdl70
-rw-r--r--gfx/layers/ipc/PImageContainer.ipdl33
-rw-r--r--gfx/layers/ipc/PLayer.ipdl39
-rw-r--r--gfx/layers/ipc/PLayerTransaction.ipdl133
-rw-r--r--gfx/layers/ipc/PTexture.ipdl46
-rw-r--r--gfx/layers/ipc/PVideoBridge.ipdl31
-rw-r--r--gfx/layers/ipc/RemoteContentController.cpp272
-rw-r--r--gfx/layers/ipc/RemoteContentController.h96
-rw-r--r--gfx/layers/ipc/ShadowLayerChild.cpp45
-rw-r--r--gfx/layers/ipc/ShadowLayerChild.h39
-rw-r--r--gfx/layers/ipc/ShadowLayerParent.cpp138
-rw-r--r--gfx/layers/ipc/ShadowLayerParent.h58
-rw-r--r--gfx/layers/ipc/ShadowLayerUtils.h52
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsMac.cpp40
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsX11.cpp153
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsX11.h85
-rw-r--r--gfx/layers/ipc/ShadowLayers.cpp1044
-rw-r--r--gfx/layers/ipc/ShadowLayers.h465
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.cpp246
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.h60
-rw-r--r--gfx/layers/ipc/SharedRGBImage.cpp113
-rw-r--r--gfx/layers/ipc/SharedRGBImage.h59
-rw-r--r--gfx/layers/ipc/SynchronousTask.h65
-rw-r--r--gfx/layers/ipc/TextureForwarder.h79
-rw-r--r--gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h91
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.cpp122
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.h75
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.cpp125
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.h72
-rw-r--r--gfx/layers/layerviewer/hide.pngbin0 -> 3079 bytes
-rw-r--r--gfx/layers/layerviewer/index.html47
-rw-r--r--gfx/layers/layerviewer/layerTreeView.js885
-rwxr-xr-xgfx/layers/layerviewer/noise.pngbin0 -> 2118 bytes
-rw-r--r--gfx/layers/layerviewer/show.pngbin0 -> 3187 bytes
-rw-r--r--gfx/layers/layerviewer/tree.css36
-rw-r--r--gfx/layers/moz.build451
-rw-r--r--gfx/layers/opengl/Composer2D.h73
-rw-r--r--gfx/layers/opengl/CompositingRenderTargetOGL.cpp120
-rw-r--r--gfx/layers/opengl/CompositingRenderTargetOGL.h195
-rw-r--r--gfx/layers/opengl/CompositorOGL.cpp1916
-rw-r--r--gfx/layers/opengl/CompositorOGL.h502
-rw-r--r--gfx/layers/opengl/EGLImageHelpers.cpp53
-rw-r--r--gfx/layers/opengl/EGLImageHelpers.h27
-rw-r--r--gfx/layers/opengl/GLBlitTextureImageHelper.cpp280
-rw-r--r--gfx/layers/opengl/GLBlitTextureImageHelper.h71
-rw-r--r--gfx/layers/opengl/GLManager.cpp70
-rw-r--r--gfx/layers/opengl/GLManager.h44
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp140
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h58
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp172
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h114
-rw-r--r--gfx/layers/opengl/OGLShaderProgram.cpp974
-rw-r--r--gfx/layers/opengl/OGLShaderProgram.h621
-rw-r--r--gfx/layers/opengl/TextureClientOGL.cpp136
-rw-r--r--gfx/layers/opengl/TextureClientOGL.h89
-rw-r--r--gfx/layers/opengl/TextureHostOGL.cpp771
-rw-r--r--gfx/layers/opengl/TextureHostOGL.h539
-rw-r--r--gfx/layers/opengl/TexturePoolOGL.cpp123
-rw-r--r--gfx/layers/opengl/TexturePoolOGL.h40
-rw-r--r--gfx/layers/opengl/X11TextureSourceOGL.cpp114
-rw-r--r--gfx/layers/opengl/X11TextureSourceOGL.h65
-rw-r--r--gfx/layers/protobuf/LayerScopePacket.pb.cc6802
-rw-r--r--gfx/layers/protobuf/LayerScopePacket.pb.h5779
-rw-r--r--gfx/layers/protobuf/LayerScopePacket.proto218
468 files changed, 143658 insertions, 0 deletions
diff --git a/gfx/layers/AsyncCanvasRenderer.cpp b/gfx/layers/AsyncCanvasRenderer.cpp
new file mode 100644
index 000000000..d9a0e9886
--- /dev/null
+++ b/gfx/layers/AsyncCanvasRenderer.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AsyncCanvasRenderer.h"
+
+#include "gfxUtils.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+AsyncCanvasRenderer::AsyncCanvasRenderer()
+ : mHTMLCanvasElement(nullptr)
+ , mContext(nullptr)
+ , mGLContext(nullptr)
+ , mIsAlphaPremultiplied(true)
+ , mWidth(0)
+ , mHeight(0)
+ , mCanvasClientAsyncID(0)
+ , mCanvasClient(nullptr)
+ , mMutex("AsyncCanvasRenderer::mMutex")
+{
+ MOZ_COUNT_CTOR(AsyncCanvasRenderer);
+}
+
+AsyncCanvasRenderer::~AsyncCanvasRenderer()
+{
+ MOZ_COUNT_DTOR(AsyncCanvasRenderer);
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutAttributesChanged()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutInvalidation()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
+{
+ mCanvasClient = aClient;
+ if (aClient) {
+ mCanvasClientAsyncID = aClient->GetAsyncID();
+ } else {
+ mCanvasClientAsyncID = 0;
+ }
+}
+
+void
+AsyncCanvasRenderer::SetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = NS_GetCurrentThread();
+}
+
+void
+AsyncCanvasRenderer::ResetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = nullptr;
+}
+
+already_AddRefed<nsIThread>
+AsyncCanvasRenderer::GetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ nsCOMPtr<nsIThread> result = mActiveThread;
+ return result.forget();
+}
+
+void
+AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
+{
+ MutexAutoLock lock(mMutex);
+
+ if (!aTextureClient) {
+ mSurfaceForBasic = nullptr;
+ return;
+ }
+
+ TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
+ if (!texLock.Succeeded()) {
+ return;
+ }
+
+ const gfx::IntSize& size = aTextureClient->GetSize();
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ // Avoid to create buffer every time.
+ if (!mSurfaceForBasic ||
+ size != mSurfaceForBasic->GetSize() ||
+ format != mSurfaceForBasic->GetFormat())
+ {
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+ }
+
+ MappedTextureData mapped;
+ if (!aTextureClient->BorrowMappedData(mapped)) {
+ return;
+ }
+
+ const uint8_t* lockedBytes = mapped.data;
+ gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
+ gfx::DataSourceSurface::MapType::WRITE);
+ if (!map.IsMapped()) {
+ return;
+ }
+
+ MOZ_ASSERT(map.GetStride() == mapped.stride);
+ memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
+
+ if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
+ gl::SwapRAndBComponents(mSurfaceForBasic);
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::UpdateTarget()
+{
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* frontbuffer = nullptr;
+ gl::GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+
+ if (!frontbuffer) {
+ return nullptr;
+ }
+
+ if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
+ return nullptr;
+ }
+
+ const gfx::IntSize& size = frontbuffer->mSize;
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+
+
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ if (!frontbuffer->ReadbackBySharedHandle(surface)) {
+ return nullptr;
+ }
+
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(surface, surface);
+ }
+
+ return surface.forget();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::GetSurface()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mMutex);
+ if (mSurfaceForBasic) {
+ // Since SourceSurface isn't thread-safe, we need copy to a new SourceSurface.
+ RefPtr<gfx::DataSourceSurface> result =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(mSurfaceForBasic->GetSize(),
+ mSurfaceForBasic->GetFormat(),
+ mSurfaceForBasic->Stride());
+
+ gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic, gfx::DataSourceSurface::READ);
+ gfx::DataSourceSurface::ScopedMap dstMap(result, gfx::DataSourceSurface::WRITE);
+
+ if (NS_WARN_IF(!srcMap.IsMapped()) ||
+ NS_WARN_IF(!dstMap.IsMapped())) {
+ return nullptr;
+ }
+
+ memcpy(dstMap.GetData(),
+ srcMap.GetData(),
+ srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
+ return result.forget();
+ } else {
+ return UpdateTarget();
+ }
+}
+
+nsresult
+AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
+ const char16_t *aEncoderOptions,
+ nsIInputStream **aStream)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<gfx::DataSourceSurface> surface = GetSurface();
+ if (!surface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Handle y flip.
+ RefPtr<gfx::DataSourceSurface> dataSurf = gl::YInvertImageSurface(surface);
+
+ return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions, aStream);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/AsyncCanvasRenderer.h b/gfx/layers/AsyncCanvasRenderer.h
new file mode 100644
index 000000000..404695de4
--- /dev/null
+++ b/gfx/layers/AsyncCanvasRenderer.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
+#define MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
+
+#include "LayersTypes.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Mutex.h"
+#include "nsCOMPtr.h" // for nsCOMPtr
+
+class nsICanvasRenderingContextInternal;
+class nsIInputStream;
+class nsIThread;
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+}
+
+namespace gl {
+class GLContext;
+}
+
+namespace dom {
+class HTMLCanvasElement;
+}
+
+namespace layers {
+
+class CanvasClient;
+class TextureClient;
+
+/**
+ * Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create
+ * AsyncCanvasRenderer which is thread-safe wrapper object for communicating
+ * among main, worker and ImageBridgeChild threads.
+ *
+ * Each HTMLCanvasElement object is responsible for creating
+ * AsyncCanvasRenderer object. Once Canvas is transfered to worker,
+ * OffscreenCanvas will keep reference pointer of this object.
+ *
+ * Sometimes main thread needs AsyncCanvasRenderer's result, such as layers
+ * fallback to BasicLayerManager or calling toDataURL in Javascript. Simply call
+ * GetSurface() in main thread will readback the result to mSurface.
+ *
+ * If layers backend is LAYERS_CLIENT, this object will pass to ImageBridgeChild
+ * for submitting frames to Compositor.
+ */
+class AsyncCanvasRenderer final
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncCanvasRenderer)
+
+public:
+ AsyncCanvasRenderer();
+
+ void NotifyElementAboutAttributesChanged();
+ void NotifyElementAboutInvalidation();
+
+ void SetCanvasClient(CanvasClient* aClient);
+
+ void SetWidth(uint32_t aWidth)
+ {
+ mWidth = aWidth;
+ }
+
+ void SetHeight(uint32_t aHeight)
+ {
+ mHeight = aHeight;
+ }
+
+ void SetIsAlphaPremultiplied(bool aIsAlphaPremultiplied)
+ {
+ mIsAlphaPremultiplied = aIsAlphaPremultiplied;
+ }
+
+ // Active thread means the thread which spawns GLContext.
+ void SetActiveThread();
+ void ResetActiveThread();
+
+ // This will readback surface and return the surface
+ // in the DataSourceSurface.
+ // Can be called in main thread only.
+ already_AddRefed<gfx::DataSourceSurface> GetSurface();
+
+ // For SharedSurface_Basic case, before the frame sending to the compositor,
+ // we readback it to a texture client because SharedSurface_Basic cannot shared.
+ // We don't want to readback it again here, so just copy the content of that
+ // texture client here to avoid readback again.
+ void CopyFromTextureClient(TextureClient *aClient);
+
+ // Readback current WebGL's content and convert it to InputStream. This
+ // function called GetSurface implicitly and GetSurface handles only get
+ // called in the main thread. So this function can be called in main thread.
+ nsresult
+ GetInputStream(const char *aMimeType,
+ const char16_t *aEncoderOptions,
+ nsIInputStream **aStream);
+
+ gfx::IntSize GetSize() const
+ {
+ return gfx::IntSize(mWidth, mHeight);
+ }
+
+ uint64_t GetCanvasClientAsyncID() const
+ {
+ return mCanvasClientAsyncID;
+ }
+
+ CanvasClient* GetCanvasClient() const
+ {
+ return mCanvasClient;
+ }
+
+ already_AddRefed<nsIThread> GetActiveThread();
+
+ // The lifetime is controllered by HTMLCanvasElement.
+ // Only accessed in main thread.
+ dom::HTMLCanvasElement* mHTMLCanvasElement;
+
+ // Only accessed in active thread.
+ nsICanvasRenderingContextInternal* mContext;
+
+ // We need to keep a reference to the context around here, otherwise the
+ // canvas' surface texture destructor will deref and destroy it too early
+ // Only accessed in active thread.
+ RefPtr<gl::GLContext> mGLContext;
+private:
+
+ virtual ~AsyncCanvasRenderer();
+
+ // Readback current WebGL's content and return it as DataSourceSurface.
+ already_AddRefed<gfx::DataSourceSurface> UpdateTarget();
+
+ bool mIsAlphaPremultiplied;
+
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint64_t mCanvasClientAsyncID;
+
+ // The lifetime of this pointer is controlled by OffscreenCanvas
+ // Can be accessed in active thread and ImageBridge thread.
+ // But we never accessed it at the same time on both thread. So no
+ // need to protect this member.
+ CanvasClient* mCanvasClient;
+
+ // When backend is LAYER_BASIC and SharedSurface type is Basic.
+ // CanvasClient will readback the GLContext to a TextureClient
+ // in order to send frame to compositor. To avoid readback again,
+ // we copy from this TextureClient to this mSurfaceForBasic directly
+ // by calling CopyFromTextureClient().
+ RefPtr<gfx::DataSourceSurface> mSurfaceForBasic;
+
+ // Protect non thread-safe objects.
+ Mutex mMutex;
+
+ // Can be accessed in any thread, need protect by mutex.
+ nsCOMPtr<nsIThread> mActiveThread;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
diff --git a/gfx/layers/AtomicRefCountedWithFinalize.h b/gfx/layers/AtomicRefCountedWithFinalize.h
new file mode 100644
index 000000000..7bd8f0203
--- /dev/null
+++ b/gfx/layers/AtomicRefCountedWithFinalize.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+#define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Likely.h"
+#include "MainThreadUtils.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "mozilla/gfx/Logging.h"
+
+#define ADDREF_MANUALLY(obj) (obj)->AddRefManually(__FUNCTION__, __FILE__, __LINE__)
+#define RELEASE_MANUALLY(obj) (obj)->ReleaseManually(__FUNCTION__, __FILE__, __LINE__)
+
+namespace mozilla {
+
+template<class U>
+class StaticRefPtr;
+
+namespace gl {
+template<typename T>
+class RefSet;
+
+template<typename T>
+class RefQueue;
+} // namespace gl
+
+template<typename T>
+class AtomicRefCountedWithFinalize
+{
+protected:
+ explicit AtomicRefCountedWithFinalize(const char* aName)
+ : mRecycleCallback(nullptr)
+ , mRefCount(0)
+#ifdef DEBUG
+ , mSpew(false)
+ , mManualAddRefs(0)
+ , mManualReleases(0)
+#endif
+#ifdef NS_BUILD_REFCNT_LOGGING
+ , mName(aName)
+#endif
+ {}
+
+ ~AtomicRefCountedWithFinalize() {
+ if (mRefCount >= 0) {
+ gfxCriticalError() << "Deleting referenced object? " << mRefCount;
+ }
+ }
+
+public:
+ // Mark user classes that are considered flawless.
+ template<class U>
+ friend class ::mozilla::StaticRefPtr;
+
+ template<class U>
+ friend struct mozilla::RefPtrTraits;
+
+ template<typename U>
+ friend class ::mozilla::gl::RefSet;
+
+ template<typename U>
+ friend class ::mozilla::gl::RefQueue;
+
+ //friend class mozilla::gl::SurfaceFactory;
+
+ void AddRefManually(const char* funcName, const char* fileName, uint32_t lineNum) {
+#ifdef DEBUG
+ uint32_t count = ++mManualAddRefs;
+ if (mSpew) {
+ printf_stderr("AddRefManually() #%u in %s at %s:%u\n", count, funcName,
+ fileName, lineNum);
+ }
+#else
+ (void)funcName;
+ (void)fileName;
+ (void)lineNum;
+#endif
+ AddRef();
+ }
+
+ void ReleaseManually(const char* funcName, const char* fileName, uint32_t lineNum) {
+#ifdef DEBUG
+ uint32_t count = ++mManualReleases;
+ if (mSpew) {
+ printf_stderr("ReleaseManually() #%u in %s at %s:%u\n", count, funcName,
+ fileName, lineNum);
+ }
+#else
+ (void)funcName;
+ (void)fileName;
+ (void)lineNum;
+#endif
+ Release();
+ }
+
+private:
+ void AddRef() {
+ MOZ_ASSERT(mRefCount >= 0, "AddRef() during/after Finalize()/dtor.");
+ mRefCount++;
+ NS_LOG_ADDREF(this, mRefCount, mName, sizeof(*this));
+ }
+
+ void Release() {
+ MOZ_ASSERT(mRefCount > 0, "Release() during/after Finalize()/dtor.");
+ // Read mRecycleCallback early so that it does not get set to
+ // deleted memory, if the object is goes away. See bug 994903.
+ // This saves us in the case where there is no callback, so that
+ // we can do the "else if" below.
+ RecycleCallback recycleCallback = mRecycleCallback;
+ int currCount = --mRefCount;
+ if (currCount < 0) {
+ gfxCriticalError() << "Invalid reference count release" << currCount;
+ ++mRefCount;
+ return;
+ }
+ NS_LOG_RELEASE(this, currCount, mName);
+
+ if (0 == currCount) {
+ mRefCount = detail::DEAD;
+ MOZ_ASSERT(IsDead());
+
+ // Recycle listeners must call ClearRecycleCallback
+ // before releasing their strong reference.
+ if (mRecycleCallback) {
+ gfxCriticalError() << "About to release with valid callback";
+ mRecycleCallback = nullptr;
+ }
+
+ MOZ_ASSERT(mManualAddRefs == mManualReleases);
+
+ T* derived = static_cast<T*>(this);
+ derived->Finalize();
+ delete derived;
+ } else if (1 == currCount && recycleCallback) {
+ // There is nothing enforcing this in the code, except how the callers
+ // are being careful to never let the reference count go down if there
+ // is a callback.
+ MOZ_ASSERT(!IsDead());
+ T* derived = static_cast<T*>(this);
+ recycleCallback(derived, mClosure);
+ }
+ }
+
+public:
+ typedef void (*RecycleCallback)(T* aObject, void* aClosure);
+ /**
+ * Set a callback responsible for recycling this object
+ * before it is finalized.
+ */
+ void SetRecycleCallback(RecycleCallback aCallback, void* aClosure)
+ {
+ MOZ_ASSERT(!IsDead());
+ mRecycleCallback = aCallback;
+ mClosure = aClosure;
+ }
+ void ClearRecycleCallback()
+ {
+ MOZ_ASSERT(!IsDead());
+ SetRecycleCallback(nullptr, nullptr);
+ }
+
+ bool HasRecycleCallback() const
+ {
+ MOZ_ASSERT(!IsDead());
+ return !!mRecycleCallback;
+ }
+
+ bool IsDead() const
+ {
+ return mRefCount < 0;
+ }
+
+private:
+ RecycleCallback mRecycleCallback;
+ void *mClosure;
+ Atomic<int> mRefCount;
+#ifdef DEBUG
+public:
+ bool mSpew;
+private:
+ Atomic<uint32_t> mManualAddRefs;
+ Atomic<uint32_t> mManualReleases;
+#endif
+#ifdef NS_BUILD_REFCNT_LOGGING
+ const char* mName;
+#endif
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/AxisPhysicsMSDModel.cpp b/gfx/layers/AxisPhysicsMSDModel.cpp
new file mode 100644
index 000000000..58649cc24
--- /dev/null
+++ b/gfx/layers/AxisPhysicsMSDModel.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsMSDModel.h"
+#include <math.h> // for sqrt and fabs
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Constructs an AxisPhysicsMSDModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulated spring,
+ * in AppUnits.
+ * @param aInitialDestination sets the resting position of the simulated spring,
+ * in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulated spring,
+ * in AppUnits / second. Critically-damped and over-damped systems are
+ * guaranteed not to overshoot aInitialDestination if this is set to 0;
+ * however, it is possible to overshoot and oscillate if not set to 0 or
+ * the system is under-damped.
+ * @param aSpringConstant sets the strength of the simulated spring. Greater
+ * values of mSpringConstant result in a stiffer / stronger spring.
+ * @param aDampingRatio controls the amount of dampening force and determines
+ * if the system is under-damped, critically-damped, or over-damped.
+ */
+AxisPhysicsMSDModel::AxisPhysicsMSDModel(double aInitialPosition,
+ double aInitialDestination,
+ double aInitialVelocity,
+ double aSpringConstant,
+ double aDampingRatio)
+ : AxisPhysicsModel(aInitialPosition, aInitialVelocity)
+ , mDestination(aInitialDestination)
+ , mSpringConstant(aSpringConstant)
+ , mSpringConstantSqrtXTwo(sqrt(mSpringConstant) * 2.0)
+ , mDampingRatio(aDampingRatio)
+{
+}
+
+AxisPhysicsMSDModel::~AxisPhysicsMSDModel()
+{
+}
+
+double
+AxisPhysicsMSDModel::Acceleration(const State &aState)
+{
+ // Simulate a Mass-Damper-Spring Model; assume a unit mass
+
+ // Hooke’s Law: http://en.wikipedia.org/wiki/Hooke%27s_law
+ double spring_force = (mDestination - aState.p) * mSpringConstant;
+ double damp_force = -aState.v * mDampingRatio * mSpringConstantSqrtXTwo;
+
+ return spring_force + damp_force;
+}
+
+
+double
+AxisPhysicsMSDModel::GetDestination() const
+{
+ return mDestination;
+}
+
+void
+AxisPhysicsMSDModel::SetDestination(double aDestination)
+{
+ mDestination = aDestination;
+}
+
+bool
+AxisPhysicsMSDModel::IsFinished(double aSmallestVisibleIncrement)
+{
+ // In order to satisfy the condition of reaching the destination, the distance
+ // between the simulation position and the destination must be less than
+ // aSmallestVisibleIncrement while the speed is simultaneously less than
+ // finishVelocity. This enables an under-damped system to overshoot the
+ // destination when desired without prematurely triggering the finished state.
+ // If finishVelocity is set too low, the animation may end long after
+ // oscillation has finished, resulting in unnecessary processing.
+ // If set too high, the animation may prematurely terminate when expected
+ // to overshoot the destination in an under-damped system.
+ // aSmallestVisibleIncrement * 2 was selected through experimentation that
+ // revealed that a critically damped system will terminate within 100ms.
+ const double finishVelocity = aSmallestVisibleIncrement * 2;
+
+ return fabs(mDestination - GetPosition ()) < aSmallestVisibleIncrement
+ && fabs(GetVelocity()) <= finishVelocity;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/AxisPhysicsMSDModel.h b/gfx/layers/AxisPhysicsMSDModel.h
new file mode 100644
index 000000000..370f81224
--- /dev/null
+++ b/gfx/layers/AxisPhysicsMSDModel.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsMSDModel_h
+#define mozilla_layers_AxisPhysicsMSDModel_h
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * AxisPhysicsMSDModel encapsulates a 1-dimensional MSD (Mass-Spring-Damper)
+ * model. A unit mass is assumed.
+ */
+class AxisPhysicsMSDModel : public AxisPhysicsModel {
+public:
+ AxisPhysicsMSDModel(double aInitialPosition, double aInitialDestination,
+ double aInitialVelocity, double aSpringConstant,
+ double aDampingRatio);
+
+ ~AxisPhysicsMSDModel();
+
+ /**
+ * Gets the raw destination of this axis at this moment.
+ */
+ double GetDestination() const;
+
+ /**
+ * Sets the raw destination of this axis at this moment.
+ */
+ void SetDestination(double aDestination);
+
+ /**
+ * Returns true when the position is close to the destination and the
+ * velocity is low.
+ */
+ bool IsFinished(double aSmallestVisibleIncrement);
+
+protected:
+ virtual double Acceleration(const State &aState);
+
+private:
+
+ /**
+ * mDestination represents the target position and the resting position of
+ * the simulated spring.
+ */
+ double mDestination;
+
+ /**
+ * Greater values of mSpringConstant result in a stiffer / stronger spring.
+ */
+ double mSpringConstant;
+
+ /**
+ * mSpringConstantSqrtTimesTwo is calculated from mSpringConstant to reduce
+ * calculations performed in the inner loop.
+ */
+ double mSpringConstantSqrtXTwo;
+
+ /**
+ * Damping Ratio: http://en.wikipedia.org/wiki/Damping_ratio
+ *
+ * When mDampingRatio < 1.0, this is an under damped system.
+ * - Overshoots destination and oscillates with the amplitude gradually
+ * decreasing to zero.
+ *
+ * When mDampingRatio == 1.0, this is a critically damped system.
+ * - Reaches destination as quickly as possible without oscillating.
+ *
+ * When mDampingRatio > 1.0, this is an over damped system.
+ * - Reaches destination (exponentially decays) without oscillating.
+ */
+ double mDampingRatio;
+
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/AxisPhysicsModel.cpp b/gfx/layers/AxisPhysicsModel.cpp
new file mode 100644
index 000000000..3ae686d16
--- /dev/null
+++ b/gfx/layers/AxisPhysicsModel.cpp
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AxisPhysicsModel.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The simulation is advanced forward in time with a fixed time step to ensure
+ * that it remains deterministic given variable framerates. To determine the
+ * position at any variable time, two samples are interpolated.
+ *
+ * kFixedtimestep is set to 120hz in order to ensure that every frame in a
+ * common 60hz refresh rate display will have at least one physics simulation
+ * sample. More accuracy can be obtained by reducing kFixedTimestep to smaller
+ * intervals, such as 240hz or 1000hz, at the cost of more CPU cycles. If
+ * kFixedTimestep is increased to much longer intervals, interpolation will
+ * become less effective at reducing temporal jitter and the simulation will
+ * lose accuracy.
+ */
+const double AxisPhysicsModel::kFixedTimestep = 1.0 / 120.0; // 120hz
+
+/**
+ * Constructs an AxisPhysicsModel with initial values for state.
+ *
+ * @param aInitialPosition sets the initial position of the simulation,
+ * in AppUnits.
+ * @param aInitialVelocity sets the initial velocity of the simulation,
+ * in AppUnits / second.
+ */
+AxisPhysicsModel::AxisPhysicsModel(double aInitialPosition,
+ double aInitialVelocity)
+ : mProgress(1.0)
+ , mPrevState(aInitialPosition, aInitialVelocity)
+ , mNextState(aInitialPosition, aInitialVelocity)
+{
+
+}
+
+AxisPhysicsModel::~AxisPhysicsModel()
+{
+
+}
+
+double
+AxisPhysicsModel::GetVelocity()
+{
+ return LinearInterpolate(mPrevState.v, mNextState.v, mProgress);
+}
+
+double
+AxisPhysicsModel::GetPosition()
+{
+ return LinearInterpolate(mPrevState.p, mNextState.p, mProgress);
+}
+
+void
+AxisPhysicsModel::SetVelocity(double aVelocity)
+{
+ mNextState.v = aVelocity;
+ mNextState.p = GetPosition();
+ mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::SetPosition(double aPosition)
+{
+ mNextState.v = GetVelocity();
+ mNextState.p = aPosition;
+ mProgress = 1.0;
+}
+
+void
+AxisPhysicsModel::Simulate(const TimeDuration& aDeltaTime)
+{
+ for(mProgress += aDeltaTime.ToSeconds() / kFixedTimestep;
+ mProgress > 1.0; mProgress -= 1.0) {
+ Integrate(kFixedTimestep);
+ }
+}
+
+void
+AxisPhysicsModel::Integrate(double aDeltaTime)
+{
+ mPrevState = mNextState;
+
+ // RK4 (Runge-Kutta method) Integration
+ // http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
+ Derivative a = Evaluate( mNextState, 0.0, Derivative() );
+ Derivative b = Evaluate( mNextState, aDeltaTime * 0.5, a );
+ Derivative c = Evaluate( mNextState, aDeltaTime * 0.5, b );
+ Derivative d = Evaluate( mNextState, aDeltaTime, c );
+
+ double dpdt = 1.0 / 6.0 * (a.dp + 2.0 * (b.dp + c.dp) + d.dp);
+ double dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
+
+ mNextState.p += dpdt * aDeltaTime;
+ mNextState.v += dvdt * aDeltaTime;
+}
+
+AxisPhysicsModel::Derivative
+AxisPhysicsModel::Evaluate(const State &aInitState, double aDeltaTime,
+ const Derivative &aDerivative)
+{
+ State state( aInitState.p + aDerivative.dp*aDeltaTime, aInitState.v + aDerivative.dv*aDeltaTime );
+
+ return Derivative( state.v, Acceleration(state) );
+}
+
+double
+AxisPhysicsModel::LinearInterpolate(double aV1, double aV2, double aBlend)
+{
+ return aV1 * (1.0 - aBlend) + aV2 * aBlend;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/AxisPhysicsModel.h b/gfx/layers/AxisPhysicsModel.h
new file mode 100644
index 000000000..b1cf59b45
--- /dev/null
+++ b/gfx/layers/AxisPhysicsModel.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AxisPhysicsModel_h
+#define mozilla_layers_AxisPhysicsModel_h
+
+#include "AxisPhysicsModel.h"
+#include <sys/types.h> // for int32_t
+#include "mozilla/TimeStamp.h" // for TimeDuration
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * AxisPhysicsModel encapsulates a generic 1-dimensional physically-based motion
+ * model.
+ *
+ * It performs frame-rate independent interpolation and RK4 integration for
+ * smooth animation with stable, deterministic behavior.
+ * Implementations are expected to subclass and override the Acceleration()
+ * method.
+ */
+class AxisPhysicsModel {
+public:
+ AxisPhysicsModel(double aInitialPosition, double aInitialVelocity);
+ ~AxisPhysicsModel();
+
+ /**
+ * Advance the physics simulation.
+ * |aDelta| is the time since the last sample.
+ */
+ void Simulate(const TimeDuration& aDeltaTime);
+
+ /**
+ * Gets the raw velocity of this axis at this moment.
+ */
+ double GetVelocity();
+
+ /**
+ * Sets the raw velocity of this axis at this moment.
+ */
+ void SetVelocity(double aVelocity);
+
+ /**
+ * Gets the raw position of this axis at this moment.
+ */
+ double GetPosition();
+
+ /**
+ * Sets the raw position of this axis at this moment.
+ */
+ void SetPosition(double aPosition);
+
+protected:
+
+ struct State
+ {
+ State(double ap, double av) : p(ap), v(av) {};
+ double p; // Position
+ double v; // Velocity
+ };
+
+ struct Derivative
+ {
+ Derivative() : dp(0.0), dv(0.0) {};
+ Derivative(double aDp, double aDv) : dp(aDp), dv(aDv) {};
+ double dp; // dp / delta time = Position
+ double dv; // dv / delta time = Velocity
+ };
+
+ /**
+ * Acceleration must be overridden and return the number of
+ * axis-position-units / second that should be added or removed from the
+ * velocity.
+ */
+ virtual double Acceleration(const State &aState) = 0;
+
+private:
+
+ /**
+ * Duration of fixed delta time step (seconds)
+ */
+ static const double kFixedTimestep;
+
+ /**
+ * 0.0 - 1.0 value indicating progress between current and next simulation
+ * sample. Normalized to units of kFixedTimestep duration.
+ */
+ double mProgress;
+
+ /**
+ * Sample of simulation state as it existed
+ * (1.0 - mProgress) * kFixedTimestep seconds in the past.
+ */
+ State mPrevState;
+
+ /**
+ * Sample of simulation state as it will be in mProgress * kFixedTimestep
+ * seconds in the future.
+ */
+ State mNextState;
+
+ /**
+ * Perform RK4 (Runge-Kutta method) Integration to calculate the next
+ * simulation sample.
+ */
+ void Integrate(double aDeltaTime);
+
+ /**
+ * Apply delta velocity and position represented by aDerivative over
+ * aDeltaTime seconds, calculate new acceleration, and return new deltas.
+ */
+ Derivative Evaluate(const State &aInitState, double aDeltaTime,
+ const Derivative &aDerivative);
+
+ /**
+ * Helper function for performing linear interpolation (lerp) of double's
+ */
+ static double LinearInterpolate(double aV1, double aV2, double aBlend);
+
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/BSPTree.cpp b/gfx/layers/BSPTree.cpp
new file mode 100644
index 000000000..e1ed8621a
--- /dev/null
+++ b/gfx/layers/BSPTree.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BSPTree.h"
+#include "mozilla/gfx/Polygon.h"
+
+namespace mozilla {
+namespace layers {
+
+LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers)
+{
+ LayerPolygon layer = Move(aLayers.front());
+ aLayers.pop_front();
+ return layer;
+}
+
+void
+BSPTree::BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
+ nsTArray<LayerPolygon>& aLayers) const
+{
+ const gfx::Point3D& normal = aNode->First().GetNormal();
+
+ UniquePtr<BSPTreeNode> *front = &aNode->front;
+ UniquePtr<BSPTreeNode> *back = &aNode->back;
+
+ // Since the goal is to return the draw order from back to front, we reverse
+ // the traversal order if the current polygon is facing towards the camera.
+ const bool reverseOrder = normal.z > 0.0f;
+
+ if (reverseOrder) {
+ std::swap(front, back);
+ }
+
+ if (*front) {
+ BuildDrawOrder(*front, aLayers);
+ }
+
+ for (LayerPolygon& layer : aNode->layers) {
+ MOZ_ASSERT(layer.geometry);
+
+ if (layer.geometry->GetPoints().Length() >= 3) {
+ aLayers.AppendElement(Move(layer));
+ }
+ }
+
+ if (*back) {
+ BuildDrawOrder(*back, aLayers);
+ }
+}
+
+void
+BSPTree::BuildTree(UniquePtr<BSPTreeNode>& aRoot,
+ std::deque<LayerPolygon>& aLayers)
+{
+ if (aLayers.empty()) {
+ return;
+ }
+
+ const gfx::Polygon3D& plane = aRoot->First();
+ std::deque<LayerPolygon> backLayers, frontLayers;
+
+ for (LayerPolygon& layerPolygon : aLayers) {
+ const Maybe<gfx::Polygon3D>& geometry = layerPolygon.geometry;
+
+ size_t pos = 0, neg = 0;
+ nsTArray<float> dots = geometry->CalculateDotProducts(plane, pos, neg);
+
+ // Back polygon
+ if (pos == 0 && neg > 0) {
+ backLayers.push_back(Move(layerPolygon));
+ }
+ // Front polygon
+ else if (pos > 0 && neg == 0) {
+ frontLayers.push_back(Move(layerPolygon));
+ }
+ // Coplanar polygon
+ else if (pos == 0 && neg == 0) {
+ aRoot->layers.push_back(Move(layerPolygon));
+ }
+ // Polygon intersects with the splitting plane.
+ else if (pos > 0 && neg > 0) {
+ nsTArray<gfx::Point3D> backPoints, frontPoints;
+ geometry->SplitPolygon(plane, dots, backPoints, frontPoints);
+
+ const gfx::Point3D& normal = geometry->GetNormal();
+ Layer *layer = layerPolygon.layer;
+
+ backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
+ frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
+ }
+ }
+
+ if (!backLayers.empty()) {
+ aRoot->back.reset(new BSPTreeNode(PopFront(backLayers)));
+ BuildTree(aRoot->back, backLayers);
+ }
+
+ if (!frontLayers.empty()) {
+ aRoot->front.reset(new BSPTreeNode(PopFront(frontLayers)));
+ BuildTree(aRoot->front, frontLayers);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/BSPTree.h b/gfx/layers/BSPTree.h
new file mode 100644
index 000000000..24a82f2ac
--- /dev/null
+++ b/gfx/layers/BSPTree.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_BSPTREE_H
+#define MOZILLA_LAYERS_BSPTREE_H
+
+#include "mozilla/gfx/Polygon.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+#include <deque>
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+// Represents a layer that might have a non-rectangular geometry.
+struct LayerPolygon {
+ explicit LayerPolygon(Layer *aLayer)
+ : layer(aLayer) {}
+
+ LayerPolygon(Layer *aLayer,
+ gfx::Polygon3D&& aGeometry)
+ : layer(aLayer), geometry(Some(aGeometry)) {}
+
+ LayerPolygon(Layer *aLayer,
+ nsTArray<gfx::Point3D>&& aPoints, const gfx::Point3D& aNormal)
+ : layer(aLayer), geometry(Some(gfx::Polygon3D(Move(aPoints), aNormal))) {}
+
+ Layer *layer;
+ Maybe<gfx::Polygon3D> geometry;
+};
+
+LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers);
+
+// Represents a node in a BSP tree. The node contains at least one layer with
+// associated geometry that is used as a splitting plane, and at most two child
+// nodes that represent the splitting planes that further subdivide the space.
+struct BSPTreeNode {
+ explicit BSPTreeNode(LayerPolygon&& layer)
+ {
+ layers.push_back(Move(layer));
+ }
+
+ const gfx::Polygon3D& First() const
+ {
+ MOZ_ASSERT(layers[0].geometry);
+ return *layers[0].geometry;
+ }
+
+ UniquePtr<BSPTreeNode> front;
+ UniquePtr<BSPTreeNode> back;
+ std::deque<LayerPolygon> layers;
+};
+
+// BSPTree class takes a list of layers as an input and uses binary space
+// partitioning algorithm to create a tree structure that can be used for
+// depth sorting.
+//
+// Sources for more information:
+// https://en.wikipedia.org/wiki/Binary_space_partitioning
+// ftp://ftp.sgi.com/other/bspfaq/faq/bspfaq.html
+class BSPTree {
+public:
+ // This constructor takes the ownership of layers in the given list.
+ explicit BSPTree(std::deque<LayerPolygon>& aLayers)
+ {
+ MOZ_ASSERT(!aLayers.empty());
+ mRoot.reset(new BSPTreeNode(PopFront(aLayers)));
+
+ BuildTree(mRoot, aLayers);
+ }
+
+ // Returns the root node of the BSP tree.
+ const UniquePtr<BSPTreeNode>& GetRoot() const
+ {
+ return mRoot;
+ }
+
+ // Builds and returns the back-to-front draw order for the created BSP tree.
+ nsTArray<LayerPolygon> GetDrawOrder() const
+ {
+ nsTArray<LayerPolygon> layers;
+ BuildDrawOrder(mRoot, layers);
+ return layers;
+ }
+
+private:
+ UniquePtr<BSPTreeNode> mRoot;
+
+ // BuildDrawOrder and BuildTree are called recursively. The depth of the
+ // recursion depends on the amount of polygons and their intersections.
+ void BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
+ nsTArray<LayerPolygon>& aLayers) const;
+ void BuildTree(UniquePtr<BSPTreeNode>& aRoot,
+ std::deque<LayerPolygon>& aLayers);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_LAYERS_BSPTREE_H */
diff --git a/gfx/layers/BufferTexture.cpp b/gfx/layers/BufferTexture.cpp
new file mode 100644
index 000000000..88c59ac90
--- /dev/null
+++ b/gfx/layers/BufferTexture.cpp
@@ -0,0 +1,594 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/fallible.h"
+#include "libyuv.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+class MemoryTextureData : public BufferTextureData
+{
+public:
+ static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ MemoryTextureData(const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ uint8_t* aBuffer, size_t aBufferSize)
+ : BufferTextureData(aDesc, aMoz2DBackend)
+ , mBuffer(aBuffer)
+ , mBufferSize(aBufferSize)
+ {
+ MOZ_ASSERT(aBuffer);
+ MOZ_ASSERT(aBufferSize);
+ }
+
+ virtual uint8_t* GetBuffer() override { return mBuffer; }
+
+ virtual size_t GetBufferSize() override { return mBufferSize; }
+
+protected:
+ uint8_t* mBuffer;
+ size_t mBufferSize;
+};
+
+class ShmemTextureData : public BufferTextureData
+{
+public:
+ static ShmemTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ ShmemTextureData(const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
+ : BufferTextureData(aDesc, aMoz2DBackend)
+ , mShmem(aShmem)
+ {
+ MOZ_ASSERT(mShmem.Size<uint8_t>());
+ }
+
+ virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
+
+ virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
+
+protected:
+ mozilla::ipc::Shmem mShmem;
+};
+
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+ return gfx::gfxVars::UseXRender();
+#endif
+ return false;
+}
+
+bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend)
+{
+ return aLayersBackend != LayersBackend::LAYERS_BASIC
+ || UsingX11Compositor()
+ || aFormat == gfx::SurfaceFormat::UNKNOWN;
+}
+
+BufferTextureData*
+BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator || aAllocator->IsSameProcess()) {
+ return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aFlags,
+ aAllocFlags, aAllocator);
+ } else {
+ return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aFlags,
+ aAllocFlags, aAllocator);
+ }
+}
+
+BufferTextureData*
+BufferTextureData::CreateInternal(LayersIPCChannel* aAllocator,
+ const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ int32_t aBufferSize,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || aAllocator->IsSameProcess()) {
+ uint8_t* buffer = new (fallible) uint8_t[aBufferSize];
+ if (!buffer) {
+ return nullptr;
+ }
+
+ GfxMemoryImageReporter::DidAlloc(buffer);
+
+ return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
+ } else {
+ ipc::Shmem shm;
+ if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
+ return nullptr;
+ }
+
+ return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
+ }
+}
+
+BufferTextureData*
+BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ int32_t aBufferSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ aAllocator->GetCompositorBackendType())
+ : true;
+
+ // Initialize the metadata with something, even if it will have to be rewritten
+ // afterwards since we don't know the dimensions of the texture at this point.
+ BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
+ 0, 0, 0, StereoMode::MONO,
+ aYUVColorSpace,
+ hasIntermediateBuffer);
+
+ return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr,
+ desc, gfx::BackendType::NONE, aBufferSize, aTextureFlags);
+}
+
+BufferTextureData*
+BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
+ if (bufSize == 0) {
+ return nullptr;
+ }
+
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ ImageDataSerializer::ComputeYCbCrOffsets(aYSize.width, aYSize.height,
+ aCbCrSize.width, aCbCrSize.height,
+ yOffset, cbOffset, crOffset);
+
+ bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ aAllocator->GetCompositorBackendType())
+ : true;
+
+ YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
+ crOffset, aStereoMode, aYUVColorSpace,
+ hasIntermediateBuffer);
+
+ return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor,
+ gfx::BackendType::NONE, bufSize, aTextureFlags);
+}
+
+void
+BufferTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = GetSize();
+ aInfo.format = GetFormat();
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = true;
+
+ if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+ aInfo.hasIntermediateBuffer = mDescriptor.get_YCbCrDescriptor().hasIntermediateBuffer();
+ } else {
+ aInfo.hasIntermediateBuffer = mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
+ }
+
+ switch (aInfo.format) {
+ case gfx::SurfaceFormat::YUV:
+ case gfx::SurfaceFormat::UNKNOWN:
+ aInfo.supportsMoz2D = false;
+ break;
+ default:
+ aInfo.supportsMoz2D = true;
+ }
+}
+
+gfx::IntSize
+BufferTextureData::GetSize() const
+{
+ return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<gfx::IntSize>
+BufferTextureData::GetCbCrSize() const
+{
+ return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<YUVColorSpace>
+BufferTextureData::GetYUVColorSpace() const
+{
+ return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
+}
+
+Maybe<StereoMode>
+BufferTextureData::GetStereoMode() const
+{
+ return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
+}
+
+gfx::SurfaceFormat
+BufferTextureData::GetFormat() const
+{
+ return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
+}
+
+already_AddRefed<gfx::DrawTarget>
+BufferTextureData::BorrowDrawTarget()
+{
+ if (mDrawTarget) {
+ mDrawTarget->SetTransform(gfx::Matrix());
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+ }
+
+ if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
+ return nullptr;
+ }
+
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ mDrawTarget = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend,
+ GetBuffer(), rgb.size(),
+ stride, rgb.format(), true);
+
+ if (mDrawTarget) {
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+ }
+
+ // TODO - should we warn? should we really fallback to cairo? perhaps
+ // at least update mMoz2DBackend...
+ if (mMoz2DBackend != gfx::BackendType::CAIRO) {
+ mDrawTarget = gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
+ GetBuffer(), rgb.size(),
+ stride, rgb.format(), true);
+ }
+
+ if (!mDrawTarget) {
+ gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mMoz2DBackend;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+ return dt.forget();
+}
+
+bool
+BufferTextureData::BorrowMappedData(MappedTextureData& aData)
+{
+ if (GetFormat() == gfx::SurfaceFormat::YUV) {
+ return false;
+ }
+
+ gfx::IntSize size = GetSize();
+
+ aData.data = GetBuffer();
+ aData.size = size;
+ aData.format = GetFormat();
+ aData.stride = ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
+
+ return true;
+}
+
+bool
+BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+ if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
+ return false;
+ }
+
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ uint8_t* data = GetBuffer();
+ auto ySize = desc.ySize();
+ auto cbCrSize = desc.cbCrSize();
+
+ aMap.stereoMode = desc.stereoMode();
+ aMap.metadata = nullptr;
+
+ aMap.y.data = data + desc.yOffset();
+ aMap.y.size = ySize;
+ aMap.y.stride = ySize.width;
+ aMap.y.skip = 0;
+
+ aMap.cb.data = data + desc.cbOffset();
+ aMap.cb.size = cbCrSize;
+ aMap.cb.stride = cbCrSize.width;
+ aMap.cb.skip = 0;
+
+ aMap.cr.data = data + desc.crOffset();
+ aMap.cr.size = cbCrSize;
+ aMap.cr.stride = cbCrSize.width;
+ aMap.cr.skip = 0;
+
+ return true;
+}
+
+bool
+BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
+ return false;
+ }
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride,
+ rgb.size(), rgb.format());
+
+ if (!surface) {
+ gfxCriticalError() << "Failed to get serializer as surface!";
+ return false;
+ }
+
+ RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
+ return false;
+ }
+
+ if (surface->GetSize() != srcSurf->GetSize() || surface->GetFormat() != srcSurf->GetFormat()) {
+ gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format (BT)! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+ return false;
+ }
+
+ gfx::DataSourceSurface::MappedSurface sourceMap;
+ gfx::DataSourceSurface::MappedSurface destMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (BT).";
+ return false;
+ }
+
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
+ srcSurf->Unmap();
+ gfxCriticalError() << "Failed to map destination surface for UpdateFromSurface.";
+ return false;
+ }
+
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(destMap.mData + destMap.mStride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ surface->Unmap();
+
+ return true;
+}
+
+void
+BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor)
+{
+ MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
+ mDescriptor = aDescriptor;
+}
+
+bool
+MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+ if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
+ aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
+
+ return true;
+}
+
+static bool
+InitBuffer(uint8_t* buf, size_t bufSize,
+ gfx::SurfaceFormat aFormat, TextureAllocationFlags aAllocFlags,
+ bool aAlreadyZero)
+{
+ if (!buf) {
+ gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
+ return false;
+ }
+
+ if ((aAllocFlags & ALLOC_CLEAR_BUFFER) ||
+ (aAllocFlags & ALLOC_CLEAR_BUFFER_BLACK)) {
+ if (aFormat == gfx::SurfaceFormat::B8G8R8X8) {
+ // Even though BGRX was requested, XRGB_UINT32 is what is meant,
+ // so use 0xFF000000 to put alpha in the right place.
+ libyuv::ARGBRect(buf, bufSize, 0, 0, bufSize / sizeof(uint32_t), 1, 0xFF000000);
+ } else if (!aAlreadyZero) {
+ memset(buf, 0, bufSize);
+ }
+ }
+
+ if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
+ memset(buf, 0xFF, bufSize);
+ }
+
+ return true;
+}
+
+MemoryTextureData*
+MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ // Should have used CreateForYCbCr.
+ MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
+
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+
+ uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
+ if (!bufSize) {
+ return nullptr;
+ }
+
+ uint8_t* buf = new (fallible) uint8_t[bufSize];
+ if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(aFormat, aLayersBackend);
+
+ GfxMemoryImageReporter::DidAlloc(buf);
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
+
+ return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
+}
+
+void
+MemoryTextureData::Deallocate(LayersIPCChannel*)
+{
+ MOZ_ASSERT(mBuffer);
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ delete [] mBuffer;
+ mBuffer = nullptr;
+}
+
+TextureData*
+MemoryTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
+ aLayersBackend, aFlags, aAllocFlags, aAllocator);
+}
+
+bool
+ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+ if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(mShmem));
+
+ return true;
+}
+
+ShmemTextureData*
+ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ // Should have used CreateForYCbCr.
+ MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
+
+ if (!aAllocator) {
+ return nullptr;
+ }
+
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+
+ uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
+ if (!bufSize) {
+ return nullptr;
+ }
+
+ mozilla::ipc::Shmem shm;
+ if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
+ return nullptr;
+ }
+
+ uint8_t* buf = shm.get<uint8_t>();
+ if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) {
+ return nullptr;
+ }
+
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(aFormat, aLayersBackend);
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
+
+ return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
+
+ return nullptr;
+}
+
+TextureData*
+ShmemTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
+ aLayersBackend, aFlags, aAllocFlags, aAllocator);
+}
+
+void
+ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ aAllocator->DeallocShmem(mShmem);
+}
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/BufferTexture.h b/gfx/layers/BufferTexture.h
new file mode 100644
index 000000000..2999d1d94
--- /dev/null
+++ b/gfx/layers/BufferTexture.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_BUFFERETEXTURE
+#define MOZILLA_LAYERS_BUFFERETEXTURE
+
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend);
+
+class BufferTextureData : public TextureData
+{
+public:
+ static BufferTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags,
+ LayersIPCChannel* aAllocator);
+
+ static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // It is generally better to use CreateForYCbCr instead.
+ // This creates a half-initialized texture since we don't know the sizes and
+ // offsets in the buffer.
+ static BufferTextureData* CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ int32_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ virtual bool Lock(OpenMode aMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool BorrowMappedData(MappedTextureData& aMap) override;
+
+ virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
+
+ // use TextureClient's default implementation
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ virtual BufferTextureData* AsBufferTextureData() override { return this; }
+
+ // Don't use this.
+ void SetDesciptor(const BufferDescriptor& aDesc);
+
+ Maybe<gfx::IntSize> GetCbCrSize() const;
+
+ Maybe<YUVColorSpace> GetYUVColorSpace() const;
+
+ Maybe<StereoMode> GetStereoMode() const;
+
+protected:
+ gfx::IntSize GetSize() const;
+
+ gfx::SurfaceFormat GetFormat() const;
+
+ static BufferTextureData* CreateInternal(LayersIPCChannel* aAllocator,
+ const BufferDescriptor& aDesc,
+ gfx::BackendType aMoz2DBackend,
+ int32_t aBufferSize,
+ TextureFlags aTextureFlags);
+
+ virtual uint8_t* GetBuffer() = 0;
+ virtual size_t GetBufferSize() = 0;
+
+ BufferTextureData(const BufferDescriptor& aDescriptor, gfx::BackendType aMoz2DBackend)
+ : mDescriptor(aDescriptor)
+ , mMoz2DBackend(aMoz2DBackend)
+ {}
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ BufferDescriptor mDescriptor;
+ gfx::BackendType mMoz2DBackend;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/layers/BufferUnrotate.cpp b/gfx/layers/BufferUnrotate.cpp
new file mode 100644
index 000000000..224818773
--- /dev/null
+++ b/gfx/layers/BufferUnrotate.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm> // min & max
+#include <cstdlib>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
+ int aByteStride, int aXBoundary, int aYBoundary)
+{
+ if (aXBoundary != 0) {
+ uint8_t* line = new uint8_t[aByteWidth];
+ uint32_t smallStart = 0;
+ uint32_t smallLen = aXBoundary;
+ uint32_t smallDest = aByteWidth - aXBoundary;
+ uint32_t largeStart = aXBoundary;
+ uint32_t largeLen = aByteWidth - aXBoundary;
+ uint32_t largeDest = 0;
+ if (aXBoundary > aByteWidth / 2) {
+ smallStart = aXBoundary;
+ smallLen = aByteWidth - aXBoundary;
+ smallDest = 0;
+ largeStart = 0;
+ largeLen = aXBoundary;
+ largeDest = smallLen;
+ }
+
+ for (int y = 0; y < aHeight; y++) {
+ int yOffset = y * aByteStride;
+ memcpy(line, &aBuffer[yOffset + smallStart], smallLen);
+ memmove(&aBuffer[yOffset + largeDest], &aBuffer[yOffset + largeStart], largeLen);
+ memcpy(&aBuffer[yOffset + smallDest], line, smallLen);
+ }
+
+ delete[] line;
+ }
+
+ if (aYBoundary != 0) {
+ uint32_t smallestHeight = std::min(aHeight - aYBoundary, aYBoundary);
+ uint32_t largestHeight = std::max(aHeight - aYBoundary, aYBoundary);
+ uint32_t smallOffset = 0;
+ uint32_t largeOffset = aYBoundary * aByteStride;
+ uint32_t largeDestOffset = 0;
+ uint32_t smallDestOffset = largestHeight * aByteStride;
+ if (aYBoundary > aHeight / 2) {
+ smallOffset = aYBoundary * aByteStride;
+ largeOffset = 0;
+ largeDestOffset = smallestHeight * aByteStride;
+ smallDestOffset = 0;
+ }
+
+ uint8_t* smallestSide = new uint8_t[aByteStride * smallestHeight];
+ memcpy(smallestSide, &aBuffer[smallOffset], aByteStride * smallestHeight);
+ memmove(&aBuffer[largeDestOffset], &aBuffer[largeOffset], aByteStride * largestHeight);
+ memcpy(&aBuffer[smallDestOffset], smallestSide, aByteStride * smallestHeight);
+ delete[] smallestSide;
+ }
+}
+
diff --git a/gfx/layers/BufferUnrotate.h b/gfx/layers/BufferUnrotate.h
new file mode 100644
index 000000000..a292d02fc
--- /dev/null
+++ b/gfx/layers/BufferUnrotate.h
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BUFFERUNROTATE_H
+#define GFX_BUFFERUNROTATE_H
+
+#include "mozilla/Types.h"
+
+void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
+ int aByteStride, int aXByteBoundary, int aYBoundary);
+
+#endif // GFX_BUFFERUNROTATE_H
diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp
new file mode 100644
index 000000000..ce7eb9008
--- /dev/null
+++ b/gfx/layers/Compositor.cpp
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/Compositor.h"
+#include "base/message_loop.h" // for MessageLoop
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "gfx2DGlue.h"
+#include "nsAppRunner.h"
+
+namespace mozilla {
+
+namespace layers {
+
+Compositor::Compositor(widget::CompositorWidget* aWidget,
+ CompositorBridgeParent* aParent)
+ : mCompositorID(0)
+ , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
+ , mParent(aParent)
+ , mPixelsPerFrame(0)
+ , mPixelsFilled(0)
+ , mScreenRotation(ROTATION_0)
+ , mWidget(aWidget)
+ , mIsDestroyed(false)
+#if defined(MOZ_WIDGET_ANDROID)
+ // If the default color isn't white for Fennec, there is a black
+ // flash before the first page of a tab is loaded.
+ , mClearColor(1.0, 1.0, 1.0, 1.0)
+ , mDefaultClearColor(1.0, 1.0, 1.0, 1.0)
+#else
+ , mClearColor(0.0, 0.0, 0.0, 0.0)
+ , mDefaultClearColor(0.0, 0.0, 0.0, 0.0)
+#endif
+{
+}
+
+Compositor::~Compositor()
+{
+ ReadUnlockTextures();
+}
+
+void
+Compositor::Destroy()
+{
+ ReadUnlockTextures();
+ FlushPendingNotifyNotUsed();
+ mIsDestroyed = true;
+}
+
+void
+Compositor::EndFrame()
+{
+ ReadUnlockTextures();
+ mLastCompositionEndTime = TimeStamp::Now();
+}
+
+void
+Compositor::ReadUnlockTextures()
+{
+ for (auto& texture : mUnlockAfterComposition) {
+ texture->ReadUnlock();
+ }
+ mUnlockAfterComposition.Clear();
+}
+
+void
+Compositor::UnlockAfterComposition(TextureHost* aTexture)
+{
+ mUnlockAfterComposition.AppendElement(aTexture);
+}
+
+void
+Compositor::NotifyNotUsedAfterComposition(TextureHost* aTextureHost)
+{
+ MOZ_ASSERT(!mIsDestroyed);
+
+ mNotifyNotUsedAfterComposition.AppendElement(aTextureHost);
+
+ // If Compositor holds many TextureHosts without compositing,
+ // the TextureHosts should be flushed to reduce memory consumption.
+ const int thresholdCount = 5;
+ const double thresholdSec = 2.0f;
+ if (mNotifyNotUsedAfterComposition.Length() > thresholdCount) {
+ TimeDuration duration = mLastCompositionEndTime ? TimeStamp::Now() - mLastCompositionEndTime : TimeDuration();
+ // Check if we could flush
+ if (duration.ToSeconds() > thresholdSec) {
+ FlushPendingNotifyNotUsed();
+ }
+ }
+}
+
+void
+Compositor::FlushPendingNotifyNotUsed()
+{
+ for (auto& textureHost : mNotifyNotUsedAfterComposition) {
+ textureHost->CallNotifyNotUsed();
+ }
+ mNotifyNotUsedAfterComposition.Clear();
+}
+
+/* static */ void
+Compositor::AssertOnCompositorThread()
+{
+ MOZ_ASSERT(!CompositorThreadHolder::Loop() ||
+ CompositorThreadHolder::Loop() == MessageLoop::current(),
+ "Can only call this from the compositor thread!");
+}
+
+bool
+Compositor::ShouldDrawDiagnostics(DiagnosticFlags aFlags)
+{
+ if ((aFlags & DiagnosticFlags::TILE) && !(mDiagnosticTypes & DiagnosticTypes::TILE_BORDERS)) {
+ return false;
+ }
+ if ((aFlags & DiagnosticFlags::BIGIMAGE) &&
+ !(mDiagnosticTypes & DiagnosticTypes::BIGIMAGE_BORDERS)) {
+ return false;
+ }
+ if (mDiagnosticTypes == DiagnosticTypes::NO_DIAGNOSTIC) {
+ return false;
+ }
+ return true;
+}
+
+void
+Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
+ const nsIntRegion& aVisibleRegion,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+ if (!ShouldDrawDiagnostics(aFlags)) {
+ return;
+ }
+
+ if (aVisibleRegion.GetNumRects() > 1) {
+ for (auto iter = aVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ DrawDiagnostics(aFlags | DiagnosticFlags::REGION_RECT,
+ IntRectToRect(iter.Get()), aClipRect, aTransform,
+ aFlashCounter);
+ }
+ }
+
+ DrawDiagnostics(aFlags, IntRectToRect(aVisibleRegion.GetBounds()),
+ aClipRect, aTransform, aFlashCounter);
+}
+
+void
+Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+ if (!ShouldDrawDiagnostics(aFlags)) {
+ return;
+ }
+
+ DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
+ aFlashCounter);
+}
+
+void
+Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ uint32_t aFlashCounter)
+{
+#ifdef MOZ_B2G
+ int lWidth = 4;
+#elif defined(ANDROID)
+ int lWidth = 10;
+#else
+ int lWidth = 2;
+#endif
+
+ gfx::Color color;
+ if (aFlags & DiagnosticFlags::CONTENT) {
+ color = gfx::Color(0.0f, 1.0f, 0.0f, 1.0f); // green
+ if (aFlags & DiagnosticFlags::COMPONENT_ALPHA) {
+ color = gfx::Color(0.0f, 1.0f, 1.0f, 1.0f); // greenish blue
+ }
+ } else if (aFlags & DiagnosticFlags::IMAGE) {
+ if (aFlags & DiagnosticFlags::NV12) {
+ color = gfx::Color(1.0f, 1.0f, 0.0f, 1.0f); // yellow
+ } else if (aFlags & DiagnosticFlags::YCBCR) {
+ color = gfx::Color(1.0f, 0.55f, 0.0f, 1.0f); // orange
+ } else {
+ color = gfx::Color(1.0f, 0.0f, 0.0f, 1.0f); // red
+ }
+ } else if (aFlags & DiagnosticFlags::COLOR) {
+ color = gfx::Color(0.0f, 0.0f, 1.0f, 1.0f); // blue
+ } else if (aFlags & DiagnosticFlags::CONTAINER) {
+ color = gfx::Color(0.8f, 0.0f, 0.8f, 1.0f); // purple
+ }
+
+ // make tile borders a bit more transparent to keep layer borders readable.
+ if (aFlags & DiagnosticFlags::TILE ||
+ aFlags & DiagnosticFlags::BIGIMAGE ||
+ aFlags & DiagnosticFlags::REGION_RECT) {
+ lWidth = 1;
+ color.r *= 0.7f;
+ color.g *= 0.7f;
+ color.b *= 0.7f;
+ color.a = color.a * 0.5f;
+ } else {
+ color.a = color.a * 0.7f;
+ }
+
+
+ if (mDiagnosticTypes & DiagnosticTypes::FLASH_BORDERS) {
+ float flash = (float)aFlashCounter / (float)DIAGNOSTIC_FLASH_COUNTER_MAX;
+ color.r *= flash;
+ color.g *= flash;
+ color.b *= flash;
+ }
+
+ SlowDrawRect(aVisibleRect, color, aClipRect, aTransform, lWidth);
+}
+
+static void
+UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aIntersection,
+ gfx::Rect aTextureCoords)
+{
+ // Calculate the relative offset of the intersection within the layer.
+ float dx = (aIntersection.x - aRect.x) / aRect.width;
+ float dy = (aIntersection.y - aRect.y) / aRect.height;
+
+ // Update the texture offset.
+ float x = aTextureCoords.x + dx * aTextureCoords.width;
+ float y = aTextureCoords.y + dy * aTextureCoords.height;
+
+ // Scale the texture width and height.
+ float w = aTextureCoords.width * aIntersection.width / aRect.width;
+ float h = aTextureCoords.height * aIntersection.height / aRect.height;
+
+ static const auto ValidateAndClamp = [](float& f) {
+ // Allow some numerical inaccuracy.
+ MOZ_ASSERT(f >= -0.0001f && f <= 1.0001f);
+
+ if (f >= 1.0f) f = 1.0f;
+ if (f <= 0.0f) f = 0.0f;
+ };
+
+ auto UpdatePoint = [&](const gfx::Point& p, gfx::Point& t)
+ {
+ t.x = x + (p.x - aIntersection.x) / aIntersection.width * w;
+ t.y = y + (p.y - aIntersection.y) / aIntersection.height * h;
+
+ ValidateAndClamp(t.x);
+ ValidateAndClamp(t.y);
+ };
+
+ UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
+ UpdatePoint(aTriangle.p2, aTriangle.textureCoords.p2);
+ UpdatePoint(aTriangle.p3, aTriangle.textureCoords.p3);
+}
+
+void
+Compositor::DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon3D>& aGeometry)
+{
+ if (!aGeometry) {
+ DrawQuad(aRect, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+ return;
+ }
+
+ // Cull invisible polygons.
+ if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
+ return;
+ }
+
+ gfx::Polygon3D clipped = aGeometry->ClipPolygon(aRect);
+ nsTArray<gfx::Triangle> triangles = clipped.ToTriangles();
+
+ for (gfx::Triangle& geometry : triangles) {
+ const gfx::Rect intersection = aRect.Intersect(geometry.BoundingBox());
+
+ // Cull invisible triangles.
+ if (intersection.IsEmpty()) {
+ continue;
+ }
+
+ MOZ_ASSERT(aRect.width > 0.0f && aRect.height > 0.0f);
+ MOZ_ASSERT(intersection.width > 0.0f && intersection.height > 0.0f);
+
+ gfx::TexturedTriangle triangle(Move(geometry));
+ triangle.width = aRect.width;
+ triangle.height = aRect.height;
+
+ // Since the texture was created for non-split geometry, we need to
+ // update the texture coordinates to account for the split.
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB) {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ UpdateTextureCoordinates(triangle, aRect, intersection,
+ texturedEffect->mTextureCoords);
+ }
+
+ DrawTriangle(triangle, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+ }
+}
+
+void
+Compositor::SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& aColor,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform, int aStrokeWidth)
+{
+ // TODO This should draw a rect using a single draw call but since
+ // this is only used for debugging overlays it's not worth optimizing ATM.
+ float opacity = 1.0f;
+ EffectChain effects;
+
+ effects.mPrimaryEffect = new EffectSolidColor(aColor);
+ // left
+ this->DrawQuad(gfx::Rect(aRect.x, aRect.y,
+ aStrokeWidth, aRect.height),
+ aClipRect, effects, opacity,
+ aTransform);
+ // top
+ this->DrawQuad(gfx::Rect(aRect.x + aStrokeWidth, aRect.y,
+ aRect.width - 2 * aStrokeWidth, aStrokeWidth),
+ aClipRect, effects, opacity,
+ aTransform);
+ // right
+ this->DrawQuad(gfx::Rect(aRect.x + aRect.width - aStrokeWidth, aRect.y,
+ aStrokeWidth, aRect.height),
+ aClipRect, effects, opacity,
+ aTransform);
+ // bottom
+ this->DrawQuad(gfx::Rect(aRect.x + aStrokeWidth, aRect.y + aRect.height - aStrokeWidth,
+ aRect.width - 2 * aStrokeWidth, aStrokeWidth),
+ aClipRect, effects, opacity,
+ aTransform);
+}
+
+void
+Compositor::FillRect(const gfx::Rect& aRect, const gfx::Color& aColor,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform)
+{
+ float opacity = 1.0f;
+ EffectChain effects;
+
+ effects.mPrimaryEffect = new EffectSolidColor(aColor);
+ this->DrawQuad(aRect,
+ aClipRect, effects, opacity,
+ aTransform);
+}
+
+
+static float
+WrapTexCoord(float v)
+{
+ // This should return values in range [0, 1.0)
+ return v - floorf(v);
+}
+
+static void
+SetRects(size_t n,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects,
+ float x0, float y0, float x1, float y1,
+ float tx0, float ty0, float tx1, float ty1,
+ bool flip_y)
+{
+ if (flip_y) {
+ std::swap(ty0, ty1);
+ }
+ (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
+ (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
+}
+
+#ifdef DEBUG
+static inline bool
+FuzzyEqual(float a, float b)
+{
+ return fabs(a - b) < 0.0001f;
+}
+static inline bool
+FuzzyLTE(float a, float b)
+{
+ return a <= b + 0.0001f;
+}
+#endif
+
+size_t
+DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects)
+{
+ gfx::Rect texCoordRect = aTexCoordRect;
+
+ // If the texture should be flipped, it will have negative height. Detect that
+ // here and compensate for it. We will flip each rect as we emit it.
+ bool flipped = false;
+ if (texCoordRect.height < 0) {
+ flipped = true;
+ texCoordRect.y += texCoordRect.height;
+ texCoordRect.height = -texCoordRect.height;
+ }
+
+ // Wrap the texture coordinates so they are within [0,1] and cap width/height
+ // at 1. We rely on this below.
+ texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x),
+ WrapTexCoord(texCoordRect.y)),
+ gfx::Size(std::min(texCoordRect.width, 1.0f),
+ std::min(texCoordRect.height, 1.0f)));
+
+ NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
+ texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
+ texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
+ texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
+ texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
+ texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
+ "We just wrapped the texture coordinates, didn't we?");
+
+ // Get the top left and bottom right points of the rectangle. Note that
+ // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
+ gfx::Point tl = texCoordRect.TopLeft();
+ gfx::Point br = texCoordRect.BottomRight();
+
+ NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
+ tl.y >= 0.0f && tl.y <= 1.0f &&
+ br.x >= tl.x && br.x <= 2.0f &&
+ br.y >= tl.y && br.y <= 2.0f &&
+ FuzzyLTE(br.x - tl.x, 1.0f) &&
+ FuzzyLTE(br.y - tl.y, 1.0f),
+ "Somehow generated invalid texture coordinates");
+
+ // Then check if we wrap in either the x or y axis.
+ bool xwrap = br.x > 1.0f;
+ bool ywrap = br.y > 1.0f;
+
+ // If xwrap is false, the texture will be sampled from tl.x .. br.x.
+ // If xwrap is true, then it will be split into tl.x .. 1.0, and
+ // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
+ // rectangle is also split appropriately, according to the calculated
+ // xmid/ymid values.
+ if (!xwrap && !ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
+ tl.x, tl.y, br.x, br.y,
+ flipped);
+ return 1;
+ }
+
+ // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
+ // wrap them here as well.
+ br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
+ ywrap ? WrapTexCoord(br.y) : br.y);
+
+ // If we wrap around along the x axis, we will draw first from
+ // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
+ // The same applies for the Y axis. The midpoints we calculate here are
+ // only valid if we actually wrap around.
+ GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
+ GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
+
+ // Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y
+ // to calculate width and height, respectively, to ensure that size will
+ // remain consistent going from absolute to relative and back again.
+ NS_ASSERTION(!xwrap ||
+ (xmid >= aRect.x &&
+ xmid <= aRect.XMost() &&
+ FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.XMost() - aRect.x)),
+ "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
+ NS_ASSERTION(!ywrap ||
+ (ymid >= aRect.y &&
+ ymid <= aRect.YMost() &&
+ FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.YMost() - aRect.y)),
+ "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
+
+ if (!xwrap && ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, aRect.XMost(), ymid,
+ tl.x, tl.y, br.x, 1.0f,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ aRect.x, ymid, aRect.XMost(), aRect.YMost(),
+ tl.x, 0.0f, br.x, br.y,
+ flipped);
+ return 2;
+ }
+
+ if (xwrap && !ywrap) {
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, xmid, aRect.YMost(),
+ tl.x, tl.y, 1.0f, br.y,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ xmid, aRect.y, aRect.XMost(), aRect.YMost(),
+ 0.0f, tl.y, br.x, br.y,
+ flipped);
+ return 2;
+ }
+
+ SetRects(0, aLayerRects, aTextureRects,
+ aRect.x, aRect.y, xmid, ymid,
+ tl.x, tl.y, 1.0f, 1.0f,
+ flipped);
+ SetRects(1, aLayerRects, aTextureRects,
+ xmid, aRect.y, aRect.XMost(), ymid,
+ 0.0f, tl.y, br.x, 1.0f,
+ flipped);
+ SetRects(2, aLayerRects, aTextureRects,
+ aRect.x, ymid, xmid, aRect.YMost(),
+ tl.x, 0.0f, 1.0f, br.y,
+ flipped);
+ SetRects(3, aLayerRects, aTextureRects,
+ xmid, ymid, aRect.XMost(), aRect.YMost(),
+ 0.0f, 0.0f, br.x, br.y,
+ flipped);
+ return 4;
+}
+
+gfx::IntRect
+Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad)
+{
+ // Compute the clip.
+ gfx::IntPoint rtOffset = GetCurrentRenderTarget()->GetOrigin();
+ gfx::IntSize rtSize = GetCurrentRenderTarget()->GetSize();
+
+ gfx::IntRect renderBounds(0, 0, rtSize.width, rtSize.height);
+ renderBounds.IntersectRect(renderBounds, aClipRect);
+ renderBounds.MoveBy(rtOffset);
+
+ // Apply the layer transform.
+ gfx::RectDouble dest = aTransform.TransformAndClipBounds(
+ gfx::RectDouble(aRect.x, aRect.y, aRect.width, aRect.height),
+ gfx::RectDouble(renderBounds.x, renderBounds.y, renderBounds.width, renderBounds.height));
+ dest -= rtOffset;
+
+ // Ensure we don't round out to -1, which trips up Direct3D.
+ dest.IntersectRect(dest, gfx::RectDouble(0, 0, rtSize.width, rtSize.height));
+
+ if (aOutLayerQuad) {
+ *aOutLayerQuad = gfx::Rect(dest.x, dest.y, dest.width, dest.height);
+ }
+
+ // Round out to integer.
+ gfx::IntRect result;
+ dest.RoundOut();
+ dest.ToIntRect(&result);
+
+ // Create a transform from adjusted clip space to render target space,
+ // translate it for the backdrop rect, then transform it into the backdrop's
+ // uv-space.
+ gfx::Matrix4x4 transform;
+ transform.PostScale(rtSize.width, rtSize.height, 1.0);
+ transform.PostTranslate(-result.x, -result.y, 0.0);
+ transform.PostScale(1 / float(result.width), 1 / float(result.height), 1.0);
+ *aOutTransform = transform;
+ return result;
+}
+
+gfx::IntRect
+Compositor::ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad)
+{
+ gfx::Rect boundingBox = aTriangle.BoundingBox();
+ return ComputeBackdropCopyRect(boundingBox, aClipRect, aTransform,
+ aOutTransform, aOutLayerQuad);
+}
+
+void
+Compositor::SetInvalid()
+{
+ mParent = nullptr;
+}
+
+bool
+Compositor::IsValid() const
+{
+ return !!mParent;
+}
+
+void
+Compositor::SetDispAcquireFence(Layer* aLayer)
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h
new file mode 100644
index 000000000..f7aecb0f1
--- /dev/null
+++ b/gfx/layers/Compositor.h
@@ -0,0 +1,726 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITOR_H
+#define MOZILLA_GFX_COMPOSITOR_H
+
+#include "Units.h" // for ScreenPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Polygon.h" // for Polygon3D
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float
+#include "mozilla/gfx/Triangle.h" // for Triangle, TexturedTriangle
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticTypes, etc
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h"
+#include <vector>
+#include "mozilla/WidgetUtils.h"
+
+/**
+ * Different elements of a web pages are rendered into separate "layers" before
+ * they are flattened into the final image that is brought to the screen.
+ * See Layers.h for more informations about layers and why we use retained
+ * structures.
+ * Most of the documentation for layers is directly in the source code in the
+ * form of doc comments. An overview can also be found in the the wiki:
+ * https://wiki.mozilla.org/Gecko:Overview#Graphics
+ *
+ *
+ * # Main interfaces and abstractions
+ *
+ * - Layer, ShadowableLayer and LayerComposite
+ * (see Layers.h and ipc/ShadowLayers.h)
+ * - CompositableClient and CompositableHost
+ * (client/CompositableClient.h composite/CompositableHost.h)
+ * - TextureClient and TextureHost
+ * (client/TextureClient.h composite/TextureHost.h)
+ * - TextureSource
+ * (composite/TextureHost.h)
+ * - Forwarders
+ * (ipc/CompositableForwarder.h ipc/ShadowLayers.h)
+ * - Compositor
+ * (this file)
+ * - IPDL protocols
+ * (.ipdl files under the gfx/layers/ipc directory)
+ *
+ * The *Client and Shadowable* classes are always used on the content thread.
+ * Forwarders are always used on the content thread.
+ * The *Host and Shadow* classes are always used on the compositor thread.
+ * Compositors, TextureSource, and Effects are always used on the compositor
+ * thread.
+ * Most enums and constants are declared in LayersTypes.h and CompositorTypes.h.
+ *
+ *
+ * # Texture transfer
+ *
+ * Most layer classes own a Compositable plus some extra information like
+ * transforms and clip rects. They are platform independent.
+ * Compositable classes manipulate Texture objects and are reponsible for
+ * things like tiling, buffer rotation or double buffering. Compositables
+ * are also platform-independent. Examples of compositable classes are:
+ * - ImageClient
+ * - CanvasClient
+ * - ContentHost
+ * - etc.
+ * Texture classes (TextureClient and TextureHost) are thin abstractions over
+ * platform-dependent texture memory. They are maniplulated by compositables
+ * and don't know about buffer rotations and such. The purposes of TextureClient
+ * and TextureHost are to synchronize, serialize and deserialize texture data.
+ * TextureHosts provide access to TextureSources that are views on the
+ * Texture data providing the necessary api for Compositor backend to composite
+ * them.
+ *
+ * Compositable and Texture clients and hosts are created using factory methods.
+ * They should only be created by using their constructor in exceptional
+ * circumstances. The factory methods are located:
+ * TextureClient - CompositableClient::CreateTextureClient
+ * TextureHost - TextureHost::CreateTextureHost, which calls a
+ * platform-specific function, e.g., CreateTextureHostOGL
+ * CompositableClient - in the appropriate subclass, e.g.,
+ * CanvasClient::CreateCanvasClient
+ * CompositableHost - CompositableHost::Create
+ *
+ *
+ * # IPDL
+ *
+ * If off-main-thread compositing (OMTC) is enabled, compositing is performed
+ * in a dedicated thread. In some setups compositing happens in a dedicated
+ * process. Documentation may refer to either the compositor thread or the
+ * compositor process.
+ * See explanations in ShadowLayers.h.
+ *
+ *
+ * # Backend implementations
+ *
+ * Compositor backends like OpenGL or flavours of D3D live in their own directory
+ * under gfx/layers/. To add a new backend, implement at least the following
+ * interfaces:
+ * - Compositor (ex. CompositorOGL)
+ * - TextureHost (ex. SurfaceTextureHost)
+ * Depending on the type of data that needs to be serialized, you may need to
+ * add specific TextureClient implementations.
+ */
+
+class nsIWidget;
+
+namespace mozilla {
+namespace gfx {
+class Matrix;
+class DrawTarget;
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+struct Effect;
+struct EffectChain;
+class Image;
+class ImageHostOverlay;
+class Layer;
+class TextureSource;
+class DataTextureSource;
+class CompositingRenderTarget;
+class CompositorBridgeParent;
+class LayerManagerComposite;
+class CompositorOGL;
+class CompositorD3D9;
+class CompositorD3D11;
+class BasicCompositor;
+class TextureHost;
+class TextureReadLock;
+
+enum SurfaceInitMode
+{
+ INIT_MODE_NONE,
+ INIT_MODE_CLEAR
+};
+
+/**
+ * Common interface for compositor backends.
+ *
+ * Compositor provides a cross-platform interface to a set of operations for
+ * compositing quads. Compositor knows nothing about the layer tree. It must be
+ * told everything about each composited quad - contents, location, transform,
+ * opacity, etc.
+ *
+ * In theory it should be possible for different widgets to use the same
+ * compositor. In practice, we use one compositor per window.
+ *
+ * # Usage
+ *
+ * For an example of a user of Compositor, see LayerManagerComposite.
+ *
+ * Initialization: create a Compositor object, call Initialize().
+ *
+ * Destruction: destroy any resources associated with the compositor, call
+ * Destroy(), delete the Compositor object.
+ *
+ * Composition:
+ * call BeginFrame,
+ * for each quad to be composited:
+ * call MakeCurrent if necessary (not necessary if no other context has been
+ * made current),
+ * take care of any texture upload required to composite the quad, this step
+ * is backend-dependent,
+ * construct an EffectChain for the quad,
+ * call DrawQuad,
+ * call EndFrame.
+ * If the compositor is usually used for compositing but compositing is
+ * temporarily done without the compositor, call EndFrameForExternalComposition
+ * after compositing each frame so the compositor can remain internally
+ * consistent.
+ *
+ * By default, the compositor will render to the screen, to render to a target,
+ * call SetTargetContext or SetRenderTarget, the latter with a target created
+ * by CreateRenderTarget or CreateRenderTargetFromSource.
+ *
+ * The target and viewport methods can be called before any DrawQuad call and
+ * affect any subsequent DrawQuad calls.
+ */
+class Compositor
+{
+protected:
+ virtual ~Compositor();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(Compositor)
+
+ explicit Compositor(widget::CompositorWidget* aWidget,
+ CompositorBridgeParent* aParent = nullptr);
+
+ virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) { return nullptr; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) = 0;
+ virtual void Destroy();
+ bool IsDestroyed() const { return mIsDestroyed; }
+
+ virtual void DetachWidget() { mWidget = nullptr; }
+
+ /**
+ * Return true if the effect type is supported.
+ *
+ * By default Compositor implementations should support all effects but in
+ * some rare cases it is not possible to support an effect efficiently.
+ * This is the case for BasicCompositor with EffectYCbCr.
+ */
+ virtual bool SupportsEffect(EffectTypes aEffect) { return true; }
+
+ /**
+ * Request a texture host identifier that may be used for creating textures
+ * across process or thread boundaries that are compatible with this
+ * compositor.
+ */
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() = 0;
+
+ /**
+ * Properties of the compositor.
+ */
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) = 0;
+ virtual int32_t GetMaxTextureSize() const = 0;
+
+ /**
+ * Set the target for rendering. Results will have been written to aTarget by
+ * the time that EndFrame returns.
+ *
+ * If this method is not used, or we pass in nullptr, we target the compositor's
+ * usual swap chain and render to the screen.
+ */
+ void SetTargetContext(gfx::DrawTarget* aTarget, const gfx::IntRect& aRect)
+ {
+ mTarget = aTarget;
+ mTargetBounds = aRect;
+ }
+ gfx::DrawTarget* GetTargetContext() const
+ {
+ return mTarget;
+ }
+ void ClearTargetContext()
+ {
+ mTarget = nullptr;
+ }
+
+ typedef uint32_t MakeCurrentFlags;
+ static const MakeCurrentFlags ForceMakeCurrent = 0x1;
+ /**
+ * Make this compositor's rendering context the current context for the
+ * underlying graphics API. This may be a global operation, depending on the
+ * API. Our context will remain the current one until someone else changes it.
+ *
+ * Clients of the compositor should call this at the start of the compositing
+ * process, it might be required by texture uploads etc.
+ *
+ * If aFlags == ForceMakeCurrent then we will (re-)set our context on the
+ * underlying API even if it is already the current context.
+ */
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) = 0;
+
+ /**
+ * Creates a Surface that can be used as a rendering target by this
+ * compositor.
+ */
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect& aRect, SurfaceInitMode aInit) = 0;
+
+ /**
+ * Creates a Surface that can be used as a rendering target by this
+ * compositor, and initializes the surface by copying from aSource.
+ * If aSource is null, then the current screen buffer is used as source.
+ *
+ * aSourcePoint specifies the point in aSource to copy data from.
+ */
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) = 0;
+
+ /**
+ * Sets the given surface as the target for subsequent calls to DrawQuad.
+ * Passing null as aSurface sets the screen as the target.
+ */
+ virtual void SetRenderTarget(CompositingRenderTarget* aSurface) = 0;
+
+ /**
+ * Returns the current target for rendering. Will return null if we are
+ * rendering to the screen.
+ */
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const = 0;
+
+ /**
+ * Mostly the compositor will pull the size from a widget and this method will
+ * be ignored, but compositor implementations are free to use it if they like.
+ */
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) = 0;
+
+ /**
+ * Declare an offset to use when rendering layers. This will be ignored when
+ * rendering to a target instead of the screen.
+ */
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
+
+ void DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect,
+ const Maybe<gfx::Polygon3D>& aGeometry);
+
+ void DrawGeometry(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const Maybe<gfx::Polygon3D>& aGeometry)
+ {
+ DrawGeometry(aRect, aClipRect, aEffectChain, aOpacity,
+ aTransform, aRect, aGeometry);
+ }
+
+ /**
+ * Tell the compositor to draw a quad. What to do draw and how it is
+ * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
+ * aTransform transforms from user space to screen space. If texture coords are
+ * required, these will be in the primary effect in the effect chain.
+ * aVisibleRect is used to determine which edges should be antialiased,
+ * without applying the effect to the inner edges of a tiled layer.
+ */
+ virtual void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) = 0;
+
+ /**
+ * Overload of DrawQuad, with aVisibleRect defaulted to the value of aRect.
+ * Use this when you are drawing a single quad that is not part of a tiled
+ * layer.
+ */
+ void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform) {
+ DrawQuad(aRect, aClipRect, aEffectChain, aOpacity, aTransform, aRect);
+ }
+
+ virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+ {
+ MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
+ }
+
+ /**
+ * Draw an unfilled solid color rect. Typically used for debugging overlays.
+ */
+ void SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& color,
+ const gfx::IntRect& aClipRect = gfx::IntRect(),
+ const gfx::Matrix4x4& aTransform = gfx::Matrix4x4(),
+ int aStrokeWidth = 1);
+
+ /**
+ * Draw a solid color filled rect. This is a simple DrawQuad helper.
+ */
+ void FillRect(const gfx::Rect& aRect, const gfx::Color& color,
+ const gfx::IntRect& aClipRect = gfx::IntRect(),
+ const gfx::Matrix4x4& aTransform = gfx::Matrix4x4());
+
+ void SetClearColor(const gfx::Color& aColor) {
+ mClearColor = aColor;
+ }
+
+ void SetDefaultClearColor(const gfx::Color& aColor) {
+ mDefaultClearColor = aColor;
+ }
+
+ void SetClearColorToDefault() {
+ mClearColor = mDefaultClearColor;
+ }
+
+ /*
+ * Clear aRect on current render target.
+ */
+ virtual void ClearRect(const gfx::Rect& aRect) = 0;
+
+ /**
+ * Start a new frame.
+ *
+ * aInvalidRect is the invalid region of the screen; it can be ignored for
+ * compositors where the performance for compositing the entire window is
+ * sufficient.
+ *
+ * aClipRectIn is the clip rect for the window in window space (optional).
+ * aTransform is the transform from user space to window space.
+ * aRenderBounds bounding rect for rendering, in user space.
+ *
+ * If aClipRectIn is null, this method sets *aClipRectOut to the clip rect
+ * actually used for rendering (if aClipRectIn is non-null, we will use that
+ * for the clip rect).
+ *
+ * If aRenderBoundsOut is non-null, it will be set to the render bounds
+ * actually used by the compositor in window space. If aRenderBoundsOut
+ * is returned empty, composition should be aborted.
+ *
+ * If aOpaque is true, then all of aInvalidRegion will be drawn to with
+ * opaque content.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect* aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect* aClipRectOut = nullptr,
+ gfx::IntRect* aRenderBoundsOut = nullptr) = 0;
+
+ /**
+ * Flush the current frame to the screen and tidy up.
+ *
+ * Derived class overriding this should call Compositor::EndFrame.
+ */
+ virtual void EndFrame();
+
+ virtual void SetDispAcquireFence(Layer* aLayer);
+
+ /**
+ * Post-rendering stuff if the rendering is done outside of this Compositor
+ * e.g., by Composer2D.
+ * aTransform is the transform from user space to window space.
+ */
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) = 0;
+
+ /**
+ * Whether textures created by this compositor can receive partial updates.
+ */
+ virtual bool SupportsPartialTextureUpdate() = 0;
+
+ void SetDiagnosticTypes(DiagnosticTypes aDiagnostics)
+ {
+ mDiagnosticTypes = aDiagnostics;
+ }
+
+ DiagnosticTypes GetDiagnosticTypes() const
+ {
+ return mDiagnosticTypes;
+ }
+
+ void DrawDiagnostics(DiagnosticFlags aFlags,
+ const gfx::Rect& visibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter = DIAGNOSTIC_FLASH_COUNTER_MAX);
+
+ void DrawDiagnostics(DiagnosticFlags aFlags,
+ const nsIntRegion& visibleRegion,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter = DIAGNOSTIC_FLASH_COUNTER_MAX);
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const = 0;
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const = 0;
+
+ virtual CompositorOGL* AsCompositorOGL() { return nullptr; }
+ virtual CompositorD3D9* AsCompositorD3D9() { return nullptr; }
+ virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; }
+ virtual BasicCompositor* AsBasicCompositor() { return nullptr; }
+
+ /**
+ * Each Compositor has a unique ID.
+ * This ID is used to keep references to each Compositor in a map accessed
+ * from the compositor thread only, so that async compositables can find
+ * the right compositor parent and schedule compositing even if the compositor
+ * changed.
+ */
+ uint32_t GetCompositorID() const
+ {
+ return mCompositorID;
+ }
+ void SetCompositorID(uint32_t aID)
+ {
+ MOZ_ASSERT(mCompositorID == 0, "The compositor ID must be set only once.");
+ mCompositorID = aID;
+ }
+
+ /**
+ * Notify the compositor that composition is being paused. This allows the
+ * compositor to temporarily release any resources.
+ * Between calling Pause and Resume, compositing may fail.
+ */
+ virtual void Pause() {}
+ /**
+ * Notify the compositor that composition is being resumed. The compositor
+ * regain any resources it requires for compositing.
+ * Returns true if succeeded.
+ */
+ virtual bool Resume() { return true; }
+
+ /**
+ * Call before rendering begins to ensure the compositor is ready to
+ * composite. Returns false if rendering should be aborted.
+ */
+ virtual bool Ready() { return true; }
+
+ virtual void ForcePresent() { }
+
+ virtual bool IsPendingComposite() { return false; }
+
+ virtual void FinishPendingComposite() {}
+
+ widget::CompositorWidget* GetWidget() const { return mWidget; }
+
+ virtual bool HasImageHostOverlays() { return false; }
+
+ virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) {}
+
+ virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) {}
+
+ /**
+ * Debug-build assertion that can be called to ensure code is running on the
+ * compositor thread.
+ */
+ static void AssertOnCompositorThread();
+
+ size_t GetFillRatio() {
+ float fillRatio = 0;
+ if (mPixelsFilled > 0 && mPixelsPerFrame > 0) {
+ fillRatio = 100.0f * float(mPixelsFilled) / float(mPixelsPerFrame);
+ if (fillRatio > 999.0f) {
+ fillRatio = 999.0f;
+ }
+ }
+ return fillRatio;
+ }
+
+ ScreenRotation GetScreenRotation() const {
+ return mScreenRotation;
+ }
+ void SetScreenRotation(ScreenRotation aRotation) {
+ mScreenRotation = aRotation;
+ }
+
+ TimeStamp GetCompositionTime() const {
+ return mCompositionTime;
+ }
+ void SetCompositionTime(TimeStamp aTimeStamp) {
+ mCompositionTime = aTimeStamp;
+ if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() &&
+ mCompositionTime >= mCompositeUntilTime) {
+ mCompositeUntilTime = TimeStamp();
+ }
+ }
+
+ void CompositeUntil(TimeStamp aTimeStamp) {
+ if (mCompositeUntilTime.IsNull() ||
+ mCompositeUntilTime < aTimeStamp) {
+ mCompositeUntilTime = aTimeStamp;
+ }
+ }
+ TimeStamp GetCompositeUntilTime() const {
+ return mCompositeUntilTime;
+ }
+
+ // A stale Compositor has no CompositorBridgeParent; it will not process
+ // frames and should not be used.
+ void SetInvalid();
+ bool IsValid() const;
+ CompositorBridgeParent* GetCompositorBridgeParent() const {
+ return mParent;
+ }
+
+ /// Most compositor backends operate asynchronously under the hood. This
+ /// means that when a layer stops using a texture it is often desirable to
+ /// wait for the end of the next composition before releasing the texture's
+ /// ReadLock.
+ /// This function provides a convenient way to do this delayed unlocking, if
+ /// the texture itself requires it.
+ void UnlockAfterComposition(TextureHost* aTexture);
+
+ /// Most compositor backends operate asynchronously under the hood. This
+ /// means that when a layer stops using a texture it is often desirable to
+ /// wait for the end of the next composition before NotifyNotUsed() call.
+ /// This function provides a convenient way to do this delayed NotifyNotUsed()
+ /// call, if the texture itself requires it.
+ /// See bug 1260611 and bug 1252835
+ void NotifyNotUsedAfterComposition(TextureHost* aTextureHost);
+
+ void FlushPendingNotifyNotUsed();
+
+protected:
+ void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
+ const gfx::Rect& aVisibleRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& transform,
+ uint32_t aFlashCounter);
+
+ bool ShouldDrawDiagnostics(DiagnosticFlags);
+
+ // Should be called at the end of each composition.
+ void ReadUnlockTextures();
+
+ /**
+ * Given a layer rect, clip, and transform, compute the area of the backdrop that
+ * needs to be copied for mix-blending. The output transform translates from 0..1
+ * space into the backdrop rect space.
+ *
+ * The transformed layer quad is also optionally returned - this is the same as
+ * the result rect, before rounding.
+ */
+ gfx::IntRect ComputeBackdropCopyRect(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad = nullptr);
+
+ gfx::IntRect ComputeBackdropCopyRect(const gfx::Triangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const gfx::Matrix4x4& aTransform,
+ gfx::Matrix4x4* aOutTransform,
+ gfx::Rect* aOutLayerQuad = nullptr);
+
+
+ /**
+ * An array of locks that will need to be unlocked after the next composition.
+ */
+ nsTArray<RefPtr<TextureHost>> mUnlockAfterComposition;
+
+ /**
+ * An array of TextureHosts that will need to call NotifyNotUsed() after the next composition.
+ */
+ nsTArray<RefPtr<TextureHost>> mNotifyNotUsedAfterComposition;
+
+ /**
+ * Last Composition end time.
+ */
+ TimeStamp mLastCompositionEndTime;
+
+ /**
+ * Render time for the current composition.
+ */
+ TimeStamp mCompositionTime;
+ /**
+ * When nonnull, during rendering, some compositable indicated that it will
+ * change its rendering at this time. In order not to miss it, we composite
+ * on every vsync until this time occurs (this is the latest such time).
+ */
+ TimeStamp mCompositeUntilTime;
+
+ uint32_t mCompositorID;
+ DiagnosticTypes mDiagnosticTypes;
+ CompositorBridgeParent* mParent;
+
+ /**
+ * We keep track of the total number of pixels filled as we composite the
+ * current frame. This value is an approximation and is not accurate,
+ * especially in the presence of transforms.
+ */
+ size_t mPixelsPerFrame;
+ size_t mPixelsFilled;
+
+ ScreenRotation mScreenRotation;
+
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ widget::CompositorWidget* mWidget;
+
+ bool mIsDestroyed;
+
+ gfx::Color mClearColor;
+ gfx::Color mDefaultClearColor;
+
+private:
+ static LayersBackend sBackend;
+
+};
+
+// Returns the number of rects. (Up to 4)
+typedef gfx::Rect decomposedRectArrayT[4];
+size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ decomposedRectArrayT* aLayerRects,
+ decomposedRectArrayT* aTextureRects);
+
+static inline bool
+BlendOpIsMixBlendMode(gfx::CompositionOp aOp)
+{
+ switch (aOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ case gfx::CompositionOp::OP_SCREEN:
+ case gfx::CompositionOp::OP_OVERLAY:
+ case gfx::CompositionOp::OP_DARKEN:
+ case gfx::CompositionOp::OP_LIGHTEN:
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ case gfx::CompositionOp::OP_EXCLUSION:
+ case gfx::CompositionOp::OP_HUE:
+ case gfx::CompositionOp::OP_SATURATION:
+ case gfx::CompositionOp::OP_COLOR:
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COMPOSITOR_H */
diff --git a/gfx/layers/CompositorTypes.h b/gfx/layers/CompositorTypes.h
new file mode 100644
index 000000000..c6ce4c2cb
--- /dev/null
+++ b/gfx/layers/CompositorTypes.h
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITORTYPES_H
+#define MOZILLA_LAYERS_COMPOSITORTYPES_H
+
+#include <stdint.h> // for uint32_t
+#include <sys/types.h> // for int32_t
+#include "LayersTypes.h" // for LayersBackend, etc
+#include "nsXULAppAPI.h" // for GeckoProcessType, etc
+#include "mozilla/gfx/Types.h"
+#include "mozilla/EnumSet.h"
+
+#include "mozilla/TypedEnumBits.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Flags used by texture clients and texture hosts. These are passed from client
+ * side to host side when textures and compositables are created. Usually set
+ * by the compositableCient, they may be modified by either the compositable or
+ * texture clients.
+ */
+enum class TextureFlags : uint32_t {
+ NO_FLAGS = 0,
+ // Use nearest-neighbour texture filtering (as opposed to linear filtering).
+ USE_NEAREST_FILTER = 1 << 0,
+ // The compositor assumes everything is origin-top-left by default.
+ ORIGIN_BOTTOM_LEFT = 1 << 1,
+ // Force the texture to be represented using a single tile (note that this means
+ // tiled textures, not tiled layers).
+ DISALLOW_BIGIMAGE = 1 << 2,
+ // The buffer will be treated as if the RB bytes are swapped.
+ // This is useful for rendering using Cairo/Thebes, because there is no
+ // BGRX Android pixel format, and so we have to do byte swapping.
+ //
+ // For example, if the GraphicBuffer has an Android pixel format of
+ // PIXEL_FORMAT_RGBA_8888 and isRBSwapped is true, when it is sampled
+ // (for example, with GL), a BGRA shader should be used.
+ RB_SWAPPED = 1 << 3,
+ // Data in this texture has not been alpha-premultiplied.
+ // XXX - Apparently only used with ImageClient/Host
+ NON_PREMULTIPLIED = 1 << 4,
+ // The TextureClient should be recycled with recycle callback when no longer
+ // in used. When the texture is used in host side, ref count of TextureClient
+ // is transparently added by ShadowLayerForwarder or ImageBridgeChild.
+ RECYCLE = 1 << 5,
+ // If DEALLOCATE_CLIENT is set, the shared data is deallocated on the
+ // client side and requires some extra synchronizaion to ensure race-free
+ // deallocation.
+ // The default behaviour is to deallocate on the host side.
+ DEALLOCATE_CLIENT = 1 << 6,
+ DEALLOCATE_SYNC = 1 << 6, // XXX - make it a separate flag.
+ DEALLOCATE_MAIN_THREAD = 1 << 8,
+ // After being shared ith the compositor side, an immutable texture is never
+ // modified, it can only be read. It is safe to not Lock/Unlock immutable
+ // textures.
+ IMMUTABLE = 1 << 9,
+ // The contents of the texture must be uploaded or copied immediately
+ // during the transaction, because the producer may want to write
+ // to it again.
+ IMMEDIATE_UPLOAD = 1 << 10,
+ // The texture is part of a component-alpha pair
+ COMPONENT_ALPHA = 1 << 11,
+ // The texture is being allocated for a compositor that no longer exists.
+ // This flag is only used in the parent process.
+ INVALID_COMPOSITOR = 1 << 12,
+ // The texture was created by converting from YCBCR to RGB
+ RGB_FROM_YCBCR = 1 << 13,
+
+ // OR union of all valid bits
+ ALL_BITS = (1 << 14) - 1,
+ // the default flags
+ DEFAULT = NO_FLAGS
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextureFlags)
+
+static inline bool
+TextureRequiresLocking(TextureFlags aFlags)
+{
+ // If we're not double buffered, or uploading
+ // within a transaction, then we need to support
+ // locking correctly.
+ return !(aFlags & (TextureFlags::IMMEDIATE_UPLOAD |
+ TextureFlags::IMMUTABLE));
+}
+
+/**
+ * The type of debug diagnostic to enable.
+ */
+enum class DiagnosticTypes : uint8_t {
+ NO_DIAGNOSTIC = 0,
+ TILE_BORDERS = 1 << 0,
+ LAYER_BORDERS = 1 << 1,
+ BIGIMAGE_BORDERS = 1 << 2,
+ FLASH_BORDERS = 1 << 3,
+ ALL_BITS = (1 << 4) - 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticTypes)
+
+#define DIAGNOSTIC_FLASH_COUNTER_MAX 100
+
+/**
+ * Information about the object that is being diagnosed.
+ */
+enum class DiagnosticFlags : uint16_t {
+ NO_DIAGNOSTIC = 0,
+ IMAGE = 1 << 0,
+ CONTENT = 1 << 1,
+ CANVAS = 1 << 2,
+ COLOR = 1 << 3,
+ CONTAINER = 1 << 4,
+ TILE = 1 << 5,
+ BIGIMAGE = 1 << 6,
+ COMPONENT_ALPHA = 1 << 7,
+ REGION_RECT = 1 << 8,
+ NV12 = 1 << 9,
+ YCBCR = 1 << 10
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DiagnosticFlags)
+
+/**
+ * See gfx/layers/Effects.h
+ */
+enum class EffectTypes : uint8_t {
+ MASK,
+ BLEND_MODE,
+ COLOR_MATRIX,
+ MAX_SECONDARY, // sentinel for the count of secondary effect types
+ RGB,
+ YCBCR,
+ NV12,
+ COMPONENT_ALPHA,
+ SOLID_COLOR,
+ RENDER_TARGET,
+ MAX //sentinel for the count of all effect types
+};
+
+/**
+ * How the Compositable should manage textures.
+ */
+enum class CompositableType : uint8_t {
+ UNKNOWN,
+ CONTENT_TILED, // tiled painted layer
+ IMAGE, // image with single buffering
+ IMAGE_BRIDGE, // ImageBridge protocol
+ CONTENT_SINGLE, // painted layer interface, single buffering
+ CONTENT_DOUBLE, // painted layer interface, double buffering
+ COUNT
+};
+
+#ifdef XP_WIN
+typedef void* SyncHandle;
+#else
+typedef uintptr_t SyncHandle;
+#endif // XP_WIN
+
+/**
+ * Sent from the compositor to the content-side LayerManager, includes properties
+ * of the compositor and should (in the future) include information about what
+ * kinds of buffer and texture clients to create.
+ */
+struct TextureFactoryIdentifier
+{
+ LayersBackend mParentBackend;
+ GeckoProcessType mParentProcessType;
+ int32_t mMaxTextureSize;
+ bool mSupportsTextureBlitting;
+ bool mSupportsPartialUploads;
+ bool mSupportsComponentAlpha;
+ SyncHandle mSyncHandle;
+
+ explicit TextureFactoryIdentifier(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
+ GeckoProcessType aParentProcessType = GeckoProcessType_Default,
+ int32_t aMaxTextureSize = 4096,
+ bool aSupportsTextureBlitting = false,
+ bool aSupportsPartialUploads = false,
+ bool aSupportsComponentAlpha = true,
+ SyncHandle aSyncHandle = 0)
+ : mParentBackend(aLayersBackend)
+ , mParentProcessType(aParentProcessType)
+ , mMaxTextureSize(aMaxTextureSize)
+ , mSupportsTextureBlitting(aSupportsTextureBlitting)
+ , mSupportsPartialUploads(aSupportsPartialUploads)
+ , mSupportsComponentAlpha(aSupportsComponentAlpha)
+ , mSyncHandle(aSyncHandle)
+ {}
+};
+
+/**
+ * Information required by the compositor from the content-side for creating or
+ * using compositables and textures.
+ * XXX - TextureInfo is a bad name: this information is useful for the compositable,
+ * not the Texture. And ith new Textures, only the compositable type is really
+ * useful. This may (should) be removed in the near future.
+ */
+struct TextureInfo
+{
+ CompositableType mCompositableType;
+ TextureFlags mTextureFlags;
+
+ TextureInfo()
+ : mCompositableType(CompositableType::UNKNOWN)
+ , mTextureFlags(TextureFlags::NO_FLAGS)
+ {}
+
+ explicit TextureInfo(CompositableType aType,
+ TextureFlags aTextureFlags = TextureFlags::DEFAULT)
+ : mCompositableType(aType)
+ , mTextureFlags(aTextureFlags)
+ {}
+
+ bool operator==(const TextureInfo& aOther) const
+ {
+ return mCompositableType == aOther.mCompositableType &&
+ mTextureFlags == aOther.mTextureFlags;
+ }
+};
+
+/**
+ * How a SurfaceDescriptor will be opened.
+ *
+ * See ShadowLayerForwarder::OpenDescriptor for example.
+ */
+enum class OpenMode : uint8_t {
+ OPEN_NONE = 0,
+ OPEN_READ = 0x1,
+ OPEN_WRITE = 0x2,
+ OPEN_READ_WRITE = OPEN_READ|OPEN_WRITE,
+ OPEN_READ_ONLY = OPEN_READ,
+ OPEN_WRITE_ONLY = OPEN_WRITE
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
+
+// The kinds of mask texture a shader can support
+// We rely on the items in this enum being sequential
+enum class MaskType : uint8_t {
+ MaskNone = 0, // no mask layer
+ Mask, // mask layer
+ NumMaskTypes
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/CopyableCanvasLayer.cpp b/gfx/layers/CopyableCanvasLayer.cpp
new file mode 100644
index 000000000..7ec9ce0cd
--- /dev/null
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CopyableCanvasLayer.h"
+
+#include "BasicLayersImpl.h" // for FillWithMask, etc
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "SharedSurface.h" // for SharedSurface
+#include "SharedSurfaceGL.h" // for SharedSurface
+#include "gfxPattern.h" // for gfxPattern, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "gfxUtils.h"
+#include "client/TextureClientSharedSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImplData) :
+ CanvasLayer(aLayerManager, aImplData)
+ , mGLFrontbuffer(nullptr)
+ , mIsAlphaPremultiplied(true)
+ , mOriginPos(gl::OriginPos::TopLeft)
+ , mIsMirror(false)
+{
+ MOZ_COUNT_CTOR(CopyableCanvasLayer);
+}
+
+CopyableCanvasLayer::~CopyableCanvasLayer()
+{
+ MOZ_COUNT_DTOR(CopyableCanvasLayer);
+}
+
+void
+CopyableCanvasLayer::Initialize(const Data& aData)
+{
+ if (aData.mGLContext) {
+ mGLContext = aData.mGLContext;
+ mIsAlphaPremultiplied = aData.mIsGLAlphaPremult;
+ mOriginPos = gl::OriginPos::BottomLeft;
+ mIsMirror = aData.mIsMirror;
+
+ MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
+
+ if (aData.mFrontbufferGLTex) {
+ gfx::IntSize size(aData.mSize.width, aData.mSize.height);
+ mGLFrontbuffer = SharedSurface_Basic::Wrap(aData.mGLContext, size, aData.mHasAlpha,
+ aData.mFrontbufferGLTex);
+ mBufferProvider = aData.mBufferProvider;
+ }
+ } else if (aData.mBufferProvider) {
+ mBufferProvider = aData.mBufferProvider;
+ } else if (aData.mRenderer) {
+ mAsyncRenderer = aData.mRenderer;
+ mOriginPos = gl::OriginPos::BottomLeft;
+ } else {
+ MOZ_CRASH("GFX: CanvasLayer created without BufferProvider, DrawTarget or GLContext?");
+ }
+
+ mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
+}
+
+bool
+CopyableCanvasLayer::IsDataValid(const Data& aData)
+{
+ return mGLContext == aData.mGLContext;
+}
+
+DataSourceSurface*
+CopyableCanvasLayer::GetTempSurface(const IntSize& aSize,
+ const SurfaceFormat aFormat)
+{
+ if (!mCachedTempSurface ||
+ aSize != mCachedTempSurface->GetSize() ||
+ aFormat != mCachedTempSurface->GetFormat())
+ {
+ // Create a surface aligned to 8 bytes since that's the highest alignment WebGL can handle.
+ uint32_t stride = GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
+ mCachedTempSurface = Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, stride);
+ }
+
+ return mCachedTempSurface;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/CopyableCanvasLayer.h b/gfx/layers/CopyableCanvasLayer.h
new file mode 100644
index 000000000..92eec0090
--- /dev/null
+++ b/gfx/layers/CopyableCanvasLayer.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_COPYABLECANVASLAYER_H
+#define GFX_COPYABLECANVASLAYER_H
+
+#include <stdint.h> // for uint32_t
+#include "GLContextTypes.h" // for GLContext
+#include "Layers.h" // for CanvasLayer, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxTypes.h"
+#include "gfxPlatform.h" // for gfxImageFormat
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+
+namespace gl {
+class SharedSurface;
+} // namespace gl
+
+namespace layers {
+
+/**
+ * A shared CanvasLayer implementation that supports copying
+ * its contents into a gfxASurface using UpdateSurface.
+ */
+class CopyableCanvasLayer : public CanvasLayer
+{
+public:
+ CopyableCanvasLayer(LayerManager* aLayerManager, void *aImplData);
+
+protected:
+ virtual ~CopyableCanvasLayer();
+
+public:
+ virtual void Initialize(const Data& aData) override;
+
+ virtual bool IsDataValid(const Data& aData) override;
+
+ bool IsGLLayer() { return !!mGLContext; }
+
+protected:
+ RefPtr<gl::GLContext> mGLContext;
+ RefPtr<PersistentBufferProvider> mBufferProvider;
+ UniquePtr<gl::SharedSurface> mGLFrontbuffer;
+
+ bool mIsAlphaPremultiplied;
+ gl::OriginPos mOriginPos;
+ bool mIsMirror;
+
+ RefPtr<gfx::DataSourceSurface> mCachedTempSurface;
+
+ gfx::DataSourceSurface* GetTempSurface(const gfx::IntSize& aSize,
+ const gfx::SurfaceFormat aFormat);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/D3D11ShareHandleImage.cpp b/gfx/layers/D3D11ShareHandleImage.cpp
new file mode 100644
index 000000000..e00a5e3b7
--- /dev/null
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "D3D11ShareHandleImage.h"
+#include "gfxImageSurface.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "d3d11.h"
+
+namespace mozilla {
+namespace layers {
+
+D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize& aSize,
+ const gfx::IntRect& aRect)
+ : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE),
+ mSize(aSize),
+ mPictureRect(aRect)
+{
+}
+
+bool
+D3D11ShareHandleImage::AllocateTexture(D3D11RecycleAllocator* aAllocator, ID3D11Device* aDevice)
+{
+ if (aAllocator) {
+ mTextureClient = aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, mSize);
+ if (mTextureClient) {
+ mTexture = static_cast<D3D11TextureData*>(mTextureClient->GetInternalData())->GetD3D11Texture();
+ return true;
+ }
+ return false;
+ } else {
+ MOZ_ASSERT(aDevice);
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ mSize.width, mSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+
+ HRESULT hr = aDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(mTexture));
+ return SUCCEEDED(hr);
+ }
+}
+
+gfx::IntSize
+D3D11ShareHandleImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+D3D11ShareHandleImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient;
+}
+
+already_AddRefed<gfx::SourceSurface>
+D3D11ShareHandleImage::GetAsSourceSurface()
+{
+ RefPtr<ID3D11Texture2D> texture = GetTexture();
+ if (!texture) {
+ NS_WARNING("Cannot readback from shared texture because no texture is available.");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ texture->GetDevice(getter_AddRefs(device));
+
+ D3D11_TEXTURE2D_DESC desc;
+ texture->GetDesc(&desc);
+
+ CD3D11_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height);
+ softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ softDesc.BindFlags = 0;
+ softDesc.MiscFlags = 0;
+ softDesc.MipLevels = 1;
+ softDesc.Usage = D3D11_USAGE_STAGING;
+
+ RefPtr<ID3D11Texture2D> softTexture;
+ HRESULT hr = device->CreateTexture2D(&softDesc,
+ NULL,
+ static_cast<ID3D11Texture2D**>(getter_AddRefs(softTexture)));
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to create 2D staging texture.");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ device->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ return nullptr;
+ }
+
+ context->CopyResource(softTexture, texture);
+
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ gfx::DataSourceSurface::MappedSurface mappedSurface;
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
+ return nullptr;
+ }
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = context->Map(softTexture, 0, D3D11_MAP_READ, 0, &map);
+ if (!SUCCEEDED(hr)) {
+ surface->Unmap();
+ return nullptr;
+ }
+
+ for (int y = 0; y < mSize.height; y++) {
+ memcpy(mappedSurface.mData + mappedSurface.mStride * y,
+ (unsigned char*)(map.pData) + map.RowPitch * y,
+ mSize.width * 4);
+ }
+
+ context->Unmap(softTexture, 0);
+ surface->Unmap();
+
+ return surface.forget();
+}
+
+ID3D11Texture2D*
+D3D11ShareHandleImage::GetTexture() const {
+ return mTexture;
+}
+
+already_AddRefed<TextureClient>
+D3D11RecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return CreateD3D11TextureClientWithDevice(aSize, aFormat,
+ aTextureFlags, aAllocFlags,
+ mDevice, mSurfaceAllocator->GetTextureForwarder());
+}
+
+already_AddRefed<TextureClient>
+D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize)
+{
+ RefPtr<TextureClient> textureClient =
+ CreateOrRecycle(aFormat,
+ aSize,
+ BackendSelector::Content,
+ layers::TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
+ return textureClient.forget();
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/D3D11ShareHandleImage.h b/gfx/layers/D3D11ShareHandleImage.h
new file mode 100644
index 000000000..bff03e93a
--- /dev/null
+++ b/gfx/layers/D3D11ShareHandleImage.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_D311_SHARE_HANDLE_IMAGE_H
+#define GFX_D311_SHARE_HANDLE_IMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "d3d11.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class D3D11RecycleAllocator : public TextureClientRecycleAllocator
+{
+public:
+ explicit D3D11RecycleAllocator(KnowsCompositor* aAllocator,
+ ID3D11Device* aDevice)
+ : TextureClientRecycleAllocator(aAllocator)
+ , mDevice(aDevice)
+ {}
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize);
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags) override;
+
+ RefPtr<ID3D11Device> mDevice;
+};
+
+// Image class that wraps a ID3D11Texture2D. This class copies the image
+// passed into SetData(), so that it can be accessed from other D3D devices.
+// This class also manages the synchronization of the copy, to ensure the
+// resource is ready to use.
+class D3D11ShareHandleImage final : public Image {
+public:
+ D3D11ShareHandleImage(const gfx::IntSize& aSize,
+ const gfx::IntRect& aRect);
+ ~D3D11ShareHandleImage() override {}
+
+ bool AllocateTexture(D3D11RecycleAllocator* aAllocator, ID3D11Device* aDevice);
+
+ gfx::IntSize GetSize() override;
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+ virtual gfx::IntRect GetPictureRect() override { return mPictureRect; }
+
+ ID3D11Texture2D* GetTexture() const;
+
+private:
+ gfx::IntSize mSize;
+ gfx::IntRect mPictureRect;
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ID3D11Texture2D> mTexture;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/gfx/layers/D3D9SurfaceImage.cpp b/gfx/layers/D3D9SurfaceImage.cpp
new file mode 100644
index 000000000..91dce6291
--- /dev/null
+++ b/gfx/layers/D3D9SurfaceImage.cpp
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "D3D9SurfaceImage.h"
+#include "gfx2DGlue.h"
+#include "mozilla/layers/TextureD3D9.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+
+D3D9SurfaceImage::D3D9SurfaceImage()
+ : Image(nullptr, ImageFormat::D3D9_RGB32_TEXTURE)
+ , mSize(0, 0)
+ , mShareHandle(0)
+ , mValid(true)
+{}
+
+D3D9SurfaceImage::~D3D9SurfaceImage()
+{
+}
+
+HRESULT
+D3D9SurfaceImage::AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+ IDirect3DSurface9* aSurface,
+ const gfx::IntRect& aRegion)
+{
+ NS_ENSURE_TRUE(aSurface, E_POINTER);
+ HRESULT hr;
+ RefPtr<IDirect3DSurface9> surface = aSurface;
+
+ RefPtr<IDirect3DDevice9> device;
+ hr = surface->GetDevice(getter_AddRefs(device));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
+
+ RefPtr<IDirect3D9> d3d9;
+ hr = device->GetDirect3D(getter_AddRefs(d3d9));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
+
+ D3DSURFACE_DESC desc;
+ surface->GetDesc(&desc);
+ // Ensure we can convert the textures format to RGB conversion
+ // in StretchRect. Fail if we can't.
+ hr = d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ desc.Format,
+ D3DFMT_A8R8G8B8);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+ // DXVA surfaces aren't created sharable, so we need to copy the surface
+ // to a sharable texture to that it's accessible to the layer manager's
+ // device.
+ if (aAllocator) {
+ mTextureClient =
+ aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, aRegion.Size());
+ if (!mTextureClient) {
+ return E_FAIL;
+ }
+
+ DXGID3D9TextureData* texData =
+ static_cast<DXGID3D9TextureData*>(mTextureClient->GetInternalData());
+ mTexture = texData->GetD3D9Texture();
+ mShareHandle = texData->GetShareHandle();
+ mDesc = texData->GetDesc();
+ } else {
+ hr = device->CreateTexture(aRegion.Size().width, aRegion.Size().height,
+ 1,
+ D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(mTexture),
+ &mShareHandle);
+ if (FAILED(hr) || !mShareHandle) {
+ return E_FAIL;
+ }
+
+ hr = mTexture->GetLevelDesc(0, &mDesc);
+ if (FAILED(hr)) {
+ return E_FAIL;
+ }
+ }
+
+ // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
+ RefPtr<IDirect3DSurface9> textureSurface = GetD3D9Surface();
+ if (!textureSurface) {
+ return E_FAIL;
+ }
+
+ RECT src = { aRegion.x, aRegion.y, aRegion.x+aRegion.width, aRegion.y+aRegion.height };
+ hr = device->StretchRect(surface, &src, textureSurface, nullptr, D3DTEXF_NONE);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+ mSize = aRegion.Size();
+ return S_OK;
+}
+
+already_AddRefed<IDirect3DSurface9>
+D3D9SurfaceImage::GetD3D9Surface()
+{
+ RefPtr<IDirect3DSurface9> textureSurface;
+ HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(textureSurface));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+ return textureSurface.forget();
+}
+
+const D3DSURFACE_DESC&
+D3D9SurfaceImage::GetDesc() const
+{
+ return mDesc;
+}
+
+HANDLE
+D3D9SurfaceImage::GetShareHandle() const
+{
+ return mShareHandle;
+}
+
+gfx::IntSize
+D3D9SurfaceImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+D3D9SurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ MOZ_ASSERT(mTextureClient);
+ MOZ_ASSERT(mTextureClient->GetAllocator() == aForwarder->GetTextureForwarder());
+ return mTextureClient;
+}
+
+already_AddRefed<gfx::SourceSurface>
+D3D9SurfaceImage::GetAsSourceSurface()
+{
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ HRESULT hr;
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ // Readback the texture from GPU memory into system memory, so that
+ // we can copy it into the Cairo image. This is expensive.
+ RefPtr<IDirect3DSurface9> textureSurface = GetD3D9Surface();
+ if (!textureSurface) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DDevice9> device;
+ hr = textureSurface->GetDevice(getter_AddRefs(device));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<IDirect3DSurface9> systemMemorySurface;
+ hr = device->CreateOffscreenPlainSurface(mSize.width,
+ mSize.height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(systemMemorySurface),
+ 0);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ hr = device->GetRenderTargetData(textureSurface, systemMemorySurface);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ D3DLOCKED_RECT rect;
+ hr = systemMemorySurface->LockRect(&rect, nullptr, 0);
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ gfx::DataSourceSurface::MappedSurface mappedSurface;
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
+ systemMemorySurface->UnlockRect();
+ return nullptr;
+ }
+
+ const unsigned char* src = (const unsigned char*)(rect.pBits);
+ const unsigned srcPitch = rect.Pitch;
+ for (int y = 0; y < mSize.height; y++) {
+ memcpy(mappedSurface.mData + mappedSurface.mStride * y,
+ (unsigned char*)(src) + srcPitch * y,
+ mSize.width * 4);
+ }
+
+ systemMemorySurface->UnlockRect();
+ surface->Unmap();
+
+ return surface.forget();
+}
+
+already_AddRefed<TextureClient>
+D3D9RecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ TextureData* data = DXGID3D9TextureData::Create(aSize, aFormat, aTextureFlags, mDevice);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ mSurfaceAllocator->GetTextureForwarder());
+}
+
+already_AddRefed<TextureClient>
+D3D9RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize)
+{
+ return CreateOrRecycle(aFormat, aSize, BackendSelector::Content,
+ TextureFlags::DEFAULT);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/D3D9SurfaceImage.h b/gfx/layers/D3D9SurfaceImage.h
new file mode 100644
index 000000000..a5326bb99
--- /dev/null
+++ b/gfx/layers/D3D9SurfaceImage.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_D3DSURFACEIMAGE_H
+#define GFX_D3DSURFACEIMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "d3d9.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureClient;
+
+class D3D9RecycleAllocator : public TextureClientRecycleAllocator
+{
+public:
+ explicit D3D9RecycleAllocator(KnowsCompositor* aAllocator,
+ IDirect3DDevice9* aDevice)
+ : TextureClientRecycleAllocator(aAllocator)
+ , mDevice(aDevice)
+ {}
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
+ const gfx::IntSize& aSize);
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags) override;
+
+ RefPtr<IDirect3DDevice9> mDevice;
+};
+
+// Image class that wraps a IDirect3DSurface9. This class copies the image
+// passed into SetData(), so that it can be accessed from other D3D devices.
+// This class also manages the synchronization of the copy, to ensure the
+// resource is ready to use.
+class D3D9SurfaceImage : public Image {
+public:
+ explicit D3D9SurfaceImage();
+ virtual ~D3D9SurfaceImage();
+
+ HRESULT AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+ IDirect3DSurface9* aSurface,
+ const gfx::IntRect& aRegion);
+
+ // Returns the description of the shared surface.
+ const D3DSURFACE_DESC& GetDesc() const;
+
+ gfx::IntSize GetSize() override;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ already_AddRefed<IDirect3DSurface9> GetD3D9Surface();
+
+ HANDLE GetShareHandle() const;
+
+ virtual bool IsValid() override { return mValid; }
+
+ void Invalidate() { mValid = false; }
+
+private:
+
+ gfx::IntSize mSize;
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<IDirect3DTexture9> mTexture;
+ HANDLE mShareHandle;
+ D3DSURFACE_DESC mDesc;
+ bool mValid;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/gfx/layers/DirectedGraph.h b/gfx/layers/DirectedGraph.h
new file mode 100644
index 000000000..272e16c1f
--- /dev/null
+++ b/gfx/layers/DirectedGraph.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DIRECTEDGRAPH_H
+#define GFX_DIRECTEDGRAPH_H
+
+#include "gfxTypes.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+template <typename T>
+class DirectedGraph {
+public:
+
+ class Edge {
+ public:
+ Edge(T aFrom, T aTo) : mFrom(aFrom), mTo(aTo) {}
+
+ bool operator==(const Edge& aOther) const
+ {
+ return mFrom == aOther.mFrom && mTo == aOther.mTo;
+ }
+
+ T mFrom;
+ T mTo;
+ };
+
+ class RemoveEdgesToComparator
+ {
+ public:
+ bool Equals(const Edge& a, T const& b) const { return a.mTo == b; }
+ };
+
+ /**
+ * Add a new edge to the graph.
+ */
+ void AddEdge(Edge aEdge)
+ {
+ NS_ASSERTION(!mEdges.Contains(aEdge), "Adding a duplicate edge!");
+ mEdges.AppendElement(aEdge);
+ }
+
+ void AddEdge(T aFrom, T aTo)
+ {
+ AddEdge(Edge(aFrom, aTo));
+ }
+
+ /**
+ * Get the list of edges.
+ */
+ const nsTArray<Edge>& GetEdgeList() const
+ {
+ return mEdges;
+ }
+
+ /**
+ * Remove the given edge from the graph.
+ */
+ void RemoveEdge(Edge aEdge)
+ {
+ mEdges.RemoveElement(aEdge);
+ }
+
+ /**
+ * Remove all edges going into aNode.
+ */
+ void RemoveEdgesTo(T aNode)
+ {
+ RemoveEdgesToComparator c;
+ while (mEdges.RemoveElement(aNode, c)) {}
+ }
+
+ /**
+ * Get the number of edges going into aNode.
+ */
+ unsigned int NumEdgesTo(T aNode)
+ {
+ unsigned int count = 0;
+ for (unsigned int i = 0; i < mEdges.Length(); i++) {
+ if (mEdges.ElementAt(i).mTo == aNode) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the list of all edges going from aNode
+ */
+ void GetEdgesFrom(T aNode, nsTArray<Edge>& aResult)
+ {
+ for (unsigned int i = 0; i < mEdges.Length(); i++) {
+ if (mEdges.ElementAt(i).mFrom == aNode) {
+ aResult.AppendElement(mEdges.ElementAt(i));
+ }
+ }
+ }
+
+ /**
+ * Get the total number of edges.
+ */
+ unsigned int GetEdgeCount() { return mEdges.Length(); }
+
+private:
+
+ nsTArray<Edge> mEdges;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_DIRECTEDGRAPH_H
diff --git a/gfx/layers/Effects.cpp b/gfx/layers/Effects.cpp
new file mode 100644
index 000000000..4383af9d2
--- /dev/null
+++ b/gfx/layers/Effects.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Effects.h"
+#include "LayersLogging.h" // for AppendToString
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+
+using namespace mozilla::layers;
+
+void
+TexturedEffect::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ AppendToString(aStream, mTextureCoords, " [texture-coords=", "]");
+
+ if (mPremultiplied) {
+ aStream << " [premultiplied]";
+ } else {
+ aStream << " [not-premultiplied]";
+ }
+
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+}
+
+void
+EffectMask::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectMask (0x%p)", this).get();
+ AppendToString(aStream, mSize, " [size=", "]");
+ AppendToString(aStream, mMaskTransform, " [mask-transform=", "]");
+}
+
+void
+EffectRenderTarget::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ TexturedEffect::PrintInfo(aStream, aPrefix);
+ aStream << nsPrintfCString(" [render-target=%p]", mRenderTarget.get()).get();
+}
+
+void
+EffectSolidColor::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectSolidColor (0x%p) [color=%x]", this, mColor.ToABGR()).get();
+}
+
+void
+EffectBlendMode::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectBlendMode (0x%p) [blendmode=%i]", this, (int)mBlendMode).get();
+}
+
+void
+EffectColorMatrix::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("EffectColorMatrix (0x%p)", this).get();
+ AppendToString(aStream, mColorMatrix, " [matrix=", "]");
+}
+
diff --git a/gfx/layers/Effects.h b/gfx/layers/Effects.h
new file mode 100644
index 000000000..a0859a539
--- /dev/null
+++ b/gfx/layers/Effects.h
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_EFFECTS_H
+#define MOZILLA_LAYERS_EFFECTS_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter, etc
+#include "mozilla/layers/CompositorTypes.h" // for EffectTypes, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/EnumeratedArray.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Effects and effect chains are used by the compositor API (see Compositor.h).
+ * An effect chain represents a rendering method, for example some shader and
+ * the data required for that shader to run. An effect is some component of the
+ * chain and its data.
+ *
+ * An effect chain consists of a primary effect - how the 'texture' memory should
+ * be interpreted (RGBA, BGRX, YCBCR, etc.) - and any number of secondary effects
+ * - any way in which rendering can be changed, e.g., applying a mask layer.
+ *
+ * During the rendering process, an effect chain is created by the layer being
+ * rendered and the primary effect is added by the compositable host. Secondary
+ * effects may be added by the layer or compositable. The effect chain is passed
+ * to the compositor by the compositable host as a parameter to DrawQuad.
+ */
+
+struct Effect
+{
+ NS_INLINE_DECL_REFCOUNTING(Effect)
+
+ explicit Effect(EffectTypes aType) : mType(aType) {}
+
+ EffectTypes mType;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
+
+protected:
+ virtual ~Effect() {}
+};
+
+// Render from a texture
+struct TexturedEffect : public Effect
+{
+ TexturedEffect(EffectTypes aType,
+ TextureSource *aTexture,
+ bool aPremultiplied,
+ gfx::SamplingFilter aSamplingFilter)
+ : Effect(aType)
+ , mTextureCoords(0, 0, 1.0f, 1.0f)
+ , mTexture(aTexture)
+ , mPremultiplied(aPremultiplied)
+ , mSamplingFilter(aSamplingFilter)
+ {}
+
+ virtual const char* Name() = 0;
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::Rect mTextureCoords;
+ TextureSource* mTexture;
+ bool mPremultiplied;
+ gfx::SamplingFilter mSamplingFilter;
+ LayerRenderState mState;
+};
+
+// Support an alpha mask.
+struct EffectMask : public Effect
+{
+ EffectMask(TextureSource *aMaskTexture,
+ gfx::IntSize aSize,
+ const gfx::Matrix4x4 &aMaskTransform)
+ : Effect(EffectTypes::MASK)
+ , mMaskTexture(aMaskTexture)
+ , mSize(aSize)
+ , mMaskTransform(aMaskTransform)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ TextureSource* mMaskTexture;
+ gfx::IntSize mSize;
+ gfx::Matrix4x4 mMaskTransform;
+};
+
+struct EffectBlendMode : public Effect
+{
+ explicit EffectBlendMode(gfx::CompositionOp aBlendMode)
+ : Effect(EffectTypes::BLEND_MODE)
+ , mBlendMode(aBlendMode)
+ { }
+
+ virtual const char* Name() { return "EffectBlendMode"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::CompositionOp mBlendMode;
+};
+
+// Render to a render target rather than the screen.
+struct EffectRenderTarget : public TexturedEffect
+{
+ explicit EffectRenderTarget(CompositingRenderTarget *aRenderTarget)
+ : TexturedEffect(EffectTypes::RENDER_TARGET, aRenderTarget, true,
+ gfx::SamplingFilter::LINEAR)
+ , mRenderTarget(aRenderTarget)
+ {}
+
+ virtual const char* Name() { return "EffectRenderTarget"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ RefPtr<CompositingRenderTarget> mRenderTarget;
+
+protected:
+ EffectRenderTarget(EffectTypes aType, CompositingRenderTarget *aRenderTarget)
+ : TexturedEffect(aType, aRenderTarget, true, gfx::SamplingFilter::LINEAR)
+ , mRenderTarget(aRenderTarget)
+ {}
+
+};
+
+// Render to a render target rather than the screen.
+struct EffectColorMatrix : public Effect
+{
+ explicit EffectColorMatrix(gfx::Matrix5x4 aMatrix)
+ : Effect(EffectTypes::COLOR_MATRIX)
+ , mColorMatrix(aMatrix)
+ {}
+
+ virtual const char* Name() { return "EffectColorMatrix"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+ const gfx::Matrix5x4 mColorMatrix;
+};
+
+
+struct EffectRGB : public TexturedEffect
+{
+ EffectRGB(TextureSource *aTexture,
+ bool aPremultiplied,
+ gfx::SamplingFilter aSamplingFilter,
+ bool aFlipped = false)
+ : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aSamplingFilter)
+ {}
+
+ virtual const char* Name() { return "EffectRGB"; }
+};
+
+struct EffectYCbCr : public TexturedEffect
+{
+ EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::YCBCR, aSource, false, aSamplingFilter)
+ , mYUVColorSpace(aYUVColorSpace)
+ {}
+
+ virtual const char* Name() { return "EffectYCbCr"; }
+
+ YUVColorSpace mYUVColorSpace;
+};
+
+struct EffectNV12 : public TexturedEffect
+{
+ EffectNV12(TextureSource *aSource, gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::NV12, aSource, false, aSamplingFilter)
+ {}
+
+ virtual const char* Name() { return "EffectNV12"; }
+};
+
+struct EffectComponentAlpha : public TexturedEffect
+{
+ EffectComponentAlpha(TextureSource *aOnBlack,
+ TextureSource *aOnWhite,
+ gfx::SamplingFilter aSamplingFilter)
+ : TexturedEffect(EffectTypes::COMPONENT_ALPHA, nullptr, false, aSamplingFilter)
+ , mOnBlack(aOnBlack)
+ , mOnWhite(aOnWhite)
+ {}
+
+ virtual const char* Name() { return "EffectComponentAlpha"; }
+
+ TextureSource* mOnBlack;
+ TextureSource* mOnWhite;
+};
+
+struct EffectSolidColor : public Effect
+{
+ explicit EffectSolidColor(const gfx::Color &aColor)
+ : Effect(EffectTypes::SOLID_COLOR)
+ , mColor(aColor)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ gfx::Color mColor;
+};
+
+struct EffectChain
+{
+ EffectChain() : mLayerRef(nullptr) {}
+ explicit EffectChain(void* aLayerRef) : mLayerRef(aLayerRef) {}
+
+ RefPtr<Effect> mPrimaryEffect;
+ EnumeratedArray<EffectTypes, EffectTypes::MAX_SECONDARY, RefPtr<Effect>>
+ mSecondaryEffects;
+ void* mLayerRef; //!< For LayerScope logging
+};
+
+/**
+ * Create a Textured effect corresponding to aFormat and using
+ * aSource as the (first) texture source.
+ *
+ * Note that aFormat can be different form aSource->GetFormat if, we are
+ * creating an effect that takes several texture sources (like with YCBCR
+ * where aFormat would be FOMRAT_YCBCR and each texture source would be
+ * a one-channel A8 texture)
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(gfx::SurfaceFormat aFormat,
+ TextureSource* aSource,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aSource);
+ RefPtr<TexturedEffect> result;
+ switch (aFormat) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ result = new EffectRGB(aSource, isAlphaPremultiplied, aSamplingFilter);
+ break;
+ case gfx::SurfaceFormat::NV12:
+ result = new EffectNV12(aSource, aSamplingFilter);
+ break;
+ case gfx::SurfaceFormat::YUV:
+ MOZ_ASSERT_UNREACHABLE("gfx::SurfaceFormat::YUV is invalid");
+ break;
+ default:
+ NS_WARNING("unhandled program type");
+ break;
+ }
+
+ result->mState = state;
+
+ return result.forget();
+}
+
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureHost* aHost,
+ TextureSource* aSource,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aHost);
+ MOZ_ASSERT(aSource);
+
+ RefPtr<TexturedEffect> result;
+ if (aHost->GetReadFormat() == gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aHost->GetYUVColorSpace() != YUVColorSpace::UNKNOWN);
+ result = new EffectYCbCr(aSource, aHost->GetYUVColorSpace(), aSamplingFilter);
+ } else {
+ result = CreateTexturedEffect(aHost->GetReadFormat(),
+ aSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ state);
+ }
+ return result.forget();
+}
+
+/**
+ * Create a textured effect based on aSource format and the presence of
+ * aSourceOnWhite.
+ *
+ * aSourceOnWhite can be null.
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureSource* aSource,
+ TextureSource* aSourceOnWhite,
+ const gfx::SamplingFilter aSamplingFilter,
+ bool isAlphaPremultiplied,
+ const LayerRenderState &state = LayerRenderState())
+{
+ MOZ_ASSERT(aSource);
+ if (aSourceOnWhite) {
+ MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+ MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
+ return MakeAndAddRef<EffectComponentAlpha>(aSource, aSourceOnWhite,
+ aSamplingFilter);
+ }
+
+ return CreateTexturedEffect(aSource->GetFormat(),
+ aSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ state);
+}
+
+/**
+ * Create a textured effect based on aSource format.
+ *
+ * This version excudes the possibility of component alpha.
+ */
+inline already_AddRefed<TexturedEffect>
+CreateTexturedEffect(TextureSource *aTexture,
+ const gfx::SamplingFilter aSamplingFilter,
+ const LayerRenderState &state = LayerRenderState())
+{
+ return CreateTexturedEffect(aTexture, nullptr, aSamplingFilter, true, state);
+}
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/FrameMetrics.cpp b/gfx/layers/FrameMetrics.cpp
new file mode 100644
index 000000000..7b5108557
--- /dev/null
+++ b/gfx/layers/FrameMetrics.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameMetrics.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+namespace layers {
+
+const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
+
+void
+ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
+ MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());
+ mUsesContainerScrolling = aValue;
+}
+
+StaticAutoPtr<const ScrollMetadata> ScrollMetadata::sNullMetadata;
+
+}
+}
diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h
new file mode 100644
index 000000000..0261dbad7
--- /dev/null
+++ b/gfx/layers/FrameMetrics.h
@@ -0,0 +1,1112 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FRAMEMETRICS_H
+#define GFX_FRAMEMETRICS_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include "Units.h" // for CSSRect, CSSPixel, etc
+#include "mozilla/HashFunctions.h" // for HashGeneric
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/gfx/Logging.h" // for Log
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsString.h"
+#include "nsStyleCoord.h" // for nsStyleCoord
+
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Helper struct to hold a couple of fields that can be updated as part of
+ * an empty transaction.
+ */
+struct ScrollUpdateInfo {
+ uint32_t mScrollGeneration;
+ CSSPoint mScrollOffset;
+};
+
+/**
+ * The viewport and displayport metrics for the painted frame at the
+ * time of a layer-tree transaction. These metrics are especially
+ * useful for shadow layers, because the metrics values are updated
+ * atomically with new pixels.
+ */
+struct FrameMetrics {
+ friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
+public:
+ // We use IDs to identify frames across processes.
+ typedef uint64_t ViewID;
+ static const ViewID NULL_SCROLL_ID; // This container layer does not scroll.
+ static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes
+ // will begin at.
+
+ enum ScrollOffsetUpdateType : uint8_t {
+ eNone, // The default; the scroll offset was not updated
+ eMainThread, // The scroll offset was updated by the main thread.
+ ePending, // The scroll offset was updated on the main thread, but not
+ // painted, so the layer texture data is still at the old
+ // offset.
+ eUserAction, // In an APZ repaint request, this means the APZ generated
+ // the scroll position based on user action (the alternative
+ // is eNone which means it's just request a repaint because
+ // it got a scroll update from the main thread).
+ eRestore, // The scroll offset was updated by the main thread, but as
+ // a restore from history or after a frame reconstruction.
+ // In this case, APZ can ignore the offset change if the
+ // user has done an APZ scroll already.
+
+ eSentinel // For IPC use only
+ };
+
+ FrameMetrics()
+ : mScrollId(NULL_SCROLL_ID)
+ , mPresShellResolution(1)
+ , mCompositionBounds(0, 0, 0, 0)
+ , mDisplayPort(0, 0, 0, 0)
+ , mCriticalDisplayPort(0, 0, 0, 0)
+ , mScrollableRect(0, 0, 0, 0)
+ , mCumulativeResolution()
+ , mDevPixelsPerCSSPixel(1)
+ , mScrollOffset(0, 0)
+ , mZoom()
+ , mScrollGeneration(0)
+ , mSmoothScrollOffset(0, 0)
+ , mRootCompositionSize(0, 0)
+ , mDisplayPortMargins(0, 0, 0, 0)
+ , mPresShellId(-1)
+ , mViewport(0, 0, 0, 0)
+ , mExtraResolution()
+ , mPaintRequestTime()
+ , mScrollUpdateType(eNone)
+ , mIsRootContent(false)
+ , mDoSmoothScroll(false)
+ , mUseDisplayPortMargins(false)
+ , mIsScrollInfoLayer(false)
+ {
+ }
+
+ // Default copy ctor and operator= are fine
+
+ bool operator==(const FrameMetrics& aOther) const
+ {
+ // Put mScrollId at the top since it's the most likely one to fail.
+ return mScrollId == aOther.mScrollId &&
+ mPresShellResolution == aOther.mPresShellResolution &&
+ mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
+ mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
+ mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
+ mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
+ mCumulativeResolution == aOther.mCumulativeResolution &&
+ mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
+ mScrollOffset == aOther.mScrollOffset &&
+ // don't compare mZoom
+ mScrollGeneration == aOther.mScrollGeneration &&
+ mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
+ mRootCompositionSize == aOther.mRootCompositionSize &&
+ mDisplayPortMargins == aOther.mDisplayPortMargins &&
+ mPresShellId == aOther.mPresShellId &&
+ mViewport.IsEqualEdges(aOther.mViewport) &&
+ mExtraResolution == aOther.mExtraResolution &&
+ mPaintRequestTime == aOther.mPaintRequestTime &&
+ mScrollUpdateType == aOther.mScrollUpdateType &&
+ mIsRootContent == aOther.mIsRootContent &&
+ mDoSmoothScroll == aOther.mDoSmoothScroll &&
+ mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
+ mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
+ }
+
+ bool operator!=(const FrameMetrics& aOther) const
+ {
+ return !operator==(aOther);
+ }
+
+ bool IsScrollable() const
+ {
+ return mScrollId != NULL_SCROLL_ID;
+ }
+
+ CSSToScreenScale2D DisplayportPixelsPerCSSPixel() const
+ {
+ // Note: use 'mZoom * ParentLayerToLayerScale(1.0f)' as the CSS-to-Layer scale
+ // instead of LayersPixelsPerCSSPixel(), because displayport calculations
+ // are done in the context of a repaint request, where we ask Layout to
+ // repaint at a new resolution that includes any async zoom. Until this
+ // repaint request is processed, LayersPixelsPerCSSPixel() does not yet
+ // include the async zoom, but it will when the displayport is interpreted
+ // for the repaint.
+ return mZoom * ParentLayerToLayerScale(1.0f) / mExtraResolution;
+ }
+
+ CSSToLayerScale2D LayersPixelsPerCSSPixel() const
+ {
+ return mDevPixelsPerCSSPixel * mCumulativeResolution;
+ }
+
+ // Get the amount by which this frame has been zoomed since the last repaint.
+ LayerToParentLayerScale GetAsyncZoom() const
+ {
+ // The async portion of the zoom should be the same along the x and y
+ // axes.
+ return (mZoom / LayersPixelsPerCSSPixel()).ToScaleFactor();
+ }
+
+ // Ensure the scrollableRect is at least as big as the compositionBounds
+ // because the scrollableRect can be smaller if the content is not large
+ // and the scrollableRect hasn't been updated yet.
+ // We move the scrollableRect up because we don't know if we can move it
+ // down. i.e. we know that scrollableRect can go back as far as zero.
+ // but we don't know how much further ahead it can go.
+ CSSRect GetExpandedScrollableRect() const
+ {
+ CSSRect scrollableRect = mScrollableRect;
+ CSSSize compSize = CalculateCompositedSizeInCssPixels();
+ if (scrollableRect.width < compSize.width) {
+ scrollableRect.x = std::max(0.f,
+ scrollableRect.x - (compSize.width - scrollableRect.width));
+ scrollableRect.width = compSize.width;
+ }
+
+ if (scrollableRect.height < compSize.height) {
+ scrollableRect.y = std::max(0.f,
+ scrollableRect.y - (compSize.height - scrollableRect.height));
+ scrollableRect.height = compSize.height;
+ }
+
+ return scrollableRect;
+ }
+
+ CSSSize CalculateCompositedSizeInCssPixels() const
+ {
+ if (GetZoom() == CSSToParentLayerScale2D(0, 0)) {
+ return CSSSize(); // avoid division by zero
+ }
+ return mCompositionBounds.Size() / GetZoom();
+ }
+
+ CSSRect CalculateCompositedRectInCssPixels() const
+ {
+ if (GetZoom() == CSSToParentLayerScale2D(0, 0)) {
+ return CSSRect(); // avoid division by zero
+ }
+ return mCompositionBounds / GetZoom();
+ }
+
+ CSSSize CalculateBoundedCompositedSizeInCssPixels() const
+ {
+ CSSSize size = CalculateCompositedSizeInCssPixels();
+ size.width = std::min(size.width, mRootCompositionSize.width);
+ size.height = std::min(size.height, mRootCompositionSize.height);
+ return size;
+ }
+
+ CSSRect CalculateScrollRange() const
+ {
+ CSSSize scrollPortSize = CalculateCompositedSizeInCssPixels();
+ CSSRect scrollRange = mScrollableRect;
+ scrollRange.width = std::max(scrollRange.width - scrollPortSize.width, 0.0f);
+ scrollRange.height = std::max(scrollRange.height - scrollPortSize.height, 0.0f);
+ return scrollRange;
+ }
+
+ void ScrollBy(const CSSPoint& aPoint)
+ {
+ mScrollOffset += aPoint;
+ }
+
+ void ZoomBy(float aScale)
+ {
+ ZoomBy(gfxSize(aScale, aScale));
+ }
+
+ void ZoomBy(const gfxSize& aScale)
+ {
+ mZoom.xScale *= aScale.width;
+ mZoom.yScale *= aScale.height;
+ }
+
+ void CopyScrollInfoFrom(const FrameMetrics& aOther)
+ {
+ mScrollOffset = aOther.mScrollOffset;
+ mScrollGeneration = aOther.mScrollGeneration;
+ }
+
+ void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
+ {
+ mSmoothScrollOffset = aOther.mSmoothScrollOffset;
+ mScrollGeneration = aOther.mScrollGeneration;
+ mDoSmoothScroll = aOther.mDoSmoothScroll;
+ }
+
+ void UpdatePendingScrollInfo(const ScrollUpdateInfo& aInfo)
+ {
+ mScrollOffset = aInfo.mScrollOffset;
+ mScrollGeneration = aInfo.mScrollGeneration;
+ mScrollUpdateType = ePending;
+ }
+
+ void SetRepaintDrivenByUserAction(bool aUserAction)
+ {
+ mScrollUpdateType = aUserAction ? eUserAction : eNone;
+ }
+
+public:
+ void SetPresShellResolution(float aPresShellResolution)
+ {
+ mPresShellResolution = aPresShellResolution;
+ }
+
+ float GetPresShellResolution() const
+ {
+ return mPresShellResolution;
+ }
+
+ void SetCompositionBounds(const ParentLayerRect& aCompositionBounds)
+ {
+ mCompositionBounds = aCompositionBounds;
+ }
+
+ const ParentLayerRect& GetCompositionBounds() const
+ {
+ return mCompositionBounds;
+ }
+
+ void SetDisplayPort(const CSSRect& aDisplayPort)
+ {
+ mDisplayPort = aDisplayPort;
+ }
+
+ const CSSRect& GetDisplayPort() const
+ {
+ return mDisplayPort;
+ }
+
+ void SetCriticalDisplayPort(const CSSRect& aCriticalDisplayPort)
+ {
+ mCriticalDisplayPort = aCriticalDisplayPort;
+ }
+
+ const CSSRect& GetCriticalDisplayPort() const
+ {
+ return mCriticalDisplayPort;
+ }
+
+ void SetCumulativeResolution(const LayoutDeviceToLayerScale2D& aCumulativeResolution)
+ {
+ mCumulativeResolution = aCumulativeResolution;
+ }
+
+ const LayoutDeviceToLayerScale2D& GetCumulativeResolution() const
+ {
+ return mCumulativeResolution;
+ }
+
+ void SetDevPixelsPerCSSPixel(const CSSToLayoutDeviceScale& aDevPixelsPerCSSPixel)
+ {
+ mDevPixelsPerCSSPixel = aDevPixelsPerCSSPixel;
+ }
+
+ const CSSToLayoutDeviceScale& GetDevPixelsPerCSSPixel() const
+ {
+ return mDevPixelsPerCSSPixel;
+ }
+
+ void SetIsRootContent(bool aIsRootContent)
+ {
+ mIsRootContent = aIsRootContent;
+ }
+
+ bool IsRootContent() const
+ {
+ return mIsRootContent;
+ }
+
+ void SetScrollOffset(const CSSPoint& aScrollOffset)
+ {
+ mScrollOffset = aScrollOffset;
+ }
+
+ const CSSPoint& GetScrollOffset() const
+ {
+ return mScrollOffset;
+ }
+
+ void SetSmoothScrollOffset(const CSSPoint& aSmoothScrollDestination)
+ {
+ mSmoothScrollOffset = aSmoothScrollDestination;
+ }
+
+ const CSSPoint& GetSmoothScrollOffset() const
+ {
+ return mSmoothScrollOffset;
+ }
+
+ void SetZoom(const CSSToParentLayerScale2D& aZoom)
+ {
+ mZoom = aZoom;
+ }
+
+ const CSSToParentLayerScale2D& GetZoom() const
+ {
+ return mZoom;
+ }
+
+ void SetScrollOffsetUpdated(uint32_t aScrollGeneration)
+ {
+ mScrollUpdateType = eMainThread;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ void SetScrollOffsetRestored(uint32_t aScrollGeneration)
+ {
+ mScrollUpdateType = eRestore;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration)
+ {
+ mDoSmoothScroll = true;
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ ScrollOffsetUpdateType GetScrollUpdateType() const
+ {
+ return mScrollUpdateType;
+ }
+
+ bool GetScrollOffsetUpdated() const
+ {
+ return mScrollUpdateType != eNone;
+ }
+
+ bool GetDoSmoothScroll() const
+ {
+ return mDoSmoothScroll;
+ }
+
+ uint32_t GetScrollGeneration() const
+ {
+ return mScrollGeneration;
+ }
+
+ ViewID GetScrollId() const
+ {
+ return mScrollId;
+ }
+
+ void SetScrollId(ViewID scrollId)
+ {
+ mScrollId = scrollId;
+ }
+
+ void SetRootCompositionSize(const CSSSize& aRootCompositionSize)
+ {
+ mRootCompositionSize = aRootCompositionSize;
+ }
+
+ const CSSSize& GetRootCompositionSize() const
+ {
+ return mRootCompositionSize;
+ }
+
+ void SetDisplayPortMargins(const ScreenMargin& aDisplayPortMargins)
+ {
+ mDisplayPortMargins = aDisplayPortMargins;
+ }
+
+ const ScreenMargin& GetDisplayPortMargins() const
+ {
+ return mDisplayPortMargins;
+ }
+
+ void SetUseDisplayPortMargins(bool aValue)
+ {
+ mUseDisplayPortMargins = aValue;
+ }
+
+ bool GetUseDisplayPortMargins() const
+ {
+ return mUseDisplayPortMargins;
+ }
+
+ uint32_t GetPresShellId() const
+ {
+ return mPresShellId;
+ }
+
+ void SetPresShellId(uint32_t aPresShellId)
+ {
+ mPresShellId = aPresShellId;
+ }
+
+ void SetViewport(const CSSRect& aViewport)
+ {
+ mViewport = aViewport;
+ }
+
+ const CSSRect& GetViewport() const
+ {
+ return mViewport;
+ }
+
+ void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution)
+ {
+ mExtraResolution = aExtraResolution;
+ }
+
+ const ScreenToLayerScale2D& GetExtraResolution() const
+ {
+ return mExtraResolution;
+ }
+
+ const CSSRect& GetScrollableRect() const
+ {
+ return mScrollableRect;
+ }
+
+ void SetScrollableRect(const CSSRect& aScrollableRect)
+ {
+ mScrollableRect = aScrollableRect;
+ }
+
+ void SetPaintRequestTime(const TimeStamp& aTime) {
+ mPaintRequestTime = aTime;
+ }
+ const TimeStamp& GetPaintRequestTime() const {
+ return mPaintRequestTime;
+ }
+
+ void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
+ mIsScrollInfoLayer = aIsScrollInfoLayer;
+ }
+ bool IsScrollInfoLayer() const {
+ return mIsScrollInfoLayer;
+ }
+
+private:
+ // A unique ID assigned to each scrollable frame.
+ ViewID mScrollId;
+
+ // The pres-shell resolution that has been induced on the document containing
+ // this scroll frame as a result of zooming this scroll frame (whether via
+ // user action, or choosing an initial zoom level on page load). This can
+ // only be different from 1.0 for frames that are zoomable, which currently
+ // is just the root content document's root scroll frame (mIsRoot = true).
+ // This is a plain float rather than a ScaleFactor because in and of itself
+ // it does not convert between any coordinate spaces for which we have names.
+ float mPresShellResolution;
+
+ // This is the area within the widget that we're compositing to. It is in the
+ // same coordinate space as the reference frame for the scrolled frame.
+ //
+ // This is useful because, on mobile, the viewport and composition dimensions
+ // are not always the same. In this case, we calculate the displayport using
+ // an area bigger than the region we're compositing to. If we used the
+ // viewport dimensions to calculate the displayport, we'd run into situations
+ // where we're prerendering the wrong regions and the content may be clipped,
+ // or too much of it prerendered. If the composition dimensions are the same
+ // as the viewport dimensions, there is no need for this and we can just use
+ // the viewport instead.
+ //
+ // This value is valid for nested scrollable layers as well, and is still
+ // relative to the layer tree origin. This value is provided by Gecko at
+ // layout/paint time.
+ ParentLayerRect mCompositionBounds;
+
+ // The area of a frame's contents that has been painted, relative to
+ // mCompositionBounds.
+ //
+ // Note that this is structured in such a way that it doesn't depend on the
+ // method layout uses to scroll content.
+ //
+ // May be larger or smaller than |mScrollableRect|.
+ //
+ // To pre-render a margin of 100 CSS pixels around the window,
+ // { x = -100, y = - 100,
+ // width = window.innerWidth + 200, height = window.innerHeight + 200 }
+ CSSRect mDisplayPort;
+
+ // If non-empty, the area of a frame's contents that is considered critical
+ // to paint. Area outside of this area (i.e. area inside mDisplayPort, but
+ // outside of mCriticalDisplayPort) is considered low-priority, and may be
+ // painted with lower precision, or not painted at all.
+ //
+ // The same restrictions for mDisplayPort apply here.
+ CSSRect mCriticalDisplayPort;
+
+ // The scrollable bounds of a frame. This is determined by reflow.
+ // Ordinarily the x and y will be 0 and the width and height will be the
+ // size of the element being scrolled. However for RTL pages or elements
+ // the x value may be negative.
+ //
+ // This is relative to the document. It is in the same coordinate space as
+ // |mScrollOffset|, but a different coordinate space than |mViewport| and
+ // |mDisplayPort|. Note also that this coordinate system is understood by
+ // window.scrollTo().
+ //
+ // This is valid on any layer unless it has no content.
+ CSSRect mScrollableRect;
+
+ // The cumulative resolution that the current frame has been painted at.
+ // This is the product of the pres-shell resolutions of the document
+ // containing this scroll frame and its ancestors, and any css-driven
+ // resolution. This information is provided by Gecko at layout/paint time.
+ // Note that this is allowed to have different x- and y-scales, but only
+ // for subframes (mIsRoot = false). (The same applies to other scales that
+ // "inherit" the 2D-ness of this one, such as mZoom.)
+ LayoutDeviceToLayerScale2D mCumulativeResolution;
+
+ // New fields from now on should be made private and old fields should
+ // be refactored to be private.
+
+ // The conversion factor between CSS pixels and device pixels for this frame.
+ // This can vary based on a variety of things, such as reflowing-zoom. The
+ // conversion factor for device pixels to layers pixels is just the
+ // resolution.
+ CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+
+ // The position of the top-left of the CSS viewport, relative to the document
+ // (or the document relative to the viewport, if that helps understand it).
+ //
+ // Thus it is relative to the document. It is in the same coordinate space as
+ // |mScrollableRect|, but a different coordinate space than |mViewport| and
+ // |mDisplayPort|.
+ //
+ // It is required that the rect:
+ // { x = mScrollOffset.x, y = mScrollOffset.y,
+ // width = mCompositionBounds.x / mResolution.scale,
+ // height = mCompositionBounds.y / mResolution.scale }
+ // Be within |mScrollableRect|.
+ //
+ // This is valid for any layer, but is always relative to this frame and
+ // not any parents, regardless of parent transforms.
+ CSSPoint mScrollOffset;
+
+ // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
+ // but will be drawn to the screen at mZoom. In the steady state, the
+ // two will be the same, but during an async zoom action the two may
+ // diverge. This information is initialized in Gecko but updated in the APZC.
+ CSSToParentLayerScale2D mZoom;
+
+ // The scroll generation counter used to acknowledge the scroll offset update.
+ uint32_t mScrollGeneration;
+
+ // If mDoSmoothScroll is true, the scroll offset will be animated smoothly
+ // to this value.
+ CSSPoint mSmoothScrollOffset;
+
+ // The size of the root scrollable's composition bounds, but in local CSS pixels.
+ CSSSize mRootCompositionSize;
+
+ // A display port expressed as layer margins that apply to the rect of what
+ // is drawn of the scrollable element.
+ ScreenMargin mDisplayPortMargins;
+
+ uint32_t mPresShellId;
+
+ // The CSS viewport, which is the dimensions we're using to constrain the
+ // <html> element of this frame, relative to the top-left of the layer. Note
+ // that its offset is structured in such a way that it doesn't depend on the
+ // method layout uses to scroll content.
+ //
+ // This is mainly useful on the root layer, however nested iframes can have
+ // their own viewport, which will just be the size of the window of the
+ // iframe. For layers that don't correspond to a document, this metric is
+ // meaningless and invalid.
+ CSSRect mViewport;
+
+ // The extra resolution at which content in this scroll frame is drawn beyond
+ // that necessary to draw one Layer pixel per Screen pixel.
+ ScreenToLayerScale2D mExtraResolution;
+
+ // The time at which the APZC last requested a repaint for this scrollframe.
+ TimeStamp mPaintRequestTime;
+
+ // Whether mScrollOffset was updated by something other than the APZ code, and
+ // if the APZC receiving this metrics should update its local copy.
+ ScrollOffsetUpdateType mScrollUpdateType;
+
+ // Whether or not this is the root scroll frame for the root content document.
+ bool mIsRootContent:1;
+
+ // When mDoSmoothScroll, the scroll offset should be animated to
+ // smoothly transition to mScrollOffset rather than be updated instantly.
+ bool mDoSmoothScroll:1;
+
+ // If this is true then we use the display port margins on this metrics,
+ // otherwise use the display port rect.
+ bool mUseDisplayPortMargins:1;
+
+ // Whether or not this frame has a "scroll info layer" to capture events.
+ bool mIsScrollInfoLayer:1;
+
+ // WARNING!!!!
+ //
+ // When adding a new field:
+ //
+ // - First, consider whether the field can be added to ScrollMetadata
+ // instead. If so, prefer that.
+ //
+ // - Otherwise, the following places should be updated to include them
+ // (as needed):
+ // FrameMetrics::operator ==
+ // AsyncPanZoomController::NotifyLayersUpdated
+ // The ParamTraits specialization in GfxMessageUtils.h
+ //
+ // Please add new fields above this comment.
+
+ // Private helpers for IPC purposes
+ void SetDoSmoothScroll(bool aValue) {
+ mDoSmoothScroll = aValue;
+ }
+};
+
+struct ScrollSnapInfo {
+ ScrollSnapInfo()
+ : mScrollSnapTypeX(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ , mScrollSnapTypeY(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ {}
+
+ bool operator==(const ScrollSnapInfo& aOther) const
+ {
+ return mScrollSnapTypeX == aOther.mScrollSnapTypeX &&
+ mScrollSnapTypeY == aOther.mScrollSnapTypeY &&
+ mScrollSnapIntervalX == aOther.mScrollSnapIntervalX &&
+ mScrollSnapIntervalY == aOther.mScrollSnapIntervalY &&
+ mScrollSnapDestination == aOther.mScrollSnapDestination &&
+ mScrollSnapCoordinates == aOther.mScrollSnapCoordinates;
+ }
+
+ // The scroll frame's scroll-snap-type.
+ // One of NS_STYLE_SCROLL_SNAP_{NONE, MANDATORY, PROXIMITY}.
+ uint8_t mScrollSnapTypeX;
+ uint8_t mScrollSnapTypeY;
+
+ // The intervals derived from the scroll frame's scroll-snap-points.
+ Maybe<nscoord> mScrollSnapIntervalX;
+ Maybe<nscoord> mScrollSnapIntervalY;
+
+ // The scroll frame's scroll-snap-destination, in cooked form (to avoid
+ // shipping the raw nsStyleCoord::CalcValue over IPC).
+ nsPoint mScrollSnapDestination;
+
+ // The scroll-snap-coordinates of any descendant frames of the scroll frame,
+ // relative to the origin of the scrolled frame.
+ nsTArray<nsPoint> mScrollSnapCoordinates;
+};
+
+/**
+ * A clip that applies to a layer, that may be scrolled by some of the
+ * scroll frames associated with the layer.
+ */
+struct LayerClip {
+ friend struct IPC::ParamTraits<mozilla::layers::LayerClip>;
+
+public:
+ LayerClip()
+ : mClipRect()
+ , mMaskLayerIndex()
+ {}
+
+ explicit LayerClip(const ParentLayerIntRect& aClipRect)
+ : mClipRect(aClipRect)
+ , mMaskLayerIndex()
+ {}
+
+ bool operator==(const LayerClip& aOther) const
+ {
+ return mClipRect == aOther.mClipRect &&
+ mMaskLayerIndex == aOther.mMaskLayerIndex;
+ }
+
+ void SetClipRect(const ParentLayerIntRect& aClipRect) {
+ mClipRect = aClipRect;
+ }
+ const ParentLayerIntRect& GetClipRect() const {
+ return mClipRect;
+ }
+
+ void SetMaskLayerIndex(const Maybe<size_t>& aIndex) {
+ mMaskLayerIndex = aIndex;
+ }
+ const Maybe<size_t>& GetMaskLayerIndex() const {
+ return mMaskLayerIndex;
+ }
+
+private:
+ ParentLayerIntRect mClipRect;
+
+ // Optionally, specifies a mask layer that's part of the clip.
+ // This is an index into the MetricsMaskLayers array on the Layer.
+ Maybe<size_t> mMaskLayerIndex;
+};
+
+typedef Maybe<LayerClip> MaybeLayerClip; // for passing over IPDL
+
+/**
+ * Metadata about a scroll frame that's stored in the layer tree for use by
+ * the compositor (including APZ). This includes the scroll frame's FrameMetrics,
+ * as well as other metadata. We don't put the other metadata into FrameMetrics
+ * to avoid FrameMetrics becoming too bloated (as a FrameMetrics is e.g. sent
+ * over IPC for every repaint request for every active scroll frame).
+ */
+struct ScrollMetadata {
+ friend struct IPC::ParamTraits<mozilla::layers::ScrollMetadata>;
+
+ typedef FrameMetrics::ViewID ViewID;
+public:
+ static StaticAutoPtr<const ScrollMetadata> sNullMetadata; // We sometimes need an empty metadata
+
+ ScrollMetadata()
+ : mMetrics()
+ , mSnapInfo()
+ , mScrollParentId(FrameMetrics::NULL_SCROLL_ID)
+ , mBackgroundColor()
+ , mContentDescription()
+ , mLineScrollAmount(0, 0)
+ , mPageScrollAmount(0, 0)
+ , mScrollClip()
+ , mHasScrollgrab(false)
+ , mAllowVerticalScrollWithWheel(false)
+ , mIsLayersIdRoot(false)
+ , mUsesContainerScrolling(false)
+ , mForceDisableApz(false)
+ {}
+
+ bool operator==(const ScrollMetadata& aOther) const
+ {
+ return mMetrics == aOther.mMetrics &&
+ mSnapInfo == aOther.mSnapInfo &&
+ mScrollParentId == aOther.mScrollParentId &&
+ mBackgroundColor == aOther.mBackgroundColor &&
+ // don't compare mContentDescription
+ mLineScrollAmount == aOther.mLineScrollAmount &&
+ mPageScrollAmount == aOther.mPageScrollAmount &&
+ mScrollClip == aOther.mScrollClip &&
+ mHasScrollgrab == aOther.mHasScrollgrab &&
+ mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
+ mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
+ mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
+ mForceDisableApz == aOther.mForceDisableApz;
+ }
+
+ bool operator!=(const ScrollMetadata& aOther) const
+ {
+ return !operator==(aOther);
+ }
+
+ bool IsDefault() const
+ {
+ ScrollMetadata def;
+
+ def.mMetrics.SetPresShellId(mMetrics.GetPresShellId());
+ return (def == *this);
+ }
+
+ FrameMetrics& GetMetrics() { return mMetrics; }
+ const FrameMetrics& GetMetrics() const { return mMetrics; }
+
+ void SetSnapInfo(ScrollSnapInfo&& aSnapInfo) {
+ mSnapInfo = Move(aSnapInfo);
+ }
+ const ScrollSnapInfo& GetSnapInfo() const { return mSnapInfo; }
+
+ ViewID GetScrollParentId() const {
+ return mScrollParentId;
+ }
+
+ void SetScrollParentId(ViewID aParentId) {
+ mScrollParentId = aParentId;
+ }
+ const gfx::Color& GetBackgroundColor() const {
+ return mBackgroundColor;
+ }
+ void SetBackgroundColor(const gfx::Color& aBackgroundColor) {
+ mBackgroundColor = aBackgroundColor;
+ }
+ const nsCString& GetContentDescription() const {
+ return mContentDescription;
+ }
+ void SetContentDescription(const nsCString& aContentDescription) {
+ mContentDescription = aContentDescription;
+ }
+ const LayoutDeviceIntSize& GetLineScrollAmount() const {
+ return mLineScrollAmount;
+ }
+ void SetLineScrollAmount(const LayoutDeviceIntSize& size) {
+ mLineScrollAmount = size;
+ }
+ const LayoutDeviceIntSize& GetPageScrollAmount() const {
+ return mPageScrollAmount;
+ }
+ void SetPageScrollAmount(const LayoutDeviceIntSize& size) {
+ mPageScrollAmount = size;
+ }
+
+ void SetScrollClip(const Maybe<LayerClip>& aScrollClip) {
+ mScrollClip = aScrollClip;
+ }
+ const Maybe<LayerClip>& GetScrollClip() const {
+ return mScrollClip;
+ }
+ bool HasScrollClip() const {
+ return mScrollClip.isSome();
+ }
+ const LayerClip& ScrollClip() const {
+ return mScrollClip.ref();
+ }
+ LayerClip& ScrollClip() {
+ return mScrollClip.ref();
+ }
+
+ bool HasMaskLayer() const {
+ return HasScrollClip() && ScrollClip().GetMaskLayerIndex();
+ }
+ Maybe<ParentLayerIntRect> GetClipRect() const {
+ return mScrollClip.isSome() ? Some(mScrollClip->GetClipRect()) : Nothing();
+ }
+
+ void SetHasScrollgrab(bool aHasScrollgrab) {
+ mHasScrollgrab = aHasScrollgrab;
+ }
+ bool GetHasScrollgrab() const {
+ return mHasScrollgrab;
+ }
+ bool AllowVerticalScrollWithWheel() const {
+ return mAllowVerticalScrollWithWheel;
+ }
+ void SetAllowVerticalScrollWithWheel(bool aValue) {
+ mAllowVerticalScrollWithWheel = aValue;
+ }
+ void SetIsLayersIdRoot(bool aValue) {
+ mIsLayersIdRoot = aValue;
+ }
+ bool IsLayersIdRoot() const {
+ return mIsLayersIdRoot;
+ }
+ // Implemented out of line because the implementation needs gfxPrefs.h
+ // and we don't want to include that from FrameMetrics.h.
+ void SetUsesContainerScrolling(bool aValue);
+ bool UsesContainerScrolling() const {
+ return mUsesContainerScrolling;
+ }
+ void SetForceDisableApz(bool aForceDisable) {
+ mForceDisableApz = aForceDisable;
+ }
+ bool IsApzForceDisabled() const {
+ return mForceDisableApz;
+ }
+
+private:
+ FrameMetrics mMetrics;
+
+ // Information used to determine where to snap to for a given scroll.
+ ScrollSnapInfo mSnapInfo;
+
+ // The ViewID of the scrollable frame to which overscroll should be handed off.
+ ViewID mScrollParentId;
+
+ // The background color to use when overscrolling.
+ gfx::Color mBackgroundColor;
+
+ // A description of the content element corresponding to this frame.
+ // This is empty unless this is a scrollable layer and the
+ // apz.printtree pref is turned on.
+ nsCString mContentDescription;
+
+ // The value of GetLineScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mLineScrollAmount;
+
+ // The value of GetPageScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mPageScrollAmount;
+
+ // A clip to apply when compositing the layer bearing this ScrollMetadata,
+ // after applying any transform arising from scrolling this scroll frame.
+ // Note that, unlike most other fields of ScrollMetadata, this is allowed
+ // to differ between different layers scrolled by the same scroll frame.
+ // TODO: Group the fields of ScrollMetadata into sub-structures to separate
+ // fields with this property better.
+ Maybe<LayerClip> mScrollClip;
+
+ // Whether or not this frame is for an element marked 'scrollgrab'.
+ bool mHasScrollgrab:1;
+
+ // Whether or not the frame can be vertically scrolled with a mouse wheel.
+ bool mAllowVerticalScrollWithWheel:1;
+
+ // Whether these framemetrics are for the root scroll frame (root element if
+ // we don't have a root scroll frame) for its layers id.
+ bool mIsLayersIdRoot:1;
+
+ // True if scrolling using containers, false otherwise. This can be removed
+ // when containerful scrolling is eliminated.
+ bool mUsesContainerScrolling:1;
+
+ // Whether or not the compositor should actually do APZ-scrolling on this
+ // scrollframe.
+ bool mForceDisableApz:1;
+
+ // WARNING!!!!
+ //
+ // When adding new fields to ScrollMetadata, the following places should be
+ // updated to include them (as needed):
+ // ScrollMetadata::operator ==
+ // AsyncPanZoomController::NotifyLayersUpdated
+ // The ParamTraits specialization in GfxMessageUtils.h
+ //
+ // Please add new fields above this comment.
+};
+
+/**
+ * This class allows us to uniquely identify a scrollable layer. The
+ * mLayersId identifies the layer tree (corresponding to a child process
+ * and/or tab) that the scrollable layer belongs to. The mPresShellId
+ * is a temporal identifier (corresponding to the document loaded that
+ * contains the scrollable layer, which may change over time). The
+ * mScrollId corresponds to the actual frame that is scrollable.
+ */
+struct ScrollableLayerGuid {
+ uint64_t mLayersId;
+ uint32_t mPresShellId;
+ FrameMetrics::ViewID mScrollId;
+
+ ScrollableLayerGuid()
+ : mLayersId(0)
+ , mPresShellId(0)
+ , mScrollId(0)
+ {
+ }
+
+ ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId,
+ FrameMetrics::ViewID aScrollId)
+ : mLayersId(aLayersId)
+ , mPresShellId(aPresShellId)
+ , mScrollId(aScrollId)
+ {
+ }
+
+ ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics)
+ : mLayersId(aLayersId)
+ , mPresShellId(aMetrics.GetPresShellId())
+ , mScrollId(aMetrics.GetScrollId())
+ {
+ }
+
+ ScrollableLayerGuid(const ScrollableLayerGuid& other)
+ : mLayersId(other.mLayersId)
+ , mPresShellId(other.mPresShellId)
+ , mScrollId(other.mScrollId)
+ {
+ }
+
+ ~ScrollableLayerGuid()
+ {
+ }
+
+ bool operator==(const ScrollableLayerGuid& other) const
+ {
+ return mLayersId == other.mLayersId
+ && mPresShellId == other.mPresShellId
+ && mScrollId == other.mScrollId;
+ }
+
+ bool operator!=(const ScrollableLayerGuid& other) const
+ {
+ return !(*this == other);
+ }
+
+ bool operator<(const ScrollableLayerGuid& other) const
+ {
+ if (mLayersId < other.mLayersId) {
+ return true;
+ }
+ if (mLayersId == other.mLayersId) {
+ if (mPresShellId < other.mPresShellId) {
+ return true;
+ }
+ if (mPresShellId == other.mPresShellId) {
+ return mScrollId < other.mScrollId;
+ }
+ }
+ return false;
+ }
+
+ uint32_t Hash() const
+ {
+ return HashGeneric(mLayersId, mPresShellId, mScrollId);
+ }
+};
+
+template <int LogLevel>
+gfx::Log<LogLevel>& operator<<(gfx::Log<LogLevel>& log, const ScrollableLayerGuid& aGuid) {
+ return log << '(' << aGuid.mLayersId << ',' << aGuid.mPresShellId << ',' << aGuid.mScrollId << ')';
+}
+
+struct ZoomConstraints {
+ bool mAllowZoom;
+ bool mAllowDoubleTapZoom;
+ CSSToParentLayerScale mMinZoom;
+ CSSToParentLayerScale mMaxZoom;
+
+ ZoomConstraints()
+ : mAllowZoom(true)
+ , mAllowDoubleTapZoom(true)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ZoomConstraints(bool aAllowZoom,
+ bool aAllowDoubleTapZoom,
+ const CSSToParentLayerScale& aMinZoom,
+ const CSSToParentLayerScale& aMaxZoom)
+ : mAllowZoom(aAllowZoom)
+ , mAllowDoubleTapZoom(aAllowDoubleTapZoom)
+ , mMinZoom(aMinZoom)
+ , mMaxZoom(aMaxZoom)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ZoomConstraints(const ZoomConstraints& other)
+ : mAllowZoom(other.mAllowZoom)
+ , mAllowDoubleTapZoom(other.mAllowDoubleTapZoom)
+ , mMinZoom(other.mMinZoom)
+ , mMaxZoom(other.mMaxZoom)
+ {
+ MOZ_COUNT_CTOR(ZoomConstraints);
+ }
+
+ ~ZoomConstraints()
+ {
+ MOZ_COUNT_DTOR(ZoomConstraints);
+ }
+
+ bool operator==(const ZoomConstraints& other) const
+ {
+ return mAllowZoom == other.mAllowZoom
+ && mAllowDoubleTapZoom == other.mAllowDoubleTapZoom
+ && mMinZoom == other.mMinZoom
+ && mMaxZoom == other.mMaxZoom;
+ }
+
+ bool operator!=(const ZoomConstraints& other) const
+ {
+ return !(*this == other);
+ }
+};
+
+typedef Maybe<ZoomConstraints> MaybeZoomConstraints;
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_FRAMEMETRICS_H */
diff --git a/gfx/layers/GLImages.cpp b/gfx/layers/GLImages.cpp
new file mode 100644
index 000000000..e45c3e7d6
--- /dev/null
+++ b/gfx/layers/GLImages.cpp
@@ -0,0 +1,113 @@
+
+#include "GLImages.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "ScopedGLHelpers.h"
+#include "GLImages.h"
+#include "GLBlitHelper.h"
+#include "GLReadTexImageHelper.h"
+#include "GLLibraryEGL.h"
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+static RefPtr<GLContext> sSnapshotContext;
+
+EGLImageImage::EGLImageImage(EGLImage aImage, EGLSync aSync,
+ const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+ bool aOwns)
+ : GLImage(ImageFormat::EGLIMAGE),
+ mImage(aImage),
+ mSync(aSync),
+ mSize(aSize),
+ mPos(aOrigin),
+ mOwns(aOwns)
+{
+}
+
+EGLImageImage::~EGLImageImage()
+{
+ if (!mOwns) {
+ return;
+ }
+
+ if (mImage) {
+ sEGLLibrary.fDestroyImage(EGL_DISPLAY(), mImage);
+ mImage = nullptr;
+ }
+
+ if (mSync) {
+ sEGLLibrary.fDestroySync(EGL_DISPLAY(), mSync);
+ mSync = nullptr;
+ }
+}
+
+already_AddRefed<gfx::SourceSurface>
+GLImage::GetAsSourceSurface()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
+
+ if (!sSnapshotContext) {
+ nsCString discardFailureId;
+ sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE,
+ &discardFailureId);
+ if (!sSnapshotContext) {
+ NS_WARNING("Failed to create snapshot GLContext");
+ return nullptr;
+ }
+ }
+
+ sSnapshotContext->MakeCurrent();
+ ScopedTexture scopedTex(sSnapshotContext);
+ ScopedBindTexture boundTex(sSnapshotContext, scopedTex.Texture());
+
+ gfx::IntSize size = GetSize();
+ sSnapshotContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA,
+ size.width, size.height, 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+
+ ScopedFramebufferForTexture autoFBForTex(sSnapshotContext, scopedTex.Texture());
+ if (!autoFBForTex.IsComplete()) {
+ gfxCriticalError() << "GetAsSourceSurface: ScopedFramebufferForTexture failed.";
+ return nullptr;
+ }
+
+ const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
+
+ if (!sSnapshotContext->BlitHelper()->BlitImageToFramebuffer(this, size,
+ autoFBForTex.FB(),
+ destOrigin))
+ {
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> source =
+ gfx::Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!source)) {
+ return nullptr;
+ }
+
+ ScopedBindFramebuffer bind(sSnapshotContext, autoFBForTex.FB());
+ ReadPixelsIntoDataSurface(sSnapshotContext, source);
+ return source.forget();
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+SurfaceTextureImage::SurfaceTextureImage(gl::AndroidSurfaceTexture* aSurfTex,
+ const gfx::IntSize& aSize,
+ gl::OriginPos aOriginPos)
+ : GLImage(ImageFormat::SURFACE_TEXTURE),
+ mSurfaceTexture(aSurfTex),
+ mSize(aSize),
+ mOriginPos(aOriginPos)
+{
+}
+#endif
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/GLImages.h b/gfx/layers/GLImages.h
new file mode 100644
index 000000000..3e419d4b6
--- /dev/null
+++ b/gfx/layers/GLImages.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GLIMAGES_H
+#define GFX_GLIMAGES_H
+
+#include "AndroidSurfaceTexture.h"
+#include "GLContextTypes.h"
+#include "GLTypes.h"
+#include "ImageContainer.h" // for Image
+#include "ImageTypes.h" // for ImageFormat::SHARED_GLTEXTURE
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+
+namespace mozilla {
+namespace layers {
+
+class GLImage : public Image {
+public:
+ explicit GLImage(ImageFormat aFormat) : Image(nullptr, aFormat){}
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ GLImage* AsGLImage() override {
+ return this;
+ }
+};
+
+class EGLImageImage : public GLImage {
+public:
+ EGLImageImage(EGLImage aImage, EGLSync aSync,
+ const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+ bool aOwns);
+
+ gfx::IntSize GetSize() override { return mSize; }
+ gl::OriginPos GetOriginPos() const {
+ return mPos;
+ }
+ EGLImage GetImage() const {
+ return mImage;
+ }
+ EGLSync GetSync() const {
+ return mSync;
+ }
+
+ EGLImageImage* AsEGLImageImage() override {
+ return this;
+ }
+
+protected:
+ virtual ~EGLImageImage();
+
+private:
+ EGLImage mImage;
+ EGLSync mSync;
+ gfx::IntSize mSize;
+ gl::OriginPos mPos;
+ bool mOwns;
+};
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class SurfaceTextureImage : public GLImage {
+public:
+ SurfaceTextureImage(gl::AndroidSurfaceTexture* aSurfTex,
+ const gfx::IntSize& aSize,
+ gl::OriginPos aOriginPos);
+
+ gfx::IntSize GetSize() override { return mSize; }
+ gl::AndroidSurfaceTexture* GetSurfaceTexture() const {
+ return mSurfaceTexture;
+ }
+ gl::OriginPos GetOriginPos() const {
+ return mOriginPos;
+ }
+
+ SurfaceTextureImage* AsSurfaceTextureImage() override {
+ return this;
+ }
+
+private:
+ RefPtr<gl::AndroidSurfaceTexture> mSurfaceTexture;
+ gfx::IntSize mSize;
+ gl::OriginPos mOriginPos;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_GLIMAGES_H
diff --git a/gfx/layers/GPUVideoImage.h b/gfx/layers/GPUVideoImage.h
new file mode 100644
index 000000000..9a661088b
--- /dev/null
+++ b/gfx/layers/GPUVideoImage.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GPU_VIDEO_IMAGE_H
+#define GFX_GPU_VIDEO_IMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/GPUVideoTextureClient.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+namespace mozilla {
+namespace dom {
+class VideoDecoderManagerChild;
+}
+namespace layers {
+
+// Image class that refers to a decoded video frame within
+// the GPU process.
+class GPUVideoImage final : public Image {
+public:
+ GPUVideoImage(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize)
+ : Image(nullptr, ImageFormat::GPU_VIDEO)
+ , mSize(aSize)
+ {
+ // Create the TextureClient immediately since the GPUVideoTextureData
+ // is responsible for deallocating the SurfaceDescriptor.
+ //
+ // Use the RECYCLE texture flag, since it's likely that our 'real'
+ // TextureData (in the decoder thread of the GPU process) is using
+ // it too, and we want to make sure we don't send the delete message
+ // until we've stopped being used on the compositor.
+ mTextureClient =
+ TextureClient::CreateWithData(new GPUVideoTextureData(aManager, aSD, aSize),
+ TextureFlags::RECYCLE,
+ ImageBridgeChild::GetSingleton().get());
+ }
+
+ ~GPUVideoImage() override {}
+
+ gfx::IntSize GetSize() override { return mSize; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
+ {
+ if (!mTextureClient) {
+ return nullptr;
+ }
+ GPUVideoTextureData* data = mTextureClient->GetInternalData()->AsGPUVideoTextureData();
+ return data->GetAsSourceSurface();
+ }
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override
+ {
+ MOZ_ASSERT(aForwarder == ImageBridgeChild::GetSingleton(), "Must only use GPUVideo on ImageBridge");
+ return mTextureClient;
+ }
+
+private:
+ gfx::IntSize mSize;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_GPU_VIDEO_IMAGE_H
diff --git a/gfx/layers/IMFYCbCrImage.cpp b/gfx/layers/IMFYCbCrImage.cpp
new file mode 100644
index 000000000..d5943b030
--- /dev/null
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IMFYCbCrImage.h"
+#include "DeviceManagerD3D9.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureClient.h"
+#include "d3d9.h"
+
+namespace mozilla {
+namespace layers {
+
+IMFYCbCrImage::IMFYCbCrImage(IMFMediaBuffer* aBuffer, IMF2DBuffer* a2DBuffer)
+ : RecyclingPlanarYCbCrImage(nullptr)
+ , mBuffer(aBuffer)
+ , m2DBuffer(a2DBuffer)
+{}
+
+IMFYCbCrImage::~IMFYCbCrImage()
+{
+ if (m2DBuffer) {
+ m2DBuffer->Unlock2D();
+ }
+ else {
+ mBuffer->Unlock();
+ }
+}
+
+struct AutoLockTexture
+{
+ AutoLockTexture(ID3D11Texture2D* aTexture)
+ {
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: IMFYCbCrImage timeout");
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ }
+ }
+
+ ~AutoLockTexture()
+ {
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+
+ RefPtr<IDXGIKeyedMutex> mMutex;
+};
+
+static already_AddRefed<IDirect3DTexture9>
+InitTextures(IDirect3DDevice9* aDevice,
+ const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ RefPtr<IDirect3DSurface9>& aSurface,
+ HANDLE& aHandle,
+ D3DLOCKED_RECT& aLockedRect)
+{
+ if (!aDevice) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> result;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_DEFAULT,
+ getter_AddRefs(result), &aHandle))) {
+ return nullptr;
+ }
+ if (!result) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> tmpTexture;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(tmpTexture), nullptr))) {
+ return nullptr;
+ }
+ if (!tmpTexture) {
+ return nullptr;
+ }
+
+ tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface));
+ if (FAILED(aSurface->LockRect(&aLockedRect, nullptr, 0)) ||
+ !aLockedRect.pBits) {
+ NS_WARNING("Could not lock surface");
+ return nullptr;
+ }
+
+ return result.forget();
+}
+
+static bool
+FinishTextures(IDirect3DDevice9* aDevice,
+ IDirect3DTexture9* aTexture,
+ IDirect3DSurface9* aSurface)
+{
+ if (!aDevice) {
+ return false;
+ }
+
+ HRESULT hr = aSurface->UnlockRect();
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<IDirect3DSurface9> dstSurface;
+ hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ hr = aDevice->UpdateSurface(aSurface, nullptr, dstSurface, nullptr);
+ if (FAILED(hr)) {
+ return false;
+ }
+ return true;
+}
+
+static bool UploadData(IDirect3DDevice9* aDevice,
+ RefPtr<IDirect3DTexture9>& aTexture,
+ HANDLE& aHandle,
+ uint8_t* aSrc,
+ const gfx::IntSize& aSrcSize,
+ int32_t aSrcStride)
+{
+ RefPtr<IDirect3DSurface9> surf;
+ D3DLOCKED_RECT rect;
+ aTexture = InitTextures(aDevice, aSrcSize, D3DFMT_A8, surf, aHandle, rect);
+ if (!aTexture) {
+ return false;
+ }
+
+ if (aSrcStride == rect.Pitch) {
+ memcpy(rect.pBits, aSrc, rect.Pitch * aSrcSize.height);
+ } else {
+ for (int i = 0; i < aSrcSize.height; i++) {
+ memcpy((uint8_t*)rect.pBits + i * rect.Pitch,
+ aSrc + i * aSrcStride,
+ aSrcSize.width);
+ }
+ }
+
+ return FinishTextures(aDevice, aTexture, surf);
+}
+
+TextureClient*
+IMFYCbCrImage::GetD3D9TextureClient(KnowsCompositor* aForwarder)
+{
+ RefPtr<IDirect3DDevice9> device = DeviceManagerD3D9::GetDevice();
+ if (!device) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureY;
+ HANDLE shareHandleY = 0;
+ if (!UploadData(device, textureY, shareHandleY,
+ mData.mYChannel, mData.mYSize, mData.mYStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureCb;
+ HANDLE shareHandleCb = 0;
+ if (!UploadData(device, textureCb, shareHandleCb,
+ mData.mCbChannel, mData.mCbCrSize, mData.mCbCrStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureCr;
+ HANDLE shareHandleCr = 0;
+ if (!UploadData(device, textureCr, shareHandleCr,
+ mData.mCrChannel, mData.mCbCrSize, mData.mCbCrStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DQuery9> query;
+ HRESULT hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query));
+ hr = query->Issue(D3DISSUE_END);
+
+ int iterations = 0;
+ bool valid = false;
+ while (iterations < 10) {
+ HRESULT hr = query->GetData(nullptr, 0, D3DGETDATA_FLUSH);
+ if (hr == S_FALSE) {
+ Sleep(1);
+ iterations++;
+ continue;
+ }
+ if (hr == S_OK) {
+ valid = true;
+ }
+ break;
+ }
+
+ if (!valid) {
+ return nullptr;
+ }
+
+ mTextureClient = TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT,
+ textureY, textureCb, textureCr,
+ shareHandleY, shareHandleCb, shareHandleCr,
+ GetSize(), mData.mYSize, mData.mCbCrSize),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+
+ return mTextureClient;
+}
+
+TextureClient*
+IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (mTextureClient) {
+ return mTextureClient;
+ }
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ }
+
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (!device || backend != LayersBackend::LAYERS_D3D11) {
+ if (backend == LayersBackend::LAYERS_D3D9 ||
+ backend == LayersBackend::LAYERS_D3D11) {
+ return GetD3D9TextureClient(aForwarder);
+ }
+ return nullptr;
+ }
+
+ if (!gfx::DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ if (mData.mYStride < 0 || mData.mCbCrStride < 0) {
+ // D3D11 only supports unsigned stride values.
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM,
+ mData.mYSize.width, mData.mYSize.height, 1, 1);
+
+ if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ } else {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+
+ RefPtr<ID3D11Texture2D> textureY;
+ D3D11_SUBRESOURCE_DATA yData = { mData.mYChannel, (UINT)mData.mYStride, 0 };
+ HRESULT hr = device->CreateTexture2D(&newDesc, &yData, getter_AddRefs(textureY));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ newDesc.Width = mData.mCbCrSize.width;
+ newDesc.Height = mData.mCbCrSize.height;
+
+ RefPtr<ID3D11Texture2D> textureCb;
+ D3D11_SUBRESOURCE_DATA cbData = { mData.mCbChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &cbData, getter_AddRefs(textureCb));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<ID3D11Texture2D> textureCr;
+ D3D11_SUBRESOURCE_DATA crData = { mData.mCrChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &crData, getter_AddRefs(textureCr));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ // Even though the textures we created are meant to be protected by a keyed mutex,
+ // it appears that D3D doesn't include the initial memory upload within this
+ // synchronization. Add an empty lock/unlock pair since that appears to
+ // be sufficient to make sure we synchronize.
+ {
+ AutoLockTexture lockCr(textureCr);
+ }
+
+ mTextureClient = TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT,
+ textureY, textureCb, textureCr,
+ GetSize(), mData.mYSize, mData.mCbCrSize),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/IMFYCbCrImage.h b/gfx/layers/IMFYCbCrImage.h
new file mode 100644
index 000000000..9a4298727
--- /dev/null
+++ b/gfx/layers/IMFYCbCrImage.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMFYCBCRIMAGE_H
+#define GFX_IMFYCBCRIMAGE_H
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mfidl.h"
+
+namespace mozilla {
+namespace layers {
+
+class IMFYCbCrImage : public RecyclingPlanarYCbCrImage
+{
+public:
+ IMFYCbCrImage(IMFMediaBuffer* aBuffer, IMF2DBuffer* a2DBuffer);
+
+ virtual bool IsValid() { return true; }
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+protected:
+
+ TextureClient* GetD3D9TextureClient(KnowsCompositor* aForwarder);
+
+ ~IMFYCbCrImage();
+
+ RefPtr<IMFMediaBuffer> mBuffer;
+ RefPtr<IMF2DBuffer> m2DBuffer;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namepace layers
+} // namespace mozilla
+
+#endif // GFX_D3DSURFACEIMAGE_H
diff --git a/gfx/layers/IPDLActor.h b/gfx/layers/IPDLActor.h
new file mode 100644
index 000000000..c94a67f52
--- /dev/null
+++ b/gfx/layers/IPDLActor.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_IPDLACTOR_H
+#define MOZILLA_LAYERS_IPDLACTOR_H
+
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/Unused.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+/// A base class to facilitate the deallocation of IPDL actors.
+///
+/// Implements the parent side of the simple deallocation handshake.
+/// Override the Destroy method rather than the ActorDestroy method.
+template<typename Protocol>
+class ParentActor : public Protocol
+{
+public:
+ ParentActor() : mDestroyed(false) {}
+
+ ~ParentActor() { MOZ_ASSERT(mDestroyed); }
+
+ bool CanSend() const { return !mDestroyed; }
+
+ // Override this rather than ActorDestroy
+ virtual void Destroy() {}
+
+ virtual bool RecvDestroy() override
+ {
+ DestroyIfNeeded();
+ Unused << Protocol::Send__delete__(this);
+ return true;
+ }
+
+ typedef ipc::IProtocol::ActorDestroyReason Why;
+
+ virtual void ActorDestroy(Why) override {
+ DestroyIfNeeded();
+ }
+
+protected:
+ void DestroyIfNeeded() {
+ if (!mDestroyed) {
+ Destroy();
+ mDestroyed = true;
+ }
+ }
+
+private:
+ bool mDestroyed;
+};
+
+} // namespace
+} // namespace
+
+#endif
diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp
new file mode 100644
index 000000000..8072e0401
--- /dev/null
+++ b/gfx/layers/ImageContainer.cpp
@@ -0,0 +1,797 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "ImageContainer.h"
+#include <string.h> // for memcpy, memset
+#include "GLImages.h" // for SurfaceTextureImage
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxUtils.h" // for gfxUtils
+#include "libyuv.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/ImageContainerChild.h"
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/SharedPlanarYCbCrImage.h"
+#include "mozilla/layers/SharedRGBImage.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "nsISupportsUtils.h" // for NS_IF_ADDREF
+#include "YCbCrUtils.h" // for YCbCr conversions
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/CheckedInt.h"
+
+#ifdef XP_MACOSX
+#include "mozilla/gfx/QuartzSupport.h"
+#endif
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#include <d3d10_1.h>
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace android;
+using namespace mozilla::gfx;
+
+Atomic<int32_t> Image::sSerialCounter(0);
+
+Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
+
+RefPtr<PlanarYCbCrImage>
+ImageFactory::CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin *aRecycleBin)
+{
+ return new RecyclingPlanarYCbCrImage(aRecycleBin);
+}
+
+BufferRecycleBin::BufferRecycleBin()
+ : mLock("mozilla.layers.BufferRecycleBin.mLock")
+ // This member is only valid when the bin is not empty and will be properly
+ // initialized in RecycleBuffer, but initializing it here avoids static analysis
+ // noise.
+ , mRecycledBufferSize(0)
+{
+}
+
+void
+BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer, uint32_t aSize)
+{
+ MutexAutoLock lock(mLock);
+
+ if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
+ mRecycledBuffers.Clear();
+ }
+ mRecycledBufferSize = aSize;
+ mRecycledBuffers.AppendElement(Move(aBuffer));
+}
+
+UniquePtr<uint8_t[]>
+BufferRecycleBin::GetBuffer(uint32_t aSize)
+{
+ MutexAutoLock lock(mLock);
+
+ if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
+ return MakeUnique<uint8_t[]>(aSize);
+
+ uint32_t last = mRecycledBuffers.Length() - 1;
+ UniquePtr<uint8_t[]> result = Move(mRecycledBuffers[last]);
+ mRecycledBuffers.RemoveElementAt(last);
+ return result;
+}
+
+void
+BufferRecycleBin::ClearRecycledBuffers()
+{
+ MutexAutoLock lock(mLock);
+ if (!mRecycledBuffers.IsEmpty()) {
+ mRecycledBuffers.Clear();
+ }
+ mRecycledBufferSize = 0;
+}
+
+void
+ImageContainer::EnsureImageClient(bool aCreate)
+{
+ // If we're not forcing a new ImageClient, then we can skip this if we don't have an existing
+ // ImageClient, or if the existing one belongs to an IPC actor that is still open.
+ if (!aCreate && (!mImageClient || mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen())) {
+ return;
+ }
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (imageBridge) {
+ mIPDLChild = new ImageContainerChild(this);
+ mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this, mIPDLChild);
+ if (mImageClient) {
+ mAsyncContainerID = mImageClient->GetAsyncID();
+ }
+ }
+}
+
+ImageContainer::ImageContainer(Mode flag)
+: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+ mGenerationCounter(++sGenerationCounter),
+ mPaintCount(0),
+ mDroppedImageCount(0),
+ mImageFactory(new ImageFactory()),
+ mRecycleBin(new BufferRecycleBin()),
+ mCurrentProducerID(-1)
+{
+ if (flag == ASYNCHRONOUS) {
+ EnsureImageClient(true);
+ } else {
+ mAsyncContainerID = sInvalidAsyncContainerId;
+ }
+}
+
+ImageContainer::ImageContainer(uint64_t aAsyncContainerID)
+ : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+ mGenerationCounter(++sGenerationCounter),
+ mPaintCount(0),
+ mDroppedImageCount(0),
+ mImageFactory(nullptr),
+ mRecycleBin(nullptr),
+ mAsyncContainerID(aAsyncContainerID),
+ mCurrentProducerID(-1)
+{
+ MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
+}
+
+ImageContainer::~ImageContainer()
+{
+ if (mIPDLChild) {
+ mIPDLChild->ForgetImageContainer();
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->ReleaseImageContainer(mIPDLChild);
+ }
+ }
+}
+
+RefPtr<PlanarYCbCrImage>
+ImageContainer::CreatePlanarYCbCrImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ EnsureImageClient(false);
+ if (mImageClient && mImageClient->AsImageClientSingle()) {
+ return new SharedPlanarYCbCrImage(mImageClient);
+ }
+ return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
+}
+
+RefPtr<SharedRGBImage>
+ImageContainer::CreateSharedRGBImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ EnsureImageClient(false);
+ if (!mImageClient || !mImageClient->AsImageClientSingle()) {
+ return nullptr;
+ }
+ return new SharedRGBImage(mImageClient);
+}
+
+void
+ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ mGenerationCounter = ++sGenerationCounter;
+
+ if (!aImages.IsEmpty()) {
+ NS_ASSERTION(mCurrentImages.IsEmpty() ||
+ mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
+ mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
+ "frame IDs shouldn't go backwards");
+ if (aImages[0].mProducerID != mCurrentProducerID) {
+ mFrameIDsNotYetComposited.Clear();
+ mCurrentProducerID = aImages[0].mProducerID;
+ } else if (!aImages[0].mTimeStamp.IsNull()) {
+ // Check for expired frames
+ for (auto& img : mCurrentImages) {
+ if (img.mProducerID != aImages[0].mProducerID ||
+ img.mTimeStamp.IsNull() ||
+ img.mTimeStamp >= aImages[0].mTimeStamp) {
+ break;
+ }
+ if (!img.mComposited && !img.mTimeStamp.IsNull() &&
+ img.mFrameID != aImages[0].mFrameID) {
+ mFrameIDsNotYetComposited.AppendElement(img.mFrameID);
+ }
+ }
+
+ // Remove really old frames, assuming they'll never be composited.
+ const uint32_t maxFrames = 100;
+ if (mFrameIDsNotYetComposited.Length() > maxFrames) {
+ uint32_t dropFrames = mFrameIDsNotYetComposited.Length() - maxFrames;
+ mDroppedImageCount += dropFrames;
+ mFrameIDsNotYetComposited.RemoveElementsAt(0, dropFrames);
+ }
+ }
+ }
+
+ nsTArray<OwningImage> newImages;
+
+ for (uint32_t i = 0; i < aImages.Length(); ++i) {
+ NS_ASSERTION(aImages[i].mImage, "image can't be null");
+ NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
+ "Multiple images require timestamps");
+ if (i > 0) {
+ NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
+ "Timestamps must not decrease");
+ NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
+ "FrameIDs must increase");
+ NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
+ "ProducerIDs must be the same");
+ }
+ OwningImage* img = newImages.AppendElement();
+ img->mImage = aImages[i].mImage;
+ img->mTimeStamp = aImages[i].mTimeStamp;
+ img->mFrameID = aImages[i].mFrameID;
+ img->mProducerID = aImages[i].mProducerID;
+ for (auto& oldImg : mCurrentImages) {
+ if (oldImg.mFrameID == img->mFrameID &&
+ oldImg.mProducerID == img->mProducerID) {
+ img->mComposited = oldImg.mComposited;
+ break;
+ }
+ }
+ }
+
+ mCurrentImages.SwapElements(newImages);
+}
+
+void
+ImageContainer::ClearImagesFromImageBridge()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ SetCurrentImageInternal(nsTArray<NonOwningImage>());
+}
+
+void
+ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
+{
+ MOZ_ASSERT(!aImages.IsEmpty());
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mImageClient) {
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->UpdateImageClient(mImageClient, this);
+ }
+ }
+ SetCurrentImageInternal(aImages);
+}
+
+void
+ImageContainer::ClearAllImages()
+{
+ if (mImageClient) {
+ // Let ImageClient release all TextureClients. This doesn't return
+ // until ImageBridge has called ClearCurrentImageFromImageBridge.
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->FlushAllImages(mImageClient, this);
+ }
+ return;
+ }
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ SetCurrentImageInternal(nsTArray<NonOwningImage>());
+}
+
+void
+ImageContainer::ClearCachedResources()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (mImageClient && mImageClient->AsImageClientSingle()) {
+ if (!mImageClient->HasTextureClientRecycler()) {
+ return;
+ }
+ mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
+ return;
+ }
+ return mRecycleBin->ClearRecycledBuffers();
+}
+
+void
+ImageContainer::SetCurrentImageInTransaction(Image *aImage)
+{
+ AutoTArray<NonOwningImage,1> images;
+ images.AppendElement(NonOwningImage(aImage));
+ SetCurrentImagesInTransaction(images);
+}
+
+void
+ImageContainer::SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+ NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
+
+ SetCurrentImageInternal(aImages);
+}
+
+bool ImageContainer::IsAsync() const
+{
+ return mAsyncContainerID != sInvalidAsyncContainerId;
+}
+
+uint64_t ImageContainer::GetAsyncContainerID()
+{
+ NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
+ EnsureImageClient(false);
+ return mAsyncContainerID;
+}
+
+bool
+ImageContainer::HasCurrentImage()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ return !mCurrentImages.IsEmpty();
+}
+
+void
+ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
+ uint32_t* aGenerationCounter)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ *aImages = mCurrentImages;
+ if (aGenerationCounter) {
+ *aGenerationCounter = mGenerationCounter;
+ }
+}
+
+gfx::IntSize
+ImageContainer::GetCurrentSize()
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (mCurrentImages.IsEmpty()) {
+ return gfx::IntSize(0, 0);
+ }
+
+ return mCurrentImages[0].mImage->GetSize();
+}
+
+void
+ImageContainer::NotifyCompositeInternal(const ImageCompositeNotification& aNotification)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // An image composition notification is sent the first time a particular
+ // image is composited by an ImageHost. Thus, every time we receive such
+ // a notification, a new image has been painted.
+ ++mPaintCount;
+
+ if (aNotification.producerID() == mCurrentProducerID) {
+ uint32_t i;
+ for (i = 0; i < mFrameIDsNotYetComposited.Length(); ++i) {
+ if (mFrameIDsNotYetComposited[i] <= aNotification.frameID()) {
+ if (mFrameIDsNotYetComposited[i] < aNotification.frameID()) {
+ ++mDroppedImageCount;
+ }
+ } else {
+ break;
+ }
+ }
+ mFrameIDsNotYetComposited.RemoveElementsAt(0, i);
+ for (auto& img : mCurrentImages) {
+ if (img.mFrameID == aNotification.frameID()) {
+ img.mComposited = true;
+ }
+ }
+ }
+
+ if (!aNotification.imageTimeStamp().IsNull()) {
+ mPaintDelay = aNotification.firstCompositeTimeStamp() -
+ aNotification.imageTimeStamp();
+ }
+}
+
+PlanarYCbCrImage::PlanarYCbCrImage()
+ : Image(nullptr, ImageFormat::PLANAR_YCBCR)
+ , mOffscreenFormat(SurfaceFormat::UNKNOWN)
+ , mBufferSize(0)
+{
+}
+
+RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage()
+{
+ if (mBuffer) {
+ mRecycleBin->RecycleBuffer(Move(mBuffer), mBufferSize);
+ }
+}
+
+size_t
+RecyclingPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // Ignoring:
+ // - mData - just wraps mBuffer
+ // - Surfaces should be reported under gfx-surfaces-*:
+ // - mSourceSurface
+ // - Base class:
+ // - mImplData is not used
+ // Not owned:
+ // - mRecycleBin
+ size_t size = aMallocSizeOf(mBuffer.get());
+
+ // Could add in the future:
+ // - mBackendData (from base class)
+
+ return size;
+}
+
+UniquePtr<uint8_t[]>
+RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
+{
+ return mRecycleBin->GetBuffer(aSize);
+}
+
+static void
+CopyPlane(uint8_t *aDst, const uint8_t *aSrc,
+ const gfx::IntSize &aSize, int32_t aStride, int32_t aSkip)
+{
+ if (!aSkip) {
+ // Fast path: planar input.
+ memcpy(aDst, aSrc, aSize.height * aStride);
+ } else {
+ int32_t height = aSize.height;
+ int32_t width = aSize.width;
+ for (int y = 0; y < height; ++y) {
+ const uint8_t *src = aSrc;
+ uint8_t *dst = aDst;
+ // Slow path
+ for (int x = 0; x < width; ++x) {
+ *dst++ = *src++;
+ src += aSkip;
+ }
+ aSrc += aStride;
+ aDst += aStride;
+ }
+ }
+}
+
+bool
+RecyclingPlanarYCbCrImage::CopyData(const Data& aData)
+{
+ mData = aData;
+
+ // update buffer size
+ // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
+ const auto checkedSize =
+ CheckedInt<uint32_t>(mData.mCbCrStride) * mData.mCbCrSize.height * 2 +
+ CheckedInt<uint32_t>(mData.mYStride) * mData.mYSize.height;
+
+ if (!checkedSize.isValid())
+ return false;
+
+ const auto size = checkedSize.value();
+
+ // get new buffer
+ mBuffer = AllocateBuffer(size);
+ if (!mBuffer)
+ return false;
+
+ // update buffer size
+ mBufferSize = size;
+
+ mData.mYChannel = mBuffer.get();
+ mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
+ mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
+
+ CopyPlane(mData.mYChannel, aData.mYChannel,
+ mData.mYSize, mData.mYStride, mData.mYSkip);
+ CopyPlane(mData.mCbChannel, aData.mCbChannel,
+ mData.mCbCrSize, mData.mCbCrStride, mData.mCbSkip);
+ CopyPlane(mData.mCrChannel, aData.mCrChannel,
+ mData.mCbCrSize, mData.mCbCrStride, mData.mCrSkip);
+
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+ return true;
+}
+
+gfxImageFormat
+PlanarYCbCrImage::GetOffscreenFormat()
+{
+ return mOffscreenFormat == SurfaceFormat::UNKNOWN ?
+ gfxVars::OffscreenFormat() :
+ mOffscreenFormat;
+}
+
+bool
+PlanarYCbCrImage::AdoptData(const Data &aData)
+{
+ mData = aData;
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+ return true;
+}
+
+uint8_t*
+RecyclingPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
+{
+ // get new buffer
+ mBuffer = AllocateBuffer(aSize);
+ if (mBuffer) {
+ // update buffer size
+ mBufferSize = aSize;
+ }
+ return mBuffer.get();
+}
+
+already_AddRefed<gfx::SourceSurface>
+PlanarYCbCrImage::GetAsSourceSurface()
+{
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ gfx::IntSize size(mSize);
+ gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+ gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
+ if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(), mapping.GetStride());
+
+ mSourceSurface = surface;
+
+ return surface.forget();
+}
+
+NVImage::NVImage()
+ : Image(nullptr, ImageFormat::NV_IMAGE)
+ , mBufferSize(0)
+{
+}
+
+NVImage::~NVImage()
+{
+}
+
+IntSize
+NVImage::GetSize()
+{
+ return mSize;
+}
+
+IntRect
+NVImage::GetPictureRect()
+{
+ return mData.GetPictureRect();
+}
+
+already_AddRefed<SourceSurface>
+NVImage::GetAsSourceSurface()
+{
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
+ // logics in PlanarYCbCrImage::GetAsSourceSurface().
+ const int bufferLength = mData.mYSize.height * mData.mYStride +
+ mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
+ uint8_t* buffer = new uint8_t[bufferLength];
+
+ Data aData = mData;
+ aData.mCbCrStride = aData.mCbCrSize.width;
+ aData.mCbSkip = 0;
+ aData.mCrSkip = 0;
+ aData.mYChannel = buffer;
+ aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
+ aData.mCrChannel = aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
+
+ if (mData.mCbChannel < mData.mCrChannel) { // NV12
+ libyuv::NV12ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCbChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ } else { // NV21
+ libyuv::NV21ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCrChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ }
+
+ // The logics in PlanarYCbCrImage::GetAsSourceSurface().
+ gfx::IntSize size(mSize);
+ gfx::SurfaceFormat format =
+ gfx::ImageFormatToSurfaceFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(), mapping.GetStride());
+
+ mSourceSurface = surface;
+
+ // Release the temporary buffer.
+ delete[] buffer;
+
+ return surface.forget();
+}
+
+bool
+NVImage::IsValid()
+{
+ return !!mBufferSize;
+}
+
+uint32_t
+NVImage::GetBufferSize() const
+{
+ return mBufferSize;
+}
+
+NVImage*
+NVImage::AsNVImage()
+{
+ return this;
+};
+
+bool
+NVImage::SetData(const Data& aData)
+{
+ MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
+ MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
+
+ // Calculate buffer size
+ // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
+ const auto checkedSize =
+ CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
+ CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
+
+ if (!checkedSize.isValid())
+ return false;
+
+ const auto size = checkedSize.value();
+
+ // Allocate a new buffer.
+ mBuffer = AllocateBuffer(size);
+ if (!mBuffer) {
+ return false;
+ }
+
+ // Update mBufferSize.
+ mBufferSize = size;
+
+ // Update mData.
+ mData = aData;
+ mData.mYChannel = mBuffer.get();
+ mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
+ mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
+
+ // Update mSize.
+ mSize = aData.mPicSize;
+
+ // Copy the input data into mBuffer.
+ // This copies the y-channel and the interleaving CbCr-channel.
+ memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
+
+ return true;
+}
+
+const NVImage::Data*
+NVImage::GetData() const
+{
+ return &mData;
+}
+
+UniquePtr<uint8_t>
+NVImage::AllocateBuffer(uint32_t aSize)
+{
+ UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
+ return buffer;
+}
+
+SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
+ : Image(nullptr, ImageFormat::CAIRO_SURFACE),
+ mSize(aSize),
+ mSourceSurface(aSourceSurface),
+ mTextureFlags(TextureFlags::DEFAULT)
+{}
+
+SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
+ : Image(nullptr, ImageFormat::CAIRO_SURFACE),
+ mSize(aSourceSurface->GetSize()),
+ mSourceSurface(aSourceSurface),
+ mTextureFlags(TextureFlags::DEFAULT)
+{}
+
+SourceSurfaceImage::~SourceSurfaceImage()
+{
+}
+
+TextureClient*
+SourceSurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (!aForwarder) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> textureClient = mTextureClients.Get(aForwarder->GetSerial());
+ if (textureClient) {
+ return textureClient;
+ }
+
+ RefPtr<SourceSurface> surface = GetAsSourceSurface();
+ MOZ_ASSERT(surface);
+ if (!surface) {
+ return nullptr;
+ }
+
+ if (!textureClient) {
+ // gfx::BackendType::NONE means default to content backend
+ textureClient = TextureClient::CreateFromSurface(aForwarder,
+ surface,
+ BackendSelector::Content,
+ mTextureFlags,
+ ALLOC_DEFAULT);
+ }
+ if (!textureClient) {
+ return nullptr;
+ }
+
+ textureClient->SyncWithObject(aForwarder->GetSyncObject());
+
+ mTextureClients.Put(aForwarder->GetSerial(), textureClient);
+ return textureClient;
+}
+
+PImageContainerChild*
+ImageContainer::GetPImageContainerChild()
+{
+ return mIPDLChild;
+}
+
+ImageContainer::ProducerID
+ImageContainer::AllocateProducerID()
+{
+ // Callable on all threads.
+ static Atomic<ImageContainer::ProducerID> sProducerID(0u);
+ return ++sProducerID;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h
new file mode 100644
index 000000000..a2f404e17
--- /dev/null
+++ b/gfx/layers/ImageContainer.h
@@ -0,0 +1,903 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGECONTAINER_H
+#define GFX_IMAGECONTAINER_H
+
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include "gfxTypes.h"
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Mutex.h" // for Mutex
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // For IntSize
+#include "mozilla/layers/GonkNativeHandle.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc
+#include "nsAutoRef.h" // for nsCountedRef
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/Atomics.h"
+#include "mozilla/WeakPtr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "nsDataHashtable.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/UniquePtr.h"
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * We need to be able to hold a reference to a Moz2D SourceSurface from Image
+ * subclasses. This is potentially a problem since Images can be addrefed
+ * or released off the main thread. We can ensure that we never AddRef
+ * a SourceSurface off the main thread, but we might want to Release due
+ * to an Image being destroyed off the main thread.
+ *
+ * We use nsCountedRef<nsMainThreadSourceSurfaceRef> to reference the
+ * SourceSurface. When AddRefing, we assert that we're on the main thread.
+ * When Releasing, if we're not on the main thread, we post an event to
+ * the main thread to do the actual release.
+ */
+class nsMainThreadSourceSurfaceRef;
+
+template <>
+class nsAutoRefTraits<nsMainThreadSourceSurfaceRef> {
+public:
+ typedef mozilla::gfx::SourceSurface* RawRef;
+
+ /**
+ * The XPCOM event that will do the actual release on the main thread.
+ */
+ class SurfaceReleaser : public mozilla::Runnable {
+ public:
+ explicit SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
+ NS_IMETHOD Run() override {
+ mRef->Release();
+ return NS_OK;
+ }
+ RawRef mRef;
+ };
+
+ static RawRef Void() { return nullptr; }
+ static void Release(RawRef aRawRef)
+ {
+ if (NS_IsMainThread()) {
+ aRawRef->Release();
+ return;
+ }
+ nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
+ NS_DispatchToMainThread(runnable);
+ }
+ static void AddRef(RawRef aRawRef)
+ {
+ NS_ASSERTION(NS_IsMainThread(),
+ "Can only add a reference on the main thread");
+ aRawRef->AddRef();
+ }
+};
+
+class nsOwningThreadSourceSurfaceRef;
+
+template <>
+class nsAutoRefTraits<nsOwningThreadSourceSurfaceRef> {
+public:
+ typedef mozilla::gfx::SourceSurface* RawRef;
+
+ /**
+ * The XPCOM event that will do the actual release on the creation thread.
+ */
+ class SurfaceReleaser : public mozilla::Runnable {
+ public:
+ explicit SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
+ NS_IMETHOD Run() override {
+ mRef->Release();
+ return NS_OK;
+ }
+ RawRef mRef;
+ };
+
+ static RawRef Void() { return nullptr; }
+ void Release(RawRef aRawRef)
+ {
+ MOZ_ASSERT(mOwningThread);
+ bool current;
+ mOwningThread->IsOnCurrentThread(&current);
+ if (current) {
+ aRawRef->Release();
+ return;
+ }
+ nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
+ mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+ }
+ void AddRef(RawRef aRawRef)
+ {
+ MOZ_ASSERT(!mOwningThread);
+ NS_GetCurrentThread(getter_AddRefs(mOwningThread));
+ aRawRef->AddRef();
+ }
+
+private:
+ nsCOMPtr<nsIThread> mOwningThread;
+};
+
+#endif
+
+#ifdef XP_WIN
+struct ID3D10Texture2D;
+struct ID3D10Device;
+struct ID3D10ShaderResourceView;
+#endif
+
+typedef void* HANDLE;
+
+namespace mozilla {
+
+
+namespace layers {
+
+class ImageClient;
+class ImageCompositeNotification;
+class ImageContainerChild;
+class PImageContainerChild;
+class SharedPlanarYCbCrImage;
+class PlanarYCbCrImage;
+class TextureClient;
+class KnowsCompositor;
+class NVImage;
+
+struct ImageBackendData
+{
+ virtual ~ImageBackendData() {}
+
+protected:
+ ImageBackendData() {}
+};
+
+/* Forward declarations for Image derivatives. */
+class GLImage;
+class EGLImageImage;
+class SharedRGBImage;
+#ifdef MOZ_WIDGET_ANDROID
+class SurfaceTextureImage;
+#elif defined(XP_MACOSX)
+class MacIOSurfaceImage;
+#endif
+
+/**
+ * A class representing a buffer of pixel data. The data can be in one
+ * of various formats including YCbCr.
+ *
+ * Create an image using an ImageContainer. Fill the image with data, and
+ * then call ImageContainer::SetImage to display it. An image must not be
+ * modified after calling SetImage. Image implementations do not need to
+ * perform locking; when filling an Image, the Image client is responsible
+ * for ensuring only one thread accesses the Image at a time, and after
+ * SetImage the image is immutable.
+ *
+ * When resampling an Image, only pixels within the buffer should be
+ * sampled. For example, cairo images should be sampled in EXTEND_PAD mode.
+ */
+class Image {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Image)
+
+public:
+ ImageFormat GetFormat() { return mFormat; }
+ void* GetImplData() { return mImplData; }
+
+ virtual gfx::IntSize GetSize() = 0;
+ virtual gfx::IntPoint GetOrigin()
+ {
+ return gfx::IntPoint(0, 0);
+ }
+ virtual gfx::IntRect GetPictureRect()
+ {
+ return gfx::IntRect(GetOrigin().x, GetOrigin().y, GetSize().width, GetSize().height);
+ }
+
+ ImageBackendData* GetBackendData(LayersBackend aBackend)
+ { return mBackendData[aBackend]; }
+ void SetBackendData(LayersBackend aBackend, ImageBackendData* aData)
+ { mBackendData[aBackend] = aData; }
+
+ int32_t GetSerial() { return mSerial; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() = 0;
+
+ virtual bool IsValid() { return true; }
+
+ virtual uint8_t* GetBuffer() { return nullptr; }
+
+ /**
+ * For use with the TextureForwarder only (so that the later can
+ * synchronize the TextureClient with the TextureHost).
+ */
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) { return nullptr; }
+
+ /* Access to derived classes. */
+ virtual EGLImageImage* AsEGLImageImage() { return nullptr; }
+ virtual GLImage* AsGLImage() { return nullptr; }
+#ifdef MOZ_WIDGET_ANDROID
+ virtual SurfaceTextureImage* AsSurfaceTextureImage() { return nullptr; }
+#endif
+#ifdef XP_MACOSX
+ virtual MacIOSurfaceImage* AsMacIOSurfaceImage() { return nullptr; }
+#endif
+ virtual PlanarYCbCrImage* AsPlanarYCbCrImage() { return nullptr; }
+
+ virtual NVImage* AsNVImage() { return nullptr; }
+
+protected:
+ Image(void* aImplData, ImageFormat aFormat) :
+ mImplData(aImplData),
+ mSerial(++sSerialCounter),
+ mFormat(aFormat)
+ {}
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Image() {}
+
+ mozilla::EnumeratedArray<mozilla::layers::LayersBackend,
+ mozilla::layers::LayersBackend::LAYERS_LAST,
+ nsAutoPtr<ImageBackendData>>
+ mBackendData;
+
+ void* mImplData;
+ int32_t mSerial;
+ ImageFormat mFormat;
+
+ static mozilla::Atomic<int32_t> sSerialCounter;
+};
+
+/**
+ * A RecycleBin is owned by an ImageContainer. We store buffers in it that we
+ * want to recycle from one image to the next.It's a separate object from
+ * ImageContainer because images need to store a strong ref to their RecycleBin
+ * and we must avoid creating a reference loop between an ImageContainer and
+ * its active image.
+ */
+class BufferRecycleBin final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferRecycleBin)
+
+ //typedef mozilla::gl::GLContext GLContext;
+
+public:
+ BufferRecycleBin();
+
+ void RecycleBuffer(mozilla::UniquePtr<uint8_t[]> aBuffer, uint32_t aSize);
+ // Returns a recycled buffer of the right size, or allocates a new buffer.
+ mozilla::UniquePtr<uint8_t[]> GetBuffer(uint32_t aSize);
+ virtual void ClearRecycledBuffers();
+private:
+ typedef mozilla::Mutex Mutex;
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~BufferRecycleBin()
+ {
+ }
+
+ // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
+ // and mRecycledTextureSizes
+ Mutex mLock;
+
+ // We should probably do something to prune this list on a timer so we don't
+ // eat excess memory while video is paused...
+ nsTArray<mozilla::UniquePtr<uint8_t[]>> mRecycledBuffers;
+ // This is only valid if mRecycledBuffers is non-empty
+ uint32_t mRecycledBufferSize;
+};
+
+/**
+ * A class that manages Image creation for a LayerManager. The only reason
+ * we need a separate class here is that LayerManagers aren't threadsafe
+ * (because layers can only be used on the main thread) and we want to
+ * be able to create images from any thread, to facilitate video playback
+ * without involving the main thread, for example.
+ * Different layer managers can implement child classes of this making it
+ * possible to create layer manager specific images.
+ * This class is not meant to be used directly but rather can be set on an
+ * image container. This is usually done by the layer system internally and
+ * not explicitly by users. For PlanarYCbCr or Cairo images the default
+ * implementation will creates images whose data lives in system memory, for
+ * MacIOSurfaces the default implementation will be a simple MacIOSurface
+ * wrapper.
+ */
+
+class ImageFactory
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
+protected:
+ friend class ImageContainer;
+
+ ImageFactory() {}
+ virtual ~ImageFactory() {}
+
+ virtual RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage(
+ const gfx::IntSize& aScaleHint,
+ BufferRecycleBin *aRecycleBin);
+};
+
+/**
+ * A class that manages Images for an ImageLayer. The only reason
+ * we need a separate class here is that ImageLayers aren't threadsafe
+ * (because layers can only be used on the main thread) and we want to
+ * be able to set the current Image from any thread, to facilitate
+ * video playback without involving the main thread, for example.
+ *
+ * An ImageContainer can operate in one of these modes:
+ * 1) Normal. Triggered by constructing the ImageContainer with
+ * DISABLE_ASYNC or when compositing is happening on the main thread.
+ * SetCurrentImages changes ImageContainer state but nothing is sent to the
+ * compositor until the next layer transaction.
+ * 2) Asynchronous. Initiated by constructing the ImageContainer with
+ * ENABLE_ASYNC when compositing is happening on the main thread.
+ * SetCurrentImages sends a message through the ImageBridge to the compositor
+ * thread to update the image, without going through the main thread or
+ * a layer transaction.
+ * The ImageContainer uses a shared memory block containing a cross-process mutex
+ * to communicate with the compositor thread. SetCurrentImage synchronously
+ * updates the shared state to point to the new image and the old image
+ * is immediately released (not true in Normal or Asynchronous modes).
+ */
+class ImageContainer final : public SupportsWeakPtr<ImageContainer>
+{
+ friend class ImageContainerChild;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
+
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
+
+ enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 };
+
+ static const uint64_t sInvalidAsyncContainerId = 0;
+
+ explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
+
+ /**
+ * Create ImageContainer just to hold another ASYNCHRONOUS ImageContainer's
+ * async container ID.
+ * @param aAsyncContainerID async container ID for which we are a proxy
+ */
+ explicit ImageContainer(uint64_t aAsyncContainerID);
+
+ typedef uint32_t FrameID;
+ typedef uint32_t ProducerID;
+
+ RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
+
+ // Factory methods for shared image types.
+ RefPtr<SharedRGBImage> CreateSharedRGBImage();
+
+ struct NonOwningImage {
+ explicit NonOwningImage(Image* aImage = nullptr,
+ TimeStamp aTimeStamp = TimeStamp(),
+ FrameID aFrameID = 0,
+ ProducerID aProducerID = 0)
+ : mImage(aImage), mTimeStamp(aTimeStamp), mFrameID(aFrameID),
+ mProducerID(aProducerID) {}
+ Image* mImage;
+ TimeStamp mTimeStamp;
+ FrameID mFrameID;
+ ProducerID mProducerID;
+ };
+ /**
+ * Set aImages as the list of timestamped to display. The Images must have
+ * been created by this ImageContainer.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ * aImages must be non-empty. The first timestamp in the list may be
+ * null but the others must not be, and the timestamps must increase.
+ * Every element of aImages must have non-null mImage.
+ * mFrameID can be zero, in which case you won't get meaningful
+ * painted/dropped frame counts. Otherwise you should use a unique and
+ * increasing ID for each decoded and submitted frame (but it's OK to
+ * pass the same frame to SetCurrentImages).
+ * mProducerID is a unique ID for the stream of images. A change in the
+ * mProducerID means changing to a new mFrameID namespace. All frames in
+ * aImages must have the same mProducerID.
+ *
+ * The Image data must not be modified after this method is called!
+ * Note that this must not be called if ENABLE_ASYNC has not been set.
+ *
+ * The implementation calls CurrentImageChanged() while holding
+ * mReentrantMonitor.
+ *
+ * If this ImageContainer has an ImageClient for async video:
+ * Schedule a task to send the image to the compositor using the
+ * PImageBridge protcol without using the main thread.
+ */
+ void SetCurrentImages(const nsTArray<NonOwningImage>& aImages);
+
+ /**
+ * Clear all images. Let ImageClient release all TextureClients.
+ */
+ void ClearAllImages();
+
+ /**
+ * Clear any resources that are not immediately necessary. This may be called
+ * in low-memory conditions.
+ */
+ void ClearCachedResources();
+
+ /**
+ * Clear the current images.
+ * This function is expect to be called only from a CompositableClient
+ * that belongs to ImageBridgeChild. Created to prevent dead lock.
+ * See Bug 901224.
+ */
+ void ClearImagesFromImageBridge();
+
+ /**
+ * Set an Image as the current image to display. The Image must have
+ * been created by this ImageContainer.
+ * Must be called on the main thread, within a layers transaction.
+ *
+ * This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ * aImage can be null. While it's null, nothing will be painted.
+ *
+ * The Image data must not be modified after this method is called!
+ * Note that this must not be called if ENABLE_ASYNC been set.
+ *
+ * You won't get meaningful painted/dropped counts when using this method.
+ */
+ void SetCurrentImageInTransaction(Image* aImage);
+ void SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages);
+
+ /**
+ * Returns true if this ImageContainer uses the ImageBridge IPDL protocol.
+ *
+ * Can be called from any thread.
+ */
+ bool IsAsync() const;
+
+ /**
+ * If this ImageContainer uses ImageBridge, returns the ID associated to
+ * this container, for use in the ImageBridge protocol.
+ * Returns 0 if this ImageContainer does not use ImageBridge. Note that
+ * 0 is always an invalid ID for asynchronous image containers.
+ *
+ * Can be called from any thread.
+ */
+ uint64_t GetAsyncContainerID();
+
+ /**
+ * Returns if the container currently has an image.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ */
+ bool HasCurrentImage();
+
+ struct OwningImage {
+ OwningImage() : mFrameID(0), mProducerID(0), mComposited(false) {}
+ RefPtr<Image> mImage;
+ TimeStamp mTimeStamp;
+ FrameID mFrameID;
+ ProducerID mProducerID;
+ bool mComposited;
+ };
+ /**
+ * Copy the current Image list to aImages.
+ * This has to add references since otherwise there are race conditions
+ * where the current image is destroyed before the caller can add
+ * a reference.
+ * Can be called on any thread.
+ * May return an empty list to indicate there is no current image.
+ * If aGenerationCounter is non-null, sets *aGenerationCounter to a value
+ * that's unique for this ImageContainer state.
+ */
+ void GetCurrentImages(nsTArray<OwningImage>* aImages,
+ uint32_t* aGenerationCounter = nullptr);
+
+ /**
+ * Returns the size of the image in pixels.
+ * Can be called on any thread. This method takes mReentrantMonitor when accessing
+ * thread-shared state.
+ */
+ gfx::IntSize GetCurrentSize();
+
+ /**
+ * Sets a size that the image is expected to be rendered at.
+ * This is a hint for image backends to optimize scaling.
+ * Default implementation in this class is to ignore the hint.
+ * Can be called on any thread. This method takes mReentrantMonitor
+ * when accessing thread-shared state.
+ */
+ void SetScaleHint(const gfx::IntSize& aScaleHint)
+ { mScaleHint = aScaleHint; }
+
+ void SetImageFactory(ImageFactory *aFactory)
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mImageFactory = aFactory ? aFactory : new ImageFactory();
+ }
+
+ ImageFactory* GetImageFactory() const
+ {
+ return mImageFactory;
+ }
+
+ /**
+ * Returns the delay between the last composited image's presentation
+ * timestamp and when it was first composited. It's possible for the delay
+ * to be negative if the first image in the list passed to SetCurrentImages
+ * has a presentation timestamp greater than "now".
+ * Returns 0 if the composited image had a null timestamp, or if no
+ * image has been composited yet.
+ */
+ TimeDuration GetPaintDelay()
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mPaintDelay;
+ }
+
+ /**
+ * Returns the number of images which have been contained in this container
+ * and painted at least once. Can be called from any thread.
+ */
+ uint32_t GetPaintCount() {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mPaintCount;
+ }
+
+ /**
+ * An entry in the current image list "expires" when the entry has an
+ * non-null timestamp, and in a SetCurrentImages call the new image list is
+ * non-empty, the timestamp of the first new image is non-null and greater
+ * than the timestamp associated with the image, and the first new image's
+ * frameID is not the same as the entry's.
+ * Every expired image that is never composited is counted as dropped.
+ */
+ uint32_t GetDroppedImageCount()
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ return mDroppedImageCount;
+ }
+
+ PImageContainerChild* GetPImageContainerChild();
+
+ /**
+ * Main thread only.
+ */
+ static ProducerID AllocateProducerID();
+
+private:
+ typedef mozilla::ReentrantMonitor ReentrantMonitor;
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~ImageContainer();
+
+ void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages);
+
+ // This is called to ensure we have an active image, this may not be true
+ // when we're storing image information in a RemoteImageData structure.
+ // NOTE: If we have remote data mRemoteDataMutex should be locked when
+ // calling this function!
+ void EnsureActiveImage();
+
+ void EnsureImageClient(bool aCreate);
+
+ void NotifyCompositeInternal(const ImageCompositeNotification& aNotification);
+
+ // ReentrantMonitor to protect thread safe access to the "current
+ // image", and any other state which is shared between threads.
+ ReentrantMonitor mReentrantMonitor;
+
+ nsTArray<OwningImage> mCurrentImages;
+
+ // Updates every time mActiveImage changes
+ uint32_t mGenerationCounter;
+
+ // Number of contained images that have been painted at least once. It's up
+ // to the ImageContainer implementation to ensure accesses to this are
+ // threadsafe.
+ uint32_t mPaintCount;
+
+ // See GetPaintDelay. Accessed only with mReentrantMonitor held.
+ TimeDuration mPaintDelay;
+
+ // See GetDroppedImageCount. Accessed only with mReentrantMonitor held.
+ uint32_t mDroppedImageCount;
+
+ // This is the image factory used by this container, layer managers using
+ // this container can set an alternative image factory that will be used to
+ // create images for this container.
+ RefPtr<ImageFactory> mImageFactory;
+
+ gfx::IntSize mScaleHint;
+
+ RefPtr<BufferRecycleBin> mRecycleBin;
+
+ // This member points to an ImageClient if this ImageContainer was
+ // sucessfully created with ENABLE_ASYNC, or points to null otherwise.
+ // 'unsuccessful' in this case only means that the ImageClient could not
+ // be created, most likely because off-main-thread compositing is not enabled.
+ // In this case the ImageContainer is perfectly usable, but it will forward
+ // frames to the compositor through transactions in the main thread rather than
+ // asynchronusly using the ImageBridge IPDL protocol.
+ RefPtr<ImageClient> mImageClient;
+
+ uint64_t mAsyncContainerID;
+
+ nsTArray<FrameID> mFrameIDsNotYetComposited;
+ // ProducerID for last current image(s), including the frames in
+ // mFrameIDsNotYetComposited
+ ProducerID mCurrentProducerID;
+
+ // Object must be released on the ImageBridge thread. Field is immutable
+ // after creation of the ImageContainer.
+ RefPtr<ImageContainerChild> mIPDLChild;
+
+ static mozilla::Atomic<uint32_t> sGenerationCounter;
+};
+
+class AutoLockImage
+{
+public:
+ explicit AutoLockImage(ImageContainer *aContainer)
+ {
+ aContainer->GetCurrentImages(&mImages);
+ }
+
+ bool HasImage() const { return !mImages.IsEmpty(); }
+ Image* GetImage() const
+ {
+ return mImages.IsEmpty() ? nullptr : mImages[0].mImage.get();
+ }
+
+private:
+ AutoTArray<ImageContainer::OwningImage,4> mImages;
+};
+
+struct PlanarYCbCrData {
+ // Luminance buffer
+ uint8_t* mYChannel;
+ int32_t mYStride;
+ gfx::IntSize mYSize;
+ int32_t mYSkip;
+ // Chroma buffers
+ uint8_t* mCbChannel;
+ uint8_t* mCrChannel;
+ int32_t mCbCrStride;
+ gfx::IntSize mCbCrSize;
+ int32_t mCbSkip;
+ int32_t mCrSkip;
+ // Picture region
+ uint32_t mPicX;
+ uint32_t mPicY;
+ gfx::IntSize mPicSize;
+ StereoMode mStereoMode;
+ YUVColorSpace mYUVColorSpace;
+
+ gfx::IntRect GetPictureRect() const {
+ return gfx::IntRect(mPicX, mPicY,
+ mPicSize.width,
+ mPicSize.height);
+ }
+
+ PlanarYCbCrData()
+ : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
+ , mCbChannel(nullptr), mCrChannel(nullptr)
+ , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
+ , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
+ , mYUVColorSpace(YUVColorSpace::BT601)
+ {}
+};
+
+/****** Image subtypes for the different formats ******/
+
+/**
+ * We assume that the image data is in the REC 470M color space (see
+ * Theora specification, section 4.3.1).
+ *
+ * The YCbCr format can be:
+ *
+ * 4:4:4 - CbCr width/height are the same as Y.
+ * 4:2:2 - CbCr width is half that of Y. Height is the same.
+ * 4:2:0 - CbCr width and height is half that of Y.
+ *
+ * The color format is detected based on the height/width ratios
+ * defined above.
+ *
+ * The Image that is rendered is the picture region defined by
+ * mPicX, mPicY and mPicSize. The size of the rendered image is
+ * mPicSize, not mYSize or mCbCrSize.
+ *
+ * mYSkip, mCbSkip, mCrSkip are added to support various output
+ * formats from hardware decoder. They are per-pixel skips in the
+ * source image.
+ *
+ * For example when image width is 640, mYStride is 670, mYSkip is 3,
+ * the mYChannel buffer looks like:
+ *
+ * |<----------------------- mYStride ----------------------------->|
+ * |<----------------- mYSize.width --------------->|
+ * 0 3 6 9 12 15 18 21 659 669
+ * |----------------------------------------------------------------|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%|
+ * | |<->|
+ * mYSkip
+ */
+class PlanarYCbCrImage : public Image {
+public:
+ typedef PlanarYCbCrData Data;
+
+ enum {
+ MAX_DIMENSION = 16384
+ };
+
+ virtual ~PlanarYCbCrImage() {}
+
+ /**
+ * This makes a copy of the data buffers, in order to support functioning
+ * in all different layer managers.
+ */
+ virtual bool CopyData(const Data& aData) = 0;
+
+ /**
+ * This doesn't make a copy of the data buffers. Can be used when mBuffer is
+ * pre allocated with AllocateAndGetNewBuffer(size) and then AdoptData is
+ * called to only update the picture size, planes etc. fields in mData.
+ * The GStreamer media backend uses this to decode into PlanarYCbCrImage(s)
+ * directly.
+ */
+ virtual bool AdoptData(const Data &aData);
+
+ /**
+ * This allocates and returns a new buffer
+ */
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) = 0;
+
+ /**
+ * Ask this Image to not convert YUV to RGB during SetData, and make
+ * the original data available through GetData. This is optional,
+ * and not all PlanarYCbCrImages will support it.
+ */
+ virtual void SetDelayedConversion(bool aDelayed) { }
+
+ /**
+ * Grab the original YUV data. This is optional.
+ */
+ virtual const Data* GetData() { return &mData; }
+
+ /**
+ * Return the number of bytes of heap memory used to store this image.
+ */
+ virtual uint32_t GetDataSize() { return mBufferSize; }
+
+ virtual bool IsValid() { return !!mBufferSize; }
+
+ virtual gfx::IntSize GetSize() { return mSize; }
+
+ virtual gfx::IntPoint GetOrigin() { return mOrigin; }
+
+ explicit PlanarYCbCrImage();
+
+ virtual SharedPlanarYCbCrImage *AsSharedPlanarYCbCrImage() { return nullptr; }
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const = 0;
+
+ PlanarYCbCrImage* AsPlanarYCbCrImage() { return this; }
+
+protected:
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
+
+ void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
+ gfxImageFormat GetOffscreenFormat();
+
+ Data mData;
+ gfx::IntPoint mOrigin;
+ gfx::IntSize mSize;
+ gfxImageFormat mOffscreenFormat;
+ nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
+ uint32_t mBufferSize;
+};
+
+class RecyclingPlanarYCbCrImage: public PlanarYCbCrImage {
+public:
+ explicit RecyclingPlanarYCbCrImage(BufferRecycleBin *aRecycleBin) : mRecycleBin(aRecycleBin) {}
+ virtual ~RecyclingPlanarYCbCrImage() override;
+ virtual bool CopyData(const Data& aData) override;
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+protected:
+
+ /**
+ * Return a buffer to store image data in.
+ */
+ mozilla::UniquePtr<uint8_t[]> AllocateBuffer(uint32_t aSize);
+
+ RefPtr<BufferRecycleBin> mRecycleBin;
+ mozilla::UniquePtr<uint8_t[]> mBuffer;
+};
+
+/**
+ * NVImage is used to store YUV420SP_NV12 and YUV420SP_NV21 data natively, which
+ * are not supported by PlanarYCbCrImage. (PlanarYCbCrImage only stores YUV444P,
+ * YUV422P and YUV420P, it converts YUV420SP_NV12 and YUV420SP_NV21 data into
+ * YUV420P in its PlanarYCbCrImage::SetData() method.)
+ *
+ * PlanarYCbCrData is able to express all the YUV family and so we keep use it
+ * in NVImage.
+ */
+class NVImage: public Image {
+ typedef PlanarYCbCrData Data;
+
+public:
+ explicit NVImage();
+ virtual ~NVImage() override;
+
+ // Methods inherited from layers::Image.
+ virtual gfx::IntSize GetSize() override;
+ virtual gfx::IntRect GetPictureRect() override;
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual bool IsValid() override;
+ virtual NVImage* AsNVImage() override;
+
+ // Methods mimic layers::PlanarYCbCrImage.
+ virtual bool SetData(const Data& aData);
+ virtual const Data* GetData() const;
+ virtual uint32_t GetBufferSize() const;
+
+protected:
+
+ /**
+ * Return a buffer to store image data in.
+ */
+ mozilla::UniquePtr<uint8_t> AllocateBuffer(uint32_t aSize);
+
+ mozilla::UniquePtr<uint8_t> mBuffer;
+ uint32_t mBufferSize;
+ gfx::IntSize mSize;
+ Data mData;
+ nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
+};
+
+/**
+ * Currently, the data in a SourceSurfaceImage surface is treated as being in the
+ * device output color space. This class is very simple as all backends
+ * have to know about how to deal with drawing a cairo image.
+ */
+class SourceSurfaceImage final : public Image {
+public:
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
+ {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ void SetTextureFlags(TextureFlags aTextureFlags) { mTextureFlags = aTextureFlags; }
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual gfx::IntSize GetSize() override { return mSize; }
+
+ SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface);
+ explicit SourceSurfaceImage(gfx::SourceSurface* aSourceSurface);
+ ~SourceSurfaceImage();
+
+private:
+ gfx::IntSize mSize;
+ nsCountedRef<nsOwningThreadSourceSurfaceRef> mSourceSurface;
+ nsDataHashtable<nsUint32HashKey, RefPtr<TextureClient> > mTextureClients;
+ TextureFlags mTextureFlags;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ImageDataSerializer.cpp b/gfx/layers/ImageDataSerializer.cpp
new file mode 100644
index 000000000..08ed83bd9
--- /dev/null
+++ b/gfx/layers/ImageDataSerializer.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageDataSerializer.h"
+#include "gfx2DGlue.h" // for SurfaceFormatToImageFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc
+#include "mozilla/gfx/Types.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+namespace ImageDataSerializer {
+
+using namespace gfx;
+
+int32_t
+ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth)
+{
+ return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat));
+}
+
+int32_t
+GetRGBStride(const RGBDescriptor& aDescriptor)
+{
+ return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width);
+}
+
+uint32_t
+ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat)
+{
+ MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0);
+
+ // This takes care of checking whether there could be overflow
+ // with enough margin for the metadata.
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return 0;
+ }
+
+ // Note we're passing height instad of the bpp parameter, but the end
+ // result is the same - and the bpp was already taken care of in the
+ // ComputeRGBStride function.
+ int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width),
+ aSize.height);
+
+ if (bufsize < 0) {
+ // This should not be possible thanks to Factory::AllowedSurfaceSize
+ return 0;
+ }
+
+ return bufsize;
+}
+
+
+
+// Minimum required shmem size in bytes
+uint32_t
+ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
+ const gfx::IntSize& aCbCrSize, int32_t aCbCrStride)
+{
+ MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
+
+ if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
+ !gfx::Factory::AllowedSurfaceSize(IntSize(aCbCrStride, aCbCrSize.height))) {
+ return 0;
+ }
+ // Overflow checks are performed in AllowedSurfaceSize
+ return GetAlignedStride<4>(aYSize.height, aYStride) +
+ 2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
+}
+
+// Minimum required shmem size in bytes
+uint32_t
+ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize)
+{
+ return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
+}
+
+uint32_t
+ComputeYCbCrBufferSize(uint32_t aBufferSize)
+{
+ return GetAlignedStride<4>(aBufferSize, 1);
+}
+
+void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
+ int32_t cbCrStride, int32_t cbCrHeight,
+ uint32_t& outYOffset, uint32_t& outCbOffset,
+ uint32_t& outCrOffset)
+{
+ outYOffset = 0;
+ outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight);
+ outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight);
+}
+
+gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return aDescriptor.get_RGBDescriptor().format();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return gfx::SurfaceFormat::YUV;
+ default:
+ MOZ_CRASH("GFX: FormatFromBufferDescriptor");
+ }
+}
+
+gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return aDescriptor.get_RGBDescriptor().size();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return aDescriptor.get_YCbCrDescriptor().ySize();
+ default:
+ MOZ_CRASH("GFX: SizeFromBufferDescriptor");
+ }
+}
+
+Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+}
+
+Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TRGBDescriptor:
+ return Nothing();
+ case BufferDescriptor::TYCbCrDescriptor:
+ return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
+ default:
+ MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
+ }
+}
+
+uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.yOffset();
+}
+
+uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.cbOffset();
+}
+
+uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
+{
+ return aBuffer + aDescriptor.crOffset();
+}
+
+already_AddRefed<DataSourceSurface>
+DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface)
+{
+ gfx::IntSize ySize = aDescriptor.ySize();
+ gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
+ int32_t yStride = ySize.width;
+ int32_t cbCrStride = cbCrSize.width;
+
+ RefPtr<DataSourceSurface> result;
+ if (aSurface) {
+ MOZ_ASSERT(aSurface->GetSize() == ySize);
+ MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+ if (aSurface->GetSize() == ySize &&
+ aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) {
+ result = aSurface;
+ }
+ }
+
+ if (!result) {
+ result =
+ Factory::CreateDataSourceSurface(ySize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
+ return nullptr;
+ }
+
+ layers::PlanarYCbCrData ycbcrData;
+ ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
+ ycbcrData.mYStride = yStride;
+ ycbcrData.mYSize = ySize;
+ ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
+ ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
+ ycbcrData.mCbCrStride = cbCrStride;
+ ycbcrData.mCbCrSize = cbCrSize;
+ ycbcrData.mPicSize = ySize;
+ ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+
+ gfx::ConvertYCbCrToRGB(ycbcrData,
+ gfx::SurfaceFormat::B8G8R8X8,
+ ySize,
+ map.mData,
+ map.mStride);
+
+ result->Unmap();
+ return result.forget();
+}
+
+void
+ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
+ const YCbCrDescriptor& aDescriptor,
+ const gfx::SurfaceFormat& aDestFormat,
+ const gfx::IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride)
+{
+ MOZ_ASSERT(aBuffer);
+ gfx::IntSize ySize = aDescriptor.ySize();
+ gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
+ int32_t yStride = ySize.width;
+ int32_t cbCrStride = cbCrSize.width;
+
+ layers::PlanarYCbCrData ycbcrData;
+ ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
+ ycbcrData.mYStride = yStride;
+ ycbcrData.mYSize = ySize;
+ ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
+ ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
+ ycbcrData.mCbCrStride = cbCrStride;
+ ycbcrData.mCbCrSize = cbCrSize;
+ ycbcrData.mPicSize = ySize;
+ ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+
+ gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride);
+}
+
+} // namespace ImageDataSerializer
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ImageDataSerializer.h b/gfx/layers/ImageDataSerializer.h
new file mode 100644
index 000000000..53a589d21
--- /dev/null
+++ b/gfx/layers/ImageDataSerializer.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_BLOBSURFACE_H
+#define GFX_LAYERS_BLOBSURFACE_H
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class DrawTarget;
+} // namespace gfx
+} // namespace mozilla
+
+namespace mozilla {
+namespace layers {
+
+namespace ImageDataSerializer {
+
+// RGB
+
+int32_t ComputeRGBStride(gfx::SurfaceFormat aFormat, int32_t aWidth);
+
+int32_t GetRGBStride(const RGBDescriptor& aDescriptor);
+
+uint32_t ComputeRGBBufferSize(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+
+// YCbCr
+
+///This function is meant as a helper to know how much shared memory we need
+///to allocate in a shmem in order to place a shared YCbCr image blob of
+///given dimensions.
+uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
+ int32_t aYStride,
+ const gfx::IntSize& aCbCrSize,
+ int32_t aCbCrStride);
+uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
+ const gfx::IntSize& aCbCrSize);
+
+uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
+
+void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
+ int32_t cbCrStride, int32_t cbCrHeight,
+ uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset);
+
+gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
+uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
+
+already_AddRefed<gfx::DataSourceSurface>
+DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface = nullptr);
+
+void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
+ const YCbCrDescriptor& aDescriptor,
+ const gfx::SurfaceFormat& aDestFormat,
+ const gfx::IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride);
+
+} // ImageDataSerializer
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ImageLayers.cpp b/gfx/layers/ImageLayers.cpp
new file mode 100644
index 000000000..454ddb921
--- /dev/null
+++ b/gfx/layers/ImageLayers.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageLayers.h"
+#include "ImageContainer.h" // for ImageContainer
+#include "gfxRect.h" // for gfxRect
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for ImageContainer::Release, etc
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageLayer::ImageLayer(LayerManager* aManager, void* aImplData)
+: Layer(aManager, aImplData), mSamplingFilter(gfx::SamplingFilter::GOOD)
+, mScaleMode(ScaleMode::SCALE_NONE)
+{}
+
+ImageLayer::~ImageLayer()
+{}
+
+void ImageLayer::SetContainer(ImageContainer* aContainer)
+{
+ mContainer = aContainer;
+}
+
+void ImageLayer::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+{
+ gfx::Matrix4x4 local = GetLocalTransform();
+
+ // Snap image edges to pixel boundaries
+ gfxRect sourceRect(0, 0, 0, 0);
+ if (mContainer) {
+ sourceRect.SizeTo(mContainer->GetCurrentSize());
+ }
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+
+ if (mScaleMode != ScaleMode::SCALE_NONE &&
+ sourceRect.width != 0.0 && sourceRect.height != 0.0) {
+ NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+ "No other scalemodes than stretch and none supported yet.");
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
+
+ mEffectiveTransformForBuffer =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ } else {
+ mEffectiveTransformForBuffer = mEffectiveTransform;
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ImageLayers.h b/gfx/layers/ImageLayers.h
new file mode 100644
index 000000000..9c5d4f4f0
--- /dev/null
+++ b/gfx/layers/ImageLayers.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGELAYER_H
+#define GFX_IMAGELAYER_H
+
+#include "Layers.h" // for Layer, etc
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/LayersTypes.h"
+#include "nsAutoPtr.h" // for nsRefPtr
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class ImageContainer;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+/**
+ * A Layer which renders an Image.
+ */
+class ImageLayer : public Layer {
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the ImageContainer. aContainer must have the same layer manager
+ * as this layer.
+ */
+ virtual void SetContainer(ImageContainer* aContainer);
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the filter used to resample this image if necessary.
+ */
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mSamplingFilter != aSamplingFilter) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this));
+ mSamplingFilter = aSamplingFilter;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the size to scale the image to and the mode at which to scale.
+ */
+ void SetScaleToSize(const gfx::IntSize &aSize, ScaleMode aMode)
+ {
+ if (mScaleToSize != aSize || mScaleMode != aMode) {
+ mScaleToSize = aSize;
+ mScaleMode = aMode;
+ Mutated();
+ }
+ }
+
+
+ ImageContainer* GetContainer() { return mContainer; }
+ gfx::SamplingFilter GetSamplingFilter() { return mSamplingFilter; }
+ const gfx::IntSize& GetScaleToSize() { return mScaleToSize; }
+ ScaleMode GetScaleMode() { return mScaleMode; }
+
+ MOZ_LAYER_DECL_NAME("ImageLayer", TYPE_IMAGE)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
+
+ virtual const gfx::Matrix4x4& GetEffectiveTransformForBuffer() const override
+ {
+ return mEffectiveTransformForBuffer;
+ }
+
+protected:
+ ImageLayer(LayerManager* aManager, void* aImplData);
+ ~ImageLayer();
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ RefPtr<ImageContainer> mContainer;
+ gfx::SamplingFilter mSamplingFilter;
+ gfx::IntSize mScaleToSize;
+ ScaleMode mScaleMode;
+ gfx::Matrix4x4 mEffectiveTransformForBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_IMAGELAYER_H */
diff --git a/gfx/layers/ImageTypes.h b/gfx/layers/ImageTypes.h
new file mode 100644
index 000000000..7a3b81f6e
--- /dev/null
+++ b/gfx/layers/ImageTypes.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_IMAGETYPES_H
+#define GFX_IMAGETYPES_H
+
+namespace mozilla {
+
+enum class ImageFormat {
+ /**
+ * The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
+ * support this format, because the Ogg video decoder depends on it.
+ * The maximum image width and height is 16384.
+ */
+ PLANAR_YCBCR,
+
+ /**
+ * The NV_IMAGE format creates a NVImage. The PLANAR_YCBCR together with this
+ * complete the YUV format family.
+ */
+ NV_IMAGE,
+
+ /**
+ * The GRALLOC_PLANAR_YCBCR format creates a GrallocImage, a subtype of
+ * PlanarYCbCrImage. It takes a PlanarYCbCrImage data or the raw gralloc
+ * data and can be used as a texture by Gonk backend directly.
+ */
+ GRALLOC_PLANAR_YCBCR,
+
+ /**
+ * The GONK_CAMERA_IMAGE format creates a GonkCameraImage, which contains two
+ * parts. One is GrallocImage image for preview image. Another one is
+ * MediaBuffer from Gonk recording image. The preview image can be rendered in
+ * a layer for display. And the MediaBuffer will be used in component like OMX
+ * encoder. It is for GUM to support preview and recording image on Gonk
+ * camera.
+ */
+ GONK_CAMERA_IMAGE,
+
+ /**
+ * The SHARED_RGB format creates a SharedRGBImage, which stores RGB data in
+ * shared memory. Some Android hardware video decoders require this format.
+ * Currently only used on Android.
+ */
+ SHARED_RGB,
+
+ /**
+ * The CAIRO_SURFACE format creates a SourceSurfaceImage. All backends should
+ * support this format, because video rendering sometimes requires it.
+ *
+ * This format is useful even though a PaintedLayer could be used.
+ * It makes it easy to render a cairo surface when another Image format
+ * could be used. It can also avoid copying the surface data in some
+ * cases.
+ *
+ * Images in CAIRO_SURFACE format should only be created and
+ * manipulated on the main thread, since the underlying cairo surface
+ * is main-thread-only.
+ */
+ CAIRO_SURFACE,
+
+ /**
+ * A MacIOSurface object.
+ */
+ MAC_IOSURFACE,
+
+ /**
+ * An Android SurfaceTexture ID that can be shared across threads and
+ * processes.
+ */
+ SURFACE_TEXTURE,
+
+ /**
+ * An EGL Image that can be shared across threads.
+ */
+ EGLIMAGE,
+
+ /**
+ * The D3D9_RGB32_TEXTURE format creates a D3D9SurfaceImage, and wraps a
+ * IDirect3DTexture9 in RGB32 layout.
+ */
+ D3D9_RGB32_TEXTURE,
+
+ /**
+ * An Image type carries an opaque handle once for each stream.
+ * The opaque handle would be a platform specific identifier.
+ */
+ OVERLAY_IMAGE,
+
+ /**
+ * A share handle to a ID3D11Texture2D.
+ */
+ D3D11_SHARE_HANDLE_TEXTURE,
+
+ /**
+ * A wrapper around a drawable TextureClient.
+ */
+ TEXTURE_WRAPPER,
+
+ /**
+ * An opaque handle that refers to an Image stored in the GPU
+ * process.
+ */
+ GPU_VIDEO
+};
+
+enum class StereoMode {
+ MONO,
+ LEFT_RIGHT,
+ RIGHT_LEFT,
+ BOTTOM_TOP,
+ TOP_BOTTOM,
+ MAX,
+};
+
+enum class YUVColorSpace {
+ BT601,
+ BT709,
+ // This represents the unknown format.
+ UNKNOWN,
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h
new file mode 100644
index 000000000..d26915633
--- /dev/null
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -0,0 +1,516 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERMETRICSWRAPPER_H
+#define GFX_LAYERMETRICSWRAPPER_H
+
+#include "Layers.h"
+#include "UnitTransforms.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A wrapper class around a target Layer with that allows user code to
+ * walk through the FrameMetrics objects on the layer the same way it
+ * would walk through a ContainerLayer hierarchy. Consider the following
+ * layer tree:
+ *
+ * +---+
+ * | A |
+ * +---+
+ * / | \
+ * / | \
+ * / | \
+ * +---+ +-----+ +---+
+ * | B | | C | | D |
+ * +---+ +-----+ +---+
+ * | FMn |
+ * | . |
+ * | . |
+ * | . |
+ * | FM1 |
+ * | FM0 |
+ * +-----+
+ * / \
+ * / \
+ * +---+ +---+
+ * | E | | F |
+ * +---+ +---+
+ *
+ * In this layer tree, there are six layers with A being the root and B,D,E,F
+ * being leaf nodes. Layer C is in the middle and has n+1 FrameMetrics, labelled
+ * FM0...FMn. FM0 is the FrameMetrics you get by calling c->GetFrameMetrics(0)
+ * and FMn is the FrameMetrics you can obtain by calling
+ * c->GetFrameMetrics(c->GetScrollMetadataCount() - 1). This layer tree is
+ * conceptually equivalent to this one below:
+ *
+ * +---+
+ * | A |
+ * +---+
+ * / | \
+ * / | \
+ * / | \
+ * +---+ +-----+ +---+
+ * | B | | Cn | | D |
+ * +---+ +-----+ +---+
+ * |
+ * .
+ * .
+ * .
+ * |
+ * +-----+
+ * | C1 |
+ * +-----+
+ * |
+ * +-----+
+ * | C0 |
+ * +-----+
+ * / \
+ * / \
+ * +---+ +---+
+ * | E | | F |
+ * +---+ +---+
+ *
+ * In this layer tree, the layer C has been expanded into a stack of container
+ * layers C1...Cn, where C1 has FrameMetrics FM1 and Cn has FrameMetrics Fn.
+ * Although in this example C (in the first layer tree) and C0 (in the second
+ * layer tree) are both ContainerLayers (because they have children), they
+ * do not have to be. They may just be PaintedLayers or ColorLayers, for example,
+ * which do not have any children. However, the type of C will always be the
+ * same as the type of C0.
+ *
+ * The LayerMetricsWrapper class allows client code to treat the first layer
+ * tree as though it were the second. That is, instead of client code having
+ * to iterate through the FrameMetrics objects directly, it can use a
+ * LayerMetricsWrapper to encapsulate that aspect of the layer tree and just
+ * walk the tree as if it were a stack of ContainerLayers.
+ *
+ * The functions on this class do different things depending on which
+ * simulated ContainerLayer is being wrapped. For example, if the
+ * LayerMetricsWrapper is pretending to be C0, the GetNextSibling() function
+ * will return null even though the underlying layer C does actually have
+ * a next sibling. The LayerMetricsWrapper pretending to be Cn will return
+ * D as the next sibling.
+ *
+ * Implementation notes:
+ *
+ * The AtTopLayer() and AtBottomLayer() functions in this class refer to
+ * Cn and C0 in the second layer tree above; that is, they are predicates
+ * to test if the LayerMetricsWrapper is simulating the topmost or bottommost
+ * layer, as those will have special behaviour.
+ *
+ * It is possible to wrap a nullptr in a LayerMetricsWrapper, in which case
+ * the IsValid() function will return false. This is required to allow
+ * LayerMetricsWrapper to be a MOZ_STACK_CLASS (desirable because it is used
+ * in loops and recursion).
+ *
+ * This class purposely does not expose the wrapped layer directly to avoid
+ * user code from accidentally calling functions directly on it. Instead
+ * any necessary functions should be wrapped in this class. It does expose
+ * the wrapped layer as a void* for printf purposes.
+ *
+ * The implementation may look like it special-cases mIndex == 0 and/or
+ * GetScrollMetadataCount() == 0. This is an artifact of the fact that both
+ * mIndex and GetScrollMetadataCount() are uint32_t and GetScrollMetadataCount()
+ * can return 0 but mIndex cannot store -1. This seems better than the
+ * alternative of making mIndex a int32_t that can store -1, but then having
+ * to cast to uint32_t all over the place.
+ */
+class MOZ_STACK_CLASS LayerMetricsWrapper {
+public:
+ enum StartAt {
+ TOP,
+ BOTTOM,
+ };
+
+ LayerMetricsWrapper()
+ : mLayer(nullptr)
+ , mIndex(0)
+ {
+ }
+
+ explicit LayerMetricsWrapper(Layer* aRoot, StartAt aStart = StartAt::TOP)
+ : mLayer(aRoot)
+ , mIndex(0)
+ {
+ if (!mLayer) {
+ return;
+ }
+
+ switch (aStart) {
+ case StartAt::TOP:
+ mIndex = mLayer->GetScrollMetadataCount();
+ if (mIndex > 0) {
+ mIndex--;
+ }
+ break;
+ case StartAt::BOTTOM:
+ mIndex = 0;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown startAt value");
+ break;
+ }
+ }
+
+ explicit LayerMetricsWrapper(Layer* aLayer, uint32_t aMetricsIndex)
+ : mLayer(aLayer)
+ , mIndex(aMetricsIndex)
+ {
+ MOZ_ASSERT(mLayer);
+ MOZ_ASSERT(mIndex == 0 || mIndex < mLayer->GetScrollMetadataCount());
+ }
+
+ bool IsValid() const
+ {
+ return mLayer != nullptr;
+ }
+
+ explicit operator bool() const
+ {
+ return IsValid();
+ }
+
+ bool IsScrollInfoLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ // If we are not at the bottommost layer then it's
+ // a stack of container layers all the way down to
+ // mLayer, which we can ignore. We only care about
+ // non-container descendants.
+ return Metrics().IsScrollable()
+ && mLayer->AsContainerLayer()
+ && !mLayer->GetFirstChild();
+ }
+
+ LayerMetricsWrapper GetParent() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex + 1);
+ }
+ if (mLayer->GetParent()) {
+ return LayerMetricsWrapper(mLayer->GetParent(), StartAt::BOTTOM);
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ LayerMetricsWrapper GetFirstChild() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtBottomLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex - 1);
+ }
+ return LayerMetricsWrapper(mLayer->GetFirstChild());
+ }
+
+ LayerMetricsWrapper GetLastChild() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (!AtBottomLayer()) {
+ return LayerMetricsWrapper(mLayer, mIndex - 1);
+ }
+ return LayerMetricsWrapper(mLayer->GetLastChild());
+ }
+
+ LayerMetricsWrapper GetPrevSibling() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer->GetPrevSibling());
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ LayerMetricsWrapper GetNextSibling() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer()) {
+ return LayerMetricsWrapper(mLayer->GetNextSibling());
+ }
+ return LayerMetricsWrapper(nullptr);
+ }
+
+ const ScrollMetadata& Metadata() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mIndex >= mLayer->GetScrollMetadataCount()) {
+ return *ScrollMetadata::sNullMetadata;
+ }
+ return mLayer->GetScrollMetadata(mIndex);
+ }
+
+ const FrameMetrics& Metrics() const
+ {
+ return Metadata().GetMetrics();
+ }
+
+ AsyncPanZoomController* GetApzc() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mIndex >= mLayer->GetScrollMetadataCount()) {
+ return nullptr;
+ }
+ return mLayer->GetAsyncPanZoomController(mIndex);
+ }
+
+ void SetApzc(AsyncPanZoomController* aApzc) const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mLayer->GetScrollMetadataCount() == 0) {
+ MOZ_ASSERT(mIndex == 0);
+ MOZ_ASSERT(aApzc == nullptr);
+ return;
+ }
+ MOZ_ASSERT(mIndex < mLayer->GetScrollMetadataCount());
+ mLayer->SetAsyncPanZoomController(mIndex, aApzc);
+ }
+
+ const char* Name() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->Name();
+ }
+ return "DummyContainerLayer";
+ }
+
+ LayerManager* Manager() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->Manager();
+ }
+
+ gfx::Matrix4x4 GetTransform() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetTransform();
+ }
+ return gfx::Matrix4x4();
+ }
+
+ CSSTransformMatrix GetTransformTyped() const
+ {
+ return ViewAs<CSSTransformMatrix>(GetTransform());
+ }
+
+ bool TransformIsPerspective() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ // mLayer->GetTransformIsPerspective() tells us whether
+ // mLayer->GetTransform() is a perspective transform. Since
+ // mLayer->GetTransform() is only used at the bottom layer, we only
+ // need to check GetTransformIsPerspective() at the bottom layer too.
+ if (AtBottomLayer()) {
+ return mLayer->GetTransformIsPerspective();
+ }
+ return false;
+ }
+
+ EventRegions GetEventRegions() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetEventRegions();
+ }
+ return EventRegions();
+ }
+
+ bool HasTransformAnimation() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->HasTransformAnimation();
+ }
+ return false;
+ }
+
+ RefLayer* AsRefLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->AsRefLayer();
+ }
+ return nullptr;
+ }
+
+ LayerIntRegion GetVisibleRegion() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtBottomLayer()) {
+ return mLayer->GetVisibleRegion();
+ }
+ LayerIntRegion region = mLayer->GetVisibleRegion();
+ region.Transform(mLayer->GetTransform());
+ return region;
+ }
+
+ Maybe<ParentLayerIntRect> GetClipRect() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ Maybe<ParentLayerIntRect> result;
+
+ // The layer can have a clip rect and a scrolled clip, which are considered
+ // to apply only to the bottommost LayerMetricsWrapper.
+ // TODO: These actually apply in a different coordinate space than the
+ // scroll clip of the bottommost metrics, so we shouldn't be intersecting
+ // them with the scroll clip; bug 1269537 tracks fixing this.
+ if (AtBottomLayer()) {
+ result = mLayer->GetClipRect();
+ result = IntersectMaybeRects(result, mLayer->GetScrolledClipRect());
+ }
+
+ // The scroll metadata can have a clip rect as well.
+ result = IntersectMaybeRects(result, Metadata().GetClipRect());
+
+ return result;
+ }
+
+ float GetPresShellResolution() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (AtTopLayer() && mLayer->AsContainerLayer()) {
+ return mLayer->AsContainerLayer()->GetPresShellResolution();
+ }
+
+ return 1.0f;
+ }
+
+ EventRegionsOverride GetEventRegionsOverride() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ if (mLayer->AsContainerLayer()) {
+ return mLayer->AsContainerLayer()->GetEventRegionsOverride();
+ }
+ return EventRegionsOverride::NoOverride;
+ }
+
+ Layer::ScrollDirection GetScrollbarDirection() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetScrollbarDirection();
+ }
+
+ FrameMetrics::ViewID GetScrollbarTargetContainerId() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetScrollbarTargetContainerId();
+ }
+
+ int32_t GetScrollbarSize() const
+ {
+ if (GetScrollbarDirection() == Layer::VERTICAL) {
+ return mLayer->GetVisibleRegion().GetBounds().height;
+ } else {
+ return mLayer->GetVisibleRegion().GetBounds().width;
+ }
+ }
+
+ bool IsScrollbarContainer() const
+ {
+ MOZ_ASSERT(IsValid());
+ return mLayer->IsScrollbarContainer();
+ }
+
+ FrameMetrics::ViewID GetFixedPositionScrollContainerId() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return mLayer->GetFixedPositionScrollContainerId();
+ }
+
+ // Expose an opaque pointer to the layer. Mostly used for printf
+ // purposes. This is not intended to be a general-purpose accessor
+ // for the underlying layer.
+ const void* GetLayer() const
+ {
+ MOZ_ASSERT(IsValid());
+
+ return (void*)mLayer;
+ }
+
+ bool operator==(const LayerMetricsWrapper& aOther) const
+ {
+ return mLayer == aOther.mLayer
+ && mIndex == aOther.mIndex;
+ }
+
+ bool operator!=(const LayerMetricsWrapper& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ static const FrameMetrics& TopmostScrollableMetrics(Layer* aLayer)
+ {
+ for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) {
+ if (aLayer->GetFrameMetrics(i - 1).IsScrollable()) {
+ return aLayer->GetFrameMetrics(i - 1);
+ }
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+ static const FrameMetrics& BottommostScrollableMetrics(Layer* aLayer)
+ {
+ for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
+ if (aLayer->GetFrameMetrics(i).IsScrollable()) {
+ return aLayer->GetFrameMetrics(i);
+ }
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+ static const FrameMetrics& BottommostMetrics(Layer* aLayer)
+ {
+ if (aLayer->GetScrollMetadataCount() > 0) {
+ return aLayer->GetFrameMetrics(0);
+ }
+ return ScrollMetadata::sNullMetadata->GetMetrics();
+ }
+
+private:
+ bool AtBottomLayer() const
+ {
+ return mIndex == 0;
+ }
+
+ bool AtTopLayer() const
+ {
+ return mLayer->GetScrollMetadataCount() == 0 || mIndex == mLayer->GetScrollMetadataCount() - 1;
+ }
+
+private:
+ Layer* mLayer;
+ uint32_t mIndex;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERMETRICSWRAPPER_H */
diff --git a/gfx/layers/LayerScope.cpp b/gfx/layers/LayerScope.cpp
new file mode 100644
index 000000000..a3b7778ef
--- /dev/null
+++ b/gfx/layers/LayerScope.cpp
@@ -0,0 +1,1833 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
+#include "LayerScope.h"
+
+#include "nsAppRunner.h"
+#include "Composer2D.h"
+#include "Effects.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+
+#include "TexturePoolOGL.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+#include "gfxContext.h"
+#include "gfxUtils.h"
+#include "gfxPrefs.h"
+#include "nsIWidget.h"
+
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "GLReadTexImageHelper.h"
+
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+
+#include <memory>
+#include "mozilla/LinkedList.h"
+#include "mozilla/Base64.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/StaticPtr.h"
+#include "nsThreadUtils.h"
+#include "nsISocketTransport.h"
+#include "nsIServerSocket.h"
+#include "nsReadLine.h"
+#include "nsNetCID.h"
+#include "nsIOutputStream.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIEventTarget.h"
+#include "nsProxyRelease.h"
+#include <list>
+
+// Undo the damage done by mozzconf.h
+#undef compress
+#include "mozilla/Compression.h"
+
+// Protocol buffer (generated automatically)
+#include "protobuf/LayerScopePacket.pb.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::Compression;
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+using namespace mozilla;
+using namespace layerscope;
+
+class DebugDataSender;
+class DebugGLData;
+
+/*
+ * Manage Websocket connections
+ */
+class LayerScopeWebSocketManager {
+public:
+ LayerScopeWebSocketManager();
+ ~LayerScopeWebSocketManager();
+
+ void RemoveAllConnections()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MutexAutoLock lock(mHandlerMutex);
+ mHandlers.Clear();
+ }
+
+ bool WriteAll(void *ptr, uint32_t size)
+ {
+ for (int32_t i = mHandlers.Length() - 1; i >= 0; --i) {
+ if (!mHandlers[i]->WriteToStream(ptr, size)) {
+ // Send failed, remove this handler
+ RemoveConnection(i);
+ }
+ }
+
+ return true;
+ }
+
+ bool IsConnected()
+ {
+ // This funtion can be called in both main thread and compositor thread.
+ MutexAutoLock lock(mHandlerMutex);
+ return (mHandlers.Length() != 0) ? true : false;
+ }
+
+ void AppendDebugData(DebugGLData *aDebugData);
+ void CleanDebugData();
+ void DispatchDebugData();
+
+private:
+ void AddConnection(nsISocketTransport *aTransport)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aTransport);
+
+ MutexAutoLock lock(mHandlerMutex);
+
+ RefPtr<SocketHandler> temp = new SocketHandler();
+ temp->OpenStream(aTransport);
+ mHandlers.AppendElement(temp.get());
+ }
+
+ void RemoveConnection(uint32_t aIndex)
+ {
+ // TBD: RemoveConnection is executed on the compositor thread and
+ // AddConntection is executed on the main thread, which might be
+ // a problem if a user disconnect and connect readlly quickly at
+ // viewer side.
+
+ // We should dispatch RemoveConnection onto main thead.
+ MOZ_ASSERT(aIndex < mHandlers.Length());
+
+ MutexAutoLock lock(mHandlerMutex);
+ mHandlers.RemoveElementAt(aIndex);
+ }
+
+ friend class SocketListener;
+ class SocketListener : public nsIServerSocketListener
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ SocketListener() { }
+
+ /* nsIServerSocketListener */
+ NS_IMETHOD OnSocketAccepted(nsIServerSocket *aServ,
+ nsISocketTransport *aTransport) override;
+ NS_IMETHOD OnStopListening(nsIServerSocket *aServ,
+ nsresult aStatus) override
+ {
+ return NS_OK;
+ }
+ private:
+ virtual ~SocketListener() { }
+ };
+
+ /*
+ * This class handle websocket protocol which included
+ * handshake and data frame's header
+ */
+ class SocketHandler : public nsIInputStreamCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ SocketHandler()
+ : mState(NoHandshake)
+ , mConnected(false)
+ { }
+
+ void OpenStream(nsISocketTransport* aTransport);
+ bool WriteToStream(void *aPtr, uint32_t aSize);
+
+ // nsIInputStreamCallback
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *aStream) override;
+
+ private:
+ virtual ~SocketHandler() { CloseConnection(); }
+
+ void ReadInputStreamData(nsTArray<nsCString>& aProtocolString);
+ bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString);
+ void ApplyMask(uint32_t aMask, uint8_t *aData, uint64_t aLen);
+ bool HandleDataFrame(uint8_t *aData, uint32_t aSize);
+ void CloseConnection();
+
+ nsresult HandleSocketMessage(nsIAsyncInputStream *aStream);
+ nsresult ProcessInput(uint8_t *aBuffer, uint32_t aCount);
+
+ private:
+ enum SocketStateType {
+ NoHandshake,
+ HandshakeSuccess,
+ HandshakeFailed
+ };
+ SocketStateType mState;
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIAsyncInputStream> mInputStream;
+ nsCOMPtr<nsISocketTransport> mTransport;
+ bool mConnected;
+ };
+
+ nsTArray<RefPtr<SocketHandler> > mHandlers;
+ nsCOMPtr<nsIThread> mDebugSenderThread;
+ RefPtr<DebugDataSender> mCurrentSender;
+ nsCOMPtr<nsIServerSocket> mServerSocket;
+
+ // Keep mHandlers accessing thread safe.
+ Mutex mHandlerMutex;
+};
+
+NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketListener,
+ nsIServerSocketListener);
+NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketHandler,
+ nsIInputStreamCallback);
+
+class DrawSession {
+public:
+ DrawSession()
+ : mOffsetX(0.0)
+ , mOffsetY(0.0)
+ , mRects(0)
+ { }
+
+ float mOffsetX;
+ float mOffsetY;
+ gfx::Matrix4x4 mMVMatrix;
+ size_t mRects;
+ gfx::Rect mLayerRects[4];
+ gfx::Rect mTextureRects[4];
+ std::list<GLuint> mTexIDs;
+};
+
+class ContentMonitor {
+public:
+ using THArray = nsTArray<const TextureHost *>;
+
+ // Notify the content of a TextureHost was changed.
+ void SetChangedHost(const TextureHost* host) {
+ if (THArray::NoIndex == mChangedHosts.IndexOf(host)) {
+ mChangedHosts.AppendElement(host);
+ }
+ }
+
+ // Clear changed flag of a host.
+ void ClearChangedHost(const TextureHost* host) {
+ if (THArray::NoIndex != mChangedHosts.IndexOf(host)) {
+ mChangedHosts.RemoveElement(host);
+ }
+ }
+
+ // Return true iff host is a new one or the content of it had been changed.
+ bool IsChangedOrNew(const TextureHost* host) {
+ if (THArray::NoIndex == mSeenHosts.IndexOf(host)) {
+ mSeenHosts.AppendElement(host);
+ return true;
+ }
+
+ if (decltype(mChangedHosts)::NoIndex != mChangedHosts.IndexOf(host)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void Empty() {
+ mSeenHosts.SetLength(0);
+ mChangedHosts.SetLength(0);
+ }
+private:
+ THArray mSeenHosts;
+ THArray mChangedHosts;
+};
+
+/*
+ * Hold all singleton objects used by LayerScope.
+ */
+class LayerScopeManager
+{
+public:
+ void CreateServerSocket()
+ {
+ // WebSocketManager must be created on the main thread.
+ if (NS_IsMainThread()) {
+ mWebSocketManager = mozilla::MakeUnique<LayerScopeWebSocketManager>();
+ } else {
+ // Dispatch creation to main thread, and make sure we
+ // dispatch this only once after booting
+ static bool dispatched = false;
+ if (dispatched) {
+ return;
+ }
+
+ DebugOnly<nsresult> rv =
+ NS_DispatchToMainThread(new CreateServerSocketRunnable(this));
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to dispatch WebSocket Creation to main thread");
+ dispatched = true;
+ }
+ }
+
+ void DestroyServerSocket()
+ {
+ // Destroy Web Server Socket
+ if (mWebSocketManager) {
+ mWebSocketManager->RemoveAllConnections();
+ }
+ }
+
+ LayerScopeWebSocketManager* GetSocketManager()
+ {
+ return mWebSocketManager.get();
+ }
+
+ ContentMonitor* GetContentMonitor()
+ {
+ if (!mContentMonitor.get()) {
+ mContentMonitor = mozilla::MakeUnique<ContentMonitor>();
+ }
+
+ return mContentMonitor.get();
+ }
+
+ void NewDrawSession() {
+ mSession = mozilla::MakeUnique<DrawSession>();
+ }
+
+ DrawSession& CurrentSession() {
+ return *mSession;
+ }
+
+ void SetPixelScale(double scale) {
+ mScale = scale;
+ }
+
+ double GetPixelScale() const {
+ return mScale;
+ }
+
+ LayerScopeManager()
+ : mScale(1.0)
+ {
+ }
+private:
+ friend class CreateServerSocketRunnable;
+ class CreateServerSocketRunnable : public Runnable
+ {
+ public:
+ explicit CreateServerSocketRunnable(LayerScopeManager *aLayerScopeManager)
+ : mLayerScopeManager(aLayerScopeManager)
+ {
+ }
+ NS_IMETHOD Run() override {
+ mLayerScopeManager->mWebSocketManager =
+ mozilla::MakeUnique<LayerScopeWebSocketManager>();
+ return NS_OK;
+ }
+ private:
+ LayerScopeManager* mLayerScopeManager;
+ };
+
+ mozilla::UniquePtr<LayerScopeWebSocketManager> mWebSocketManager;
+ mozilla::UniquePtr<DrawSession> mSession;
+ mozilla::UniquePtr<ContentMonitor> mContentMonitor;
+ double mScale;
+};
+
+LayerScopeManager gLayerScopeManager;
+
+/*
+ * The static helper functions that set data into the packet
+ * 1. DumpRect
+ * 2. DumpFilter
+ */
+template<typename T>
+static void DumpRect(T* aPacketRect, const Rect& aRect)
+{
+ aPacketRect->set_x(aRect.x);
+ aPacketRect->set_y(aRect.y);
+ aPacketRect->set_w(aRect.width);
+ aPacketRect->set_h(aRect.height);
+}
+
+static void DumpFilter(TexturePacket* aTexturePacket,
+ const SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ aTexturePacket->set_mfilter(TexturePacket::GOOD);
+ break;
+ case SamplingFilter::LINEAR:
+ aTexturePacket->set_mfilter(TexturePacket::LINEAR);
+ break;
+ case SamplingFilter::POINT:
+ aTexturePacket->set_mfilter(TexturePacket::POINT);
+ break;
+ default:
+ MOZ_ASSERT(false, "Can't dump unexpected mSamplingFilter to texture packet!");
+ break;
+ }
+}
+
+/*
+ * DebugGLData is the base class of
+ * 1. DebugGLFrameStatusData (Frame start/end packet)
+ * 2. DebugGLColorData (Color data packet)
+ * 3. DebugGLTextureData (Texture data packet)
+ * 4. DebugGLLayersData (Layers Tree data packet)
+ * 5. DebugGLMetaData (Meta data packet)
+ */
+class DebugGLData: public LinkedListElement<DebugGLData> {
+public:
+ explicit DebugGLData(Packet::DataType aDataType)
+ : mDataType(aDataType)
+ { }
+
+ virtual ~DebugGLData() { }
+
+ virtual bool Write() = 0;
+
+protected:
+ static bool WriteToStream(Packet& aPacket) {
+ if (!gLayerScopeManager.GetSocketManager())
+ return true;
+
+ uint32_t size = aPacket.ByteSize();
+ auto data = MakeUnique<uint8_t[]>(size);
+ aPacket.SerializeToArray(data.get(), size);
+ return gLayerScopeManager.GetSocketManager()->WriteAll(data.get(), size);
+ }
+
+ Packet::DataType mDataType;
+};
+
+class DebugGLFrameStatusData final: public DebugGLData
+{
+public:
+ DebugGLFrameStatusData(Packet::DataType aDataType,
+ int64_t aValue)
+ : DebugGLData(aDataType),
+ mFrameStamp(aValue)
+ { }
+
+ explicit DebugGLFrameStatusData(Packet::DataType aDataType)
+ : DebugGLData(aDataType),
+ mFrameStamp(0)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ FramePacket* fp = packet.mutable_frame();
+ fp->set_value(static_cast<uint64_t>(mFrameStamp));
+
+ fp->set_scale(gLayerScopeManager.GetPixelScale());
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ int64_t mFrameStamp;
+};
+
+class DebugGLTextureData final: public DebugGLData {
+public:
+ DebugGLTextureData(GLContext* cx,
+ void* layerRef,
+ GLenum target,
+ GLuint name,
+ DataSourceSurface* img,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket)
+ : DebugGLData(Packet::TEXTURE),
+ mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
+ mTarget(target),
+ mName(name),
+ mContextAddress(reinterpret_cast<intptr_t>(cx)),
+ mDatasize(0),
+ mIsMask(aIsMask),
+ mPacket(Move(aPacket))
+ {
+ // pre-packing
+ // DataSourceSurface may have locked buffer,
+ // so we should compress now, and then it could
+ // be unlocked outside.
+ pack(img);
+ }
+
+ virtual bool Write() override {
+ return WriteToStream(*mPacket);
+ }
+
+private:
+ void pack(DataSourceSurface* aImage) {
+ mPacket->set_type(mDataType);
+
+ TexturePacket* tp = mPacket->mutable_texture();
+ tp->set_layerref(mLayerRef);
+ tp->set_name(mName);
+ tp->set_target(mTarget);
+ tp->set_dataformat(LOCAL_GL_RGBA);
+ tp->set_glcontext(static_cast<uint64_t>(mContextAddress));
+ tp->set_ismask(mIsMask);
+
+ if (aImage) {
+ tp->set_width(aImage->GetSize().width);
+ tp->set_height(aImage->GetSize().height);
+ tp->set_stride(aImage->Stride());
+
+ mDatasize = aImage->GetSize().height * aImage->Stride();
+
+ auto compresseddata = MakeUnique<char[]>(LZ4::maxCompressedSize(mDatasize));
+ if (compresseddata) {
+ int ndatasize = LZ4::compress((char*)aImage->GetData(),
+ mDatasize,
+ compresseddata.get());
+ if (ndatasize > 0) {
+ mDatasize = ndatasize;
+ tp->set_dataformat((1 << 16 | tp->dataformat()));
+ tp->set_data(compresseddata.get(), mDatasize);
+ } else {
+ NS_WARNING("Compress data failed");
+ tp->set_data(aImage->GetData(), mDatasize);
+ }
+ } else {
+ NS_WARNING("Couldn't new compressed data.");
+ tp->set_data(aImage->GetData(), mDatasize);
+ }
+ } else {
+ tp->set_width(0);
+ tp->set_height(0);
+ tp->set_stride(0);
+ }
+ }
+
+protected:
+ uint64_t mLayerRef;
+ GLenum mTarget;
+ GLuint mName;
+ intptr_t mContextAddress;
+ uint32_t mDatasize;
+ bool mIsMask;
+
+ // Packet data
+ UniquePtr<Packet> mPacket;
+};
+
+class DebugGLColorData final: public DebugGLData {
+public:
+ DebugGLColorData(void* layerRef,
+ const Color& color,
+ int width,
+ int height)
+ : DebugGLData(Packet::COLOR),
+ mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
+ mColor(color.ToABGR()),
+ mSize(width, height)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ ColorPacket* cp = packet.mutable_color();
+ cp->set_layerref(mLayerRef);
+ cp->set_color(mColor);
+ cp->set_width(mSize.width);
+ cp->set_height(mSize.height);
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ uint64_t mLayerRef;
+ uint32_t mColor;
+ IntSize mSize;
+};
+
+class DebugGLLayersData final: public DebugGLData {
+public:
+ explicit DebugGLLayersData(UniquePtr<Packet> aPacket)
+ : DebugGLData(Packet::LAYERS),
+ mPacket(Move(aPacket))
+ { }
+
+ virtual bool Write() override {
+ mPacket->set_type(mDataType);
+ return WriteToStream(*mPacket);
+ }
+
+protected:
+ UniquePtr<Packet> mPacket;
+};
+
+class DebugGLMetaData final: public DebugGLData
+{
+public:
+ DebugGLMetaData(Packet::DataType aDataType,
+ bool aValue)
+ : DebugGLData(aDataType),
+ mComposedByHwc(aValue)
+ { }
+
+ explicit DebugGLMetaData(Packet::DataType aDataType)
+ : DebugGLData(aDataType),
+ mComposedByHwc(false)
+ { }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ MetaPacket* mp = packet.mutable_meta();
+ mp->set_composedbyhwc(mComposedByHwc);
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ bool mComposedByHwc;
+};
+
+class DebugGLDrawData final: public DebugGLData {
+public:
+ DebugGLDrawData(float aOffsetX,
+ float aOffsetY,
+ const gfx::Matrix4x4& aMVMatrix,
+ size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects,
+ const std::list<GLuint> aTexIDs,
+ void* aLayerRef)
+ : DebugGLData(Packet::DRAW),
+ mOffsetX(aOffsetX),
+ mOffsetY(aOffsetY),
+ mMVMatrix(aMVMatrix),
+ mRects(aRects),
+ mTexIDs(aTexIDs),
+ mLayerRef(reinterpret_cast<uint64_t>(aLayerRef))
+ {
+ for (size_t i = 0; i < mRects; i++){
+ mLayerRects[i] = aLayerRects[i];
+ mTextureRects[i] = aTextureRects[i];
+ }
+ }
+
+ virtual bool Write() override {
+ Packet packet;
+ packet.set_type(mDataType);
+
+ DrawPacket* dp = packet.mutable_draw();
+ dp->set_layerref(mLayerRef);
+
+ dp->set_offsetx(mOffsetX);
+ dp->set_offsety(mOffsetY);
+
+ auto element = reinterpret_cast<Float *>(&mMVMatrix);
+ for (int i = 0; i < 16; i++) {
+ dp->add_mvmatrix(*element++);
+ }
+ dp->set_totalrects(mRects);
+
+ MOZ_ASSERT(mRects > 0 && mRects < 4);
+ for (size_t i = 0; i < mRects; i++) {
+ // Vertex
+ DumpRect(dp->add_layerrect(), mLayerRects[i]);
+ // UV
+ DumpRect(dp->add_texturerect(), mTextureRects[i]);
+ }
+
+ for (GLuint texId: mTexIDs) {
+ dp->add_texids(texId);
+ }
+
+ return WriteToStream(packet);
+ }
+
+protected:
+ float mOffsetX;
+ float mOffsetY;
+ gfx::Matrix4x4 mMVMatrix;
+ size_t mRects;
+ gfx::Rect mLayerRects[4];
+ gfx::Rect mTextureRects[4];
+ std::list<GLuint> mTexIDs;
+ uint64_t mLayerRef;
+};
+
+class DebugDataSender
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DebugDataSender)
+
+ // Append a DebugData into mList on mThread
+ class AppendTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ AppendTask(DebugDataSender *host, DebugGLData *d)
+ : mData(d),
+ mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ mHost->mList.insertBack(mData);
+ return NS_OK;
+ }
+
+ private:
+ virtual ~AppendTask() { }
+
+ DebugGLData *mData;
+ // Keep a strong reference to DebugDataSender to prevent this object
+ // accessing mHost on mThread, when it's been destroyed on the main
+ // thread.
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ // Clear all DebugData in mList on mThead.
+ class ClearTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ explicit ClearTask(DebugDataSender *host)
+ : mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ mHost->RemoveData();
+ return NS_OK;
+ }
+
+ private:
+ virtual ~ClearTask() { }
+
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ // Send all DebugData in mList via websocket, and then, clean up
+ // mList on mThread.
+ class SendTask: public nsIRunnable
+ {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit SendTask(DebugDataSender *host)
+ : mHost(host)
+ { }
+
+ NS_IMETHOD Run() override {
+ // Sendout all appended debug data.
+ DebugGLData *d = nullptr;
+ while ((d = mHost->mList.popFirst()) != nullptr) {
+ UniquePtr<DebugGLData> cleaner(d);
+ if (!d->Write()) {
+ gLayerScopeManager.DestroyServerSocket();
+ break;
+ }
+ }
+
+ // Cleanup.
+ mHost->RemoveData();
+ return NS_OK;
+ }
+ private:
+ virtual ~SendTask() { }
+
+ RefPtr<DebugDataSender> mHost;
+ };
+
+ explicit DebugDataSender(nsIThread *thread)
+ : mThread(thread)
+ { }
+
+ void Append(DebugGLData *d) {
+ mThread->Dispatch(new AppendTask(this, d), NS_DISPATCH_NORMAL);
+ }
+
+ void Cleanup() {
+ mThread->Dispatch(new ClearTask(this), NS_DISPATCH_NORMAL);
+ }
+
+ void Send() {
+ mThread->Dispatch(new SendTask(this), NS_DISPATCH_NORMAL);
+ }
+
+protected:
+ virtual ~DebugDataSender() {}
+ void RemoveData() {
+ MOZ_ASSERT(NS_GetCurrentThread() == mThread);
+ if (mList.isEmpty())
+ return;
+
+ DebugGLData *d;
+ while ((d = mList.popFirst()) != nullptr)
+ delete d;
+ }
+
+ // We can only modify or aceess mList on mThread.
+ LinkedList<DebugGLData> mList;
+ nsCOMPtr<nsIThread> mThread;
+};
+
+NS_IMPL_ISUPPORTS(DebugDataSender::AppendTask, nsIRunnable);
+NS_IMPL_ISUPPORTS(DebugDataSender::ClearTask, nsIRunnable);
+NS_IMPL_ISUPPORTS(DebugDataSender::SendTask, nsIRunnable);
+
+
+/*
+ * LayerScope SendXXX Structure
+ * 1. SendLayer
+ * 2. SendEffectChain
+ * 1. SendTexturedEffect
+ * -> SendTextureSource
+ * 2. SendMaskEffect
+ * -> SendTextureSource
+ * 3. SendYCbCrEffect
+ * -> SendTextureSource
+ * 4. SendColor
+ */
+class SenderHelper
+{
+// Sender public APIs
+public:
+ static void SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight);
+
+ static void SendEffectChain(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth = 0,
+ int aHeight = 0);
+
+ static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
+
+ static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
+
+ static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
+
+ static void ClearSentTextureIds();
+
+// Sender private functions
+private:
+ static void SendColor(void* aLayerRef,
+ const Color& aColor,
+ int aWidth,
+ int aHeight);
+ static void SendTextureSource(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ bool aFlipY,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket);
+ static void SetAndSendTexture(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ const TexturedEffect* aEffect);
+ static void SendTexturedEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const TexturedEffect* aEffect);
+ static void SendMaskEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectMask* aEffect);
+ static void SendYCbCrEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectYCbCr* aEffect);
+ static GLuint GetTextureID(GLContext* aGLContext,
+ TextureSourceOGL* aSource);
+ static bool HasTextureIdBeenSent(GLuint aTextureId);
+// Data fields
+private:
+ static bool sLayersTreeSendable;
+ static bool sLayersBufferSendable;
+ static std::vector<GLuint> sSentTextureIds;
+};
+
+bool SenderHelper::sLayersTreeSendable = true;
+bool SenderHelper::sLayersBufferSendable = true;
+std::vector<GLuint> SenderHelper::sSentTextureIds;
+
+
+// ----------------------------------------------
+// SenderHelper implementation
+// ----------------------------------------------
+void
+SenderHelper::ClearSentTextureIds()
+{
+ sSentTextureIds.clear();
+}
+
+bool
+SenderHelper::HasTextureIdBeenSent(GLuint aTextureId)
+{
+ return std::find(sSentTextureIds.begin(), sSentTextureIds.end(), aTextureId) != sSentTextureIds.end();
+}
+
+void
+SenderHelper::SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight)
+{
+ MOZ_ASSERT(aLayer && aLayer->GetLayer());
+ if (!aLayer || !aLayer->GetLayer()) {
+ return;
+ }
+
+ switch (aLayer->GetLayer()->GetType()) {
+ case Layer::TYPE_COLOR: {
+ EffectChain effect;
+ aLayer->GenEffectChain(effect);
+
+ LayerScope::DrawBegin();
+ LayerScope::DrawEnd(nullptr, effect, aWidth, aHeight);
+ break;
+ }
+ case Layer::TYPE_IMAGE:
+ case Layer::TYPE_CANVAS:
+ case Layer::TYPE_PAINTED: {
+ // Get CompositableHost and Compositor
+ CompositableHost* compHost = aLayer->GetCompositableHost();
+ Compositor* comp = compHost->GetCompositor();
+ // Send EffectChain only for CompositorOGL
+ if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) {
+ CompositorOGL* compOGL = comp->AsCompositorOGL();
+ EffectChain effect;
+ // Generate primary effect (lock and gen)
+ AutoLockCompositableHost lock(compHost);
+ aLayer->GenEffectChain(effect);
+
+ LayerScope::DrawBegin();
+ LayerScope::DrawEnd(compOGL->gl(), effect, aWidth, aHeight);
+ }
+ break;
+ }
+ case Layer::TYPE_CONTAINER:
+ default:
+ break;
+ }
+}
+
+void
+SenderHelper::SendColor(void* aLayerRef,
+ const Color& aColor,
+ int aWidth,
+ int aHeight)
+{
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
+}
+
+GLuint
+SenderHelper::GetTextureID(GLContext* aGLContext,
+ TextureSourceOGL* aSource) {
+ GLenum textureTarget = aSource->GetTextureTarget();
+ aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::SamplingFilter::LINEAR);
+
+ GLuint texID = 0;
+ // This is horrid hack. It assumes that aGLContext matches the context
+ // aSource has bound to.
+ if (textureTarget == LOCAL_GL_TEXTURE_2D) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &texID);
+ } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &texID);
+ } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
+ aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &texID);
+ }
+
+ return texID;
+}
+
+void
+SenderHelper::SendTextureSource(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ bool aFlipY,
+ bool aIsMask,
+ UniquePtr<Packet> aPacket)
+{
+ MOZ_ASSERT(aGLContext);
+ if (!aGLContext) {
+ return;
+ }
+ GLuint texID = GetTextureID(aGLContext, aSource);
+ if (HasTextureIdBeenSent(texID)) {
+ return;
+ }
+
+ GLenum textureTarget = aSource->GetTextureTarget();
+ ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
+ aSource->GetFormat());
+ int shaderConfig = config.mFeatures;
+
+ gfx::IntSize size = aSource->GetSize();
+
+ // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
+ // texture correctly. texID is used for tracking in DebugGLTextureData.
+ RefPtr<DataSourceSurface> img =
+ aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
+ size,
+ shaderConfig, aFlipY);
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
+ texID, img, aIsMask, Move(aPacket)));
+
+ sSentTextureIds.push_back(texID);
+ gLayerScopeManager.CurrentSession().mTexIDs.push_back(texID);
+
+}
+
+void
+SenderHelper::SetAndSendTexture(GLContext* aGLContext,
+ void* aLayerRef,
+ TextureSourceOGL* aSource,
+ const TexturedEffect* aEffect)
+{
+ // Expose packet creation here, so we could dump primary texture effect attributes.
+ auto packet = MakeUnique<layerscope::Packet>();
+ layerscope::TexturePacket* texturePacket = packet->mutable_texture();
+ texturePacket->set_mpremultiplied(aEffect->mPremultiplied);
+ DumpFilter(texturePacket, aEffect->mSamplingFilter);
+ DumpRect(texturePacket->mutable_mtexturecoords(), aEffect->mTextureCoords);
+ SendTextureSource(aGLContext, aLayerRef, aSource, false, false, Move(packet));
+}
+
+void
+SenderHelper::SendTexturedEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const TexturedEffect* aEffect)
+{
+ TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL();
+ if (!source) {
+ return;
+ }
+
+ // Fallback texture sending path.
+ SetAndSendTexture(aGLContext, aLayerRef, source, aEffect);
+}
+
+void
+SenderHelper::SendMaskEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectMask* aEffect)
+{
+ TextureSourceOGL* source = aEffect->mMaskTexture->AsSourceOGL();
+ if (!source) {
+ return;
+ }
+
+ // Expose packet creation here, so we could dump secondary mask effect attributes.
+ auto packet = MakeUnique<layerscope::Packet>();
+ TexturePacket::EffectMask* mask = packet->mutable_texture()->mutable_mask();
+ mask->mutable_msize()->set_w(aEffect->mSize.width);
+ mask->mutable_msize()->set_h(aEffect->mSize.height);
+ auto element = reinterpret_cast<const Float *>(&(aEffect->mMaskTransform));
+ for (int i = 0; i < 16; i++) {
+ mask->mutable_mmasktransform()->add_m(*element++);
+ }
+
+ SendTextureSource(aGLContext, aLayerRef, source, false, true, Move(packet));
+}
+
+void
+SenderHelper::SendYCbCrEffect(GLContext* aGLContext,
+ void* aLayerRef,
+ const EffectYCbCr* aEffect)
+{
+ TextureSource* sourceYCbCr = aEffect->mTexture;
+ if (!sourceYCbCr)
+ return;
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSourceOGL *sources[] = {
+ sourceYCbCr->GetSubSource(Y)->AsSourceOGL(),
+ sourceYCbCr->GetSubSource(Cb)->AsSourceOGL(),
+ sourceYCbCr->GetSubSource(Cr)->AsSourceOGL()
+ };
+
+ for (auto source: sources) {
+ SetAndSendTexture(aGLContext, aLayerRef, source, aEffect);
+ }
+}
+
+void
+SenderHelper::SendEffectChain(GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight)
+{
+ if (!sLayersBufferSendable) return;
+
+ const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
+ MOZ_ASSERT(primaryEffect);
+
+ if (!primaryEffect) {
+ return;
+ }
+
+ switch (primaryEffect->mType) {
+ case EffectTypes::RGB: {
+ const TexturedEffect* texturedEffect =
+ static_cast<const TexturedEffect*>(primaryEffect);
+ SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect);
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ const EffectYCbCr* yCbCrEffect =
+ static_cast<const EffectYCbCr*>(primaryEffect);
+ SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect);
+ break;
+ }
+ case EffectTypes::SOLID_COLOR: {
+ const EffectSolidColor* solidColorEffect =
+ static_cast<const EffectSolidColor*>(primaryEffect);
+ SendColor(aEffectChain.mLayerRef, solidColorEffect->mColor,
+ aWidth, aHeight);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA:
+ case EffectTypes::RENDER_TARGET:
+ default:
+ break;
+ }
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ const EffectMask* effectMask =
+ static_cast<const EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ SendMaskEffect(aGLContext, aEffectChain.mLayerRef, effectMask);
+ }
+}
+
+void
+LayerScope::ContentChanged(TextureHost *host)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.GetContentMonitor()->SetChangedHost(host);
+}
+
+// ----------------------------------------------
+// SocketHandler implementation
+// ----------------------------------------------
+void
+LayerScopeWebSocketManager::SocketHandler::OpenStream(nsISocketTransport* aTransport)
+{
+ MOZ_ASSERT(aTransport);
+
+ mTransport = aTransport;
+ mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
+ 0,
+ 0,
+ getter_AddRefs(mOutputStream));
+
+ nsCOMPtr<nsIInputStream> debugInputStream;
+ mTransport->OpenInputStream(0,
+ 0,
+ 0,
+ getter_AddRefs(debugInputStream));
+ mInputStream = do_QueryInterface(debugInputStream);
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::WriteToStream(void *aPtr,
+ uint32_t aSize)
+{
+ if (mState == NoHandshake) {
+ // Not yet handshake, just return true in case of
+ // LayerScope remove this handle
+ return true;
+ } else if (mState == HandshakeFailed) {
+ return false;
+ }
+
+ if (!mOutputStream) {
+ return false;
+ }
+
+ // Generate WebSocket header
+ uint8_t wsHeader[10];
+ int wsHeaderSize = 0;
+ const uint8_t opcode = 0x2;
+ wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
+ if (aSize <= 125) {
+ wsHeaderSize = 2;
+ wsHeader[1] = aSize;
+ } else if (aSize < 65536) {
+ wsHeaderSize = 4;
+ wsHeader[1] = 0x7E;
+ NetworkEndian::writeUint16(wsHeader + 2, aSize);
+ } else {
+ wsHeaderSize = 10;
+ wsHeader[1] = 0x7F;
+ NetworkEndian::writeUint64(wsHeader + 2, aSize);
+ }
+
+ // Send WebSocket header
+ nsresult rv;
+ uint32_t cnt;
+ rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
+ wsHeaderSize, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ uint32_t written = 0;
+ while (written < aSize) {
+ uint32_t cnt;
+ rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written,
+ aSize - written, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ written += cnt;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+LayerScopeWebSocketManager::SocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
+{
+ MOZ_ASSERT(mInputStream);
+
+ if (!mInputStream) {
+ return NS_OK;
+ }
+
+ if (!mConnected) {
+ nsTArray<nsCString> protocolString;
+ ReadInputStreamData(protocolString);
+
+ if (WebSocketHandshake(protocolString)) {
+ mState = HandshakeSuccess;
+ mConnected = true;
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+ } else {
+ mState = HandshakeFailed;
+ }
+ return NS_OK;
+ } else {
+ return HandleSocketMessage(aStream);
+ }
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
+{
+ nsLineBuffer<char> lineBuffer;
+ nsCString line;
+ bool more = true;
+ do {
+ NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
+
+ if (line.Length() > 0) {
+ aProtocolString.AppendElement(line);
+ }
+ } while (more && line.Length() > 0);
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
+{
+ nsresult rv;
+ bool isWebSocket = false;
+ nsCString version;
+ nsCString wsKey;
+ nsCString protocol;
+
+ // Validate WebSocket client request.
+ if (aProtocolString.Length() == 0)
+ return false;
+
+ // Check that the HTTP method is GET
+ const char* HTTP_METHOD = "GET ";
+ if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
+ return false;
+ }
+
+ for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
+ const char* line = aProtocolString[i].get();
+ const char* prop_pos = strchr(line, ':');
+ if (prop_pos != nullptr) {
+ nsCString key(line, prop_pos - line);
+ nsCString value(prop_pos + 2);
+ if (key.EqualsIgnoreCase("upgrade") &&
+ value.EqualsIgnoreCase("websocket")) {
+ isWebSocket = true;
+ } else if (key.EqualsIgnoreCase("sec-websocket-version")) {
+ version = value;
+ } else if (key.EqualsIgnoreCase("sec-websocket-key")) {
+ wsKey = value;
+ } else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
+ protocol = value;
+ }
+ }
+ }
+
+ if (!isWebSocket) {
+ return false;
+ }
+
+ if (!(version.EqualsLiteral("7") ||
+ version.EqualsLiteral("8") ||
+ version.EqualsLiteral("13"))) {
+ return false;
+ }
+
+ if (!(protocol.EqualsIgnoreCase("binary"))) {
+ return false;
+ }
+
+ if (!mOutputStream) {
+ return false;
+ }
+
+ // Client request is valid. Start to generate and send server response.
+ nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ nsAutoCString res;
+ SHA1Sum sha1;
+ nsCString combined(wsKey + guid);
+ sha1.update(combined.get(), combined.Length());
+ uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
+ sha1.finish(digest);
+ nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
+ rv = Base64Encode(newString, res);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
+ response.AppendLiteral("Upgrade: websocket\r\n");
+ response.AppendLiteral("Connection: Upgrade\r\n");
+ response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
+ response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
+ uint32_t written = 0;
+ uint32_t size = response.Length();
+ while (written < size) {
+ uint32_t cnt;
+ rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
+ size - written, &cnt);
+ if (NS_FAILED(rv))
+ return false;
+
+ written += cnt;
+ }
+ mOutputStream->Flush();
+
+ return true;
+}
+
+nsresult
+LayerScopeWebSocketManager::SocketHandler::HandleSocketMessage(nsIAsyncInputStream *aStream)
+{
+ // The reading and parsing of this input stream is customized for layer viewer.
+ const uint32_t cPacketSize = 1024;
+ char buffer[cPacketSize];
+ uint32_t count = 0;
+ nsresult rv = NS_OK;
+
+ do {
+ rv = mInputStream->Read((char *)buffer, cPacketSize, &count);
+
+ // TODO: combine packets if we have to read more than once
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ if (count == 0) {
+ // NS_BASE_STREAM_CLOSED
+ CloseConnection();
+ break;
+ }
+
+ rv = ProcessInput(reinterpret_cast<uint8_t *>(buffer), count);
+ } while (NS_SUCCEEDED(rv) && mInputStream);
+ return rv;
+}
+
+nsresult
+LayerScopeWebSocketManager::SocketHandler::ProcessInput(uint8_t *aBuffer,
+ uint32_t aCount)
+{
+ uint32_t avail = aCount;
+
+ // Decode Websocket data frame
+ if (avail <= 2) {
+ NS_WARNING("Packet size is less than 2 bytes");
+ return NS_OK;
+ }
+
+ // First byte, data type, only care the opcode
+ // rsvBits: aBuffer[0] & 0x70 (0111 0000)
+ uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000
+ uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111
+
+ if (!finBit) {
+ NS_WARNING("We cannot handle multi-fragments messages in Layerscope websocket parser.");
+ return NS_OK;
+ }
+
+ // Second byte, data length
+ uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000
+ int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111
+
+ if (!maskBit) {
+ NS_WARNING("Client to Server should set the mask bit");
+ return NS_OK;
+ }
+
+ uint32_t framingLength = 2 + 4; // 4 for masks
+
+ if (payloadLength64 < 126) {
+ if (avail < framingLength)
+ return NS_OK;
+ } else if (payloadLength64 == 126) {
+ // 16 bit length field
+ framingLength += 2;
+ if (avail < framingLength) {
+ return NS_OK;
+ }
+
+ payloadLength64 = aBuffer[2] << 8 | aBuffer[3];
+ } else {
+ // 64 bit length
+ framingLength += 8;
+ if (avail < framingLength) {
+ return NS_OK;
+ }
+
+ if (aBuffer[2] & 0x80) {
+ // Section 4.2 says that the most significant bit MUST be
+ // 0. (i.e. this is really a 63 bit value)
+ NS_WARNING("High bit of 64 bit length set");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // copy this in case it is unaligned
+ payloadLength64 = NetworkEndian::readInt64(aBuffer + 2);
+ }
+
+ uint8_t *payload = aBuffer + framingLength;
+ avail -= framingLength;
+
+ uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
+ if (avail < payloadLength) {
+ NS_WARNING("Packet size mismatch the payload length");
+ return NS_OK;
+ }
+
+ // Apply mask
+ uint32_t mask = NetworkEndian::readUint32(payload - 4);
+ ApplyMask(mask, payload, payloadLength);
+
+ if (opcode == 0x8) {
+ // opcode == 0x8 means connection close
+ CloseConnection();
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ if (!HandleDataFrame(payload, payloadLength)) {
+ NS_WARNING("Cannot decode payload data by the protocol buffer");
+ }
+
+ return NS_OK;
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::ApplyMask(uint32_t aMask,
+ uint8_t *aData,
+ uint64_t aLen)
+{
+ if (!aData || aLen == 0) {
+ return;
+ }
+
+ // Optimally we want to apply the mask 32 bits at a time,
+ // but the buffer might not be alligned. So we first deal with
+ // 0 to 3 bytes of preamble individually
+ while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) {
+ *aData ^= aMask >> 24;
+ aMask = RotateLeft(aMask, 8);
+ aData++;
+ aLen--;
+ }
+
+ // perform mask on full words of data
+ uint32_t *iData = reinterpret_cast<uint32_t *>(aData);
+ uint32_t *end = iData + (aLen >> 2);
+ NetworkEndian::writeUint32(&aMask, aMask);
+ for (; iData < end; iData++) {
+ *iData ^= aMask;
+ }
+ aMask = NetworkEndian::readUint32(&aMask);
+ aData = (uint8_t *)iData;
+ aLen = aLen % 4;
+
+ // There maybe up to 3 trailing bytes that need to be dealt with
+ // individually
+ while (aLen) {
+ *aData ^= aMask >> 24;
+ aMask = RotateLeft(aMask, 8);
+ aData++;
+ aLen--;
+ }
+}
+
+bool
+LayerScopeWebSocketManager::SocketHandler::HandleDataFrame(uint8_t *aData,
+ uint32_t aSize)
+{
+ // Handle payload data by protocol buffer
+ auto p = MakeUnique<CommandPacket>();
+ p->ParseFromArray(static_cast<void*>(aData), aSize);
+
+ if (!p->has_type()) {
+ MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it");
+ return false;
+ }
+
+ switch (p->type()) {
+ case CommandPacket::LAYERS_TREE:
+ if (p->has_value()) {
+ SenderHelper::SetLayersTreeSendable(p->value());
+ }
+ break;
+
+ case CommandPacket::LAYERS_BUFFER:
+ if (p->has_value()) {
+ SenderHelper::SetLayersBufferSendable(p->value());
+ }
+ break;
+
+ case CommandPacket::NO_OP:
+ default:
+ NS_WARNING("Invalid message type");
+ break;
+ }
+ return true;
+}
+
+void
+LayerScopeWebSocketManager::SocketHandler::CloseConnection()
+{
+ gLayerScopeManager.GetSocketManager()->CleanDebugData();
+ if (mInputStream) {
+ mInputStream->AsyncWait(nullptr, 0, 0, nullptr);
+ mInputStream = nullptr;
+ }
+ if (mOutputStream) {
+ mOutputStream = nullptr;
+ }
+ if (mTransport) {
+ mTransport->Close(NS_BASE_STREAM_CLOSED);
+ mTransport = nullptr;
+ }
+ mConnected = false;
+}
+
+// ----------------------------------------------
+// LayerScopeWebSocketManager implementation
+// ----------------------------------------------
+LayerScopeWebSocketManager::LayerScopeWebSocketManager()
+ : mHandlerMutex("LayerScopeWebSocketManager::mHandlerMutex")
+{
+ NS_NewThread(getter_AddRefs(mDebugSenderThread));
+
+ mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
+ int port = gfxPrefs::LayerScopePort();
+ mServerSocket->Init(port, false, -1);
+ mServerSocket->AsyncListen(new SocketListener);
+}
+
+LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
+{
+ mServerSocket->Close();
+}
+
+void
+LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
+{
+ if (!mCurrentSender) {
+ mCurrentSender = new DebugDataSender(mDebugSenderThread);
+ }
+
+ mCurrentSender->Append(aDebugData);
+}
+
+void
+LayerScopeWebSocketManager::CleanDebugData()
+{
+ if (mCurrentSender) {
+ mCurrentSender->Cleanup();
+ }
+}
+
+void
+LayerScopeWebSocketManager::DispatchDebugData()
+{
+ MOZ_ASSERT(mCurrentSender.get() != nullptr);
+
+ mCurrentSender->Send();
+ mCurrentSender = nullptr;
+}
+
+NS_IMETHODIMP LayerScopeWebSocketManager::SocketListener::OnSocketAccepted(
+ nsIServerSocket *aServ,
+ nsISocketTransport *aTransport)
+{
+ if (!gLayerScopeManager.GetSocketManager())
+ return NS_OK;
+
+ printf_stderr("*** LayerScope: Accepted connection\n");
+ gLayerScopeManager.GetSocketManager()->AddConnection(aTransport);
+ gLayerScopeManager.GetContentMonitor()->Empty();
+ return NS_OK;
+}
+
+// ----------------------------------------------
+// LayerScope implementation
+// ----------------------------------------------
+/*static*/
+void
+LayerScope::Init()
+{
+ if (!gfxPrefs::LayerScopeEnabled() || XRE_IsGPUProcess()) {
+ return;
+ }
+
+ gLayerScopeManager.CreateServerSocket();
+}
+
+/*static*/
+void
+LayerScope::DrawBegin()
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.NewDrawSession();
+}
+
+/*static*/
+void
+LayerScope::SetRenderOffset(float aX, float aY)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.CurrentSession().mOffsetX = aX;
+ gLayerScopeManager.CurrentSession().mOffsetY = aY;
+}
+
+/*static*/
+void
+LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.CurrentSession().mMVMatrix = aMatrix;
+}
+
+/*static*/
+void
+LayerScope::SetDrawRects(size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects)
+{
+ if (!CheckSendable()) {
+ return;
+ }
+
+ MOZ_ASSERT(aRects > 0 && aRects <= 4);
+ MOZ_ASSERT(aLayerRects);
+
+ gLayerScopeManager.CurrentSession().mRects = aRects;
+
+ for (size_t i = 0; i < aRects; i++){
+ gLayerScopeManager.CurrentSession().mLayerRects[i] = aLayerRects[i];
+ gLayerScopeManager.CurrentSession().mTextureRects[i] = aTextureRects[i];
+ }
+}
+
+/*static*/
+void
+LayerScope::DrawEnd(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight)
+{
+ // Protect this public function
+ if (!CheckSendable()) {
+ return;
+ }
+
+ // 1. Send textures.
+ SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
+
+ // 2. Send parameters of draw call, such as uniforms and attributes of
+ // vertex adnd fragment shader.
+ DrawSession& draws = gLayerScopeManager.CurrentSession();
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLDrawData(draws.mOffsetX, draws.mOffsetY,
+ draws.mMVMatrix, draws.mRects,
+ draws.mLayerRects,
+ draws.mTextureRects,
+ draws.mTexIDs,
+ aEffectChain.mLayerRef));
+
+}
+
+/*static*/
+void
+LayerScope::SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight)
+{
+ // Protect this public function
+ if (!CheckSendable()) {
+ return;
+ }
+ SenderHelper::SendLayer(aLayer, aWidth, aHeight);
+}
+
+/*static*/
+void
+LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
+{
+ // Protect this public function
+ if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) {
+ return;
+ }
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLLayersData(Move(aPacket)));
+}
+
+/*static*/
+bool
+LayerScope::CheckSendable()
+{
+ // Only compositor threads check LayerScope status
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || gIsGtest);
+
+ if (!gfxPrefs::LayerScopeEnabled()) {
+ return false;
+ }
+ if (!gLayerScopeManager.GetSocketManager()) {
+ Init();
+ return false;
+ }
+ if (!gLayerScopeManager.GetSocketManager()->IsConnected()) {
+ return false;
+ }
+ return true;
+}
+
+/*static*/
+void
+LayerScope::CleanLayer()
+{
+ if (CheckSendable()) {
+ gLayerScopeManager.GetSocketManager()->CleanDebugData();
+ }
+}
+
+/*static*/
+void
+LayerScope::SetHWComposed()
+{
+ if (CheckSendable()) {
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLMetaData(Packet::META, true));
+ }
+}
+
+/*static*/
+void
+LayerScope::SetPixelScale(double devPixelsPerCSSPixel)
+{
+ gLayerScopeManager.SetPixelScale(devPixelsPerCSSPixel);
+}
+
+// ----------------------------------------------
+// LayerScopeAutoFrame implementation
+// ----------------------------------------------
+LayerScopeAutoFrame::LayerScopeAutoFrame(int64_t aFrameStamp)
+{
+ // Do Begin Frame
+ BeginFrame(aFrameStamp);
+}
+
+LayerScopeAutoFrame::~LayerScopeAutoFrame()
+{
+ // Do End Frame
+ EndFrame();
+}
+
+void
+LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp)
+{
+ if (!LayerScope::CheckSendable()) {
+ return;
+ }
+ SenderHelper::ClearSentTextureIds();
+
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp));
+}
+
+void
+LayerScopeAutoFrame::EndFrame()
+{
+ if (!LayerScope::CheckSendable()) {
+ return;
+ }
+
+ gLayerScopeManager.GetSocketManager()->AppendDebugData(
+ new DebugGLFrameStatusData(Packet::FRAMEEND));
+ gLayerScopeManager.GetSocketManager()->DispatchDebugData();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/LayerScope.h b/gfx/layers/LayerScope.h
new file mode 100644
index 000000000..24fde2397
--- /dev/null
+++ b/gfx/layers/LayerScope.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSCOPE_H
+#define GFX_LAYERSCOPE_H
+
+#include <stdint.h>
+#include <mozilla/UniquePtr.h>
+#include "gfxMatrix.h"
+
+namespace mozilla {
+
+namespace gl { class GLContext; }
+
+namespace layers {
+
+
+namespace layerscope { class Packet; }
+
+struct EffectChain;
+class LayerComposite;
+class TextureHost;
+
+class LayerScope {
+public:
+ static void DrawBegin();
+ static void SetRenderOffset(float aX, float aY);
+ static void SetLayerTransform(const gfx::Matrix4x4& aMatrix);
+ static void SetDrawRects(size_t aRects,
+ const gfx::Rect* aLayerRects,
+ const gfx::Rect* aTextureRects);
+ static void DrawEnd(gl::GLContext* aGLContext,
+ const EffectChain& aEffectChain,
+ int aWidth,
+ int aHeight);
+
+ static void SendLayer(LayerComposite* aLayer,
+ int aWidth,
+ int aHeight);
+ static void SendLayerDump(UniquePtr<layerscope::Packet> aPacket);
+ static bool CheckSendable();
+ static void CleanLayer();
+ static void SetHWComposed();
+
+ static void SetPixelScale(double devPixelsPerCSSPixel);
+ static void ContentChanged(TextureHost *host);
+private:
+ static void Init();
+};
+
+// Perform BeginFrame and EndFrame automatically
+class LayerScopeAutoFrame {
+public:
+ explicit LayerScopeAutoFrame(int64_t aFrameStamp);
+ ~LayerScopeAutoFrame();
+
+private:
+ static void BeginFrame(int64_t aFrameStamp);
+ static void EndFrame();
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSCOPE_H */
diff --git a/gfx/layers/LayerSorter.cpp b/gfx/layers/LayerSorter.cpp
new file mode 100644
index 000000000..ce180c374
--- /dev/null
+++ b/gfx/layers/LayerSorter.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerSorter.h"
+#include <math.h> // for fabs
+#include <stdint.h> // for uint32_t
+#include <stdio.h> // for fprintf, stderr, FILE
+#include <stdlib.h> // for getenv
+#include "DirectedGraph.h" // for DirectedGraph
+#include "Layers.h" // for Layer
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxLineSegment.h" // for gfxLineSegment
+#include "gfxPoint.h" // for gfxPoint
+#include "gfxQuad.h" // for gfxQuad
+#include "gfxRect.h" // for gfxRect
+#include "gfxTypes.h" // for gfxFloat
+#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
+#include "mozilla/Sprintf.h" // for SprintfLiteral
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, etc
+#include "limits.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+enum LayerSortOrder {
+ Undefined,
+ ABeforeB,
+ BBeforeA,
+};
+
+/**
+ * Recover the z component from a 2d transformed point by finding the intersection
+ * of a line through the point in the z direction and the transformed plane.
+ *
+ * We want to solve:
+ *
+ * point = normal . (p0 - l0) / normal . l
+ */
+static gfxFloat RecoverZDepth(const Matrix4x4& aTransform, const gfxPoint& aPoint)
+{
+ const Point3D l(0, 0, 1);
+ Point3D l0 = Point3D(aPoint.x, aPoint.y, 0);
+ Point3D p0 = aTransform.TransformPoint(Point3D(0, 0, 0));
+ Point3D normal = aTransform.GetNormalVector();
+
+ gfxFloat n = normal.DotProduct(p0 - l0);
+ gfxFloat d = normal.DotProduct(l);
+
+ if (!d) {
+ return 0;
+ }
+
+ return n/d;
+}
+
+/**
+ * Determine if this transform layer should be drawn before another when they
+ * are both preserve-3d children.
+ *
+ * We want to find the relative z depths of the 2 layers at points where they
+ * intersect when projected onto the 2d screen plane. Intersections are defined
+ * as corners that are positioned within the other quad, as well as intersections
+ * of the lines.
+ *
+ * We then choose the intersection point with the greatest difference in Z
+ * depths and use this point to determine an ordering for the two layers.
+ * For layers that are intersecting in 3d space, this essentially guesses an
+ * order. In a lot of cases we only intersect right at the edge point (3d cubes
+ * in particular) and this generates the 'correct' looking ordering. For planes
+ * that truely intersect, then there is no correct ordering and this remains
+ * unsolved without changing our rendering code.
+ */
+static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
+ gfxRect ourRect = ThebesRect(aOne->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ gfxRect otherRect = ThebesRect(aTwo->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+
+ MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
+ aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
+ // Effective transform of leaves may had been projected to 2D.
+ Matrix4x4 ourTransform =
+ aOne->GetLocalTransform() * aOne->GetParent()->GetEffectiveTransform();
+ Matrix4x4 otherTransform =
+ aTwo->GetLocalTransform() * aTwo->GetParent()->GetEffectiveTransform();
+
+ // Transform both rectangles and project into 2d space.
+ gfxQuad ourTransformedRect = ourRect.TransformToQuad(ourTransform);
+ gfxQuad otherTransformedRect = otherRect.TransformToQuad(otherTransform);
+
+ gfxRect ourBounds = ourTransformedRect.GetBounds();
+ gfxRect otherBounds = otherTransformedRect.GetBounds();
+
+ if (!ourBounds.Intersects(otherBounds)) {
+ return Undefined;
+ }
+
+ // Make a list of all points that are within the other rect.
+ // Could we just check Contains() on the bounds rects. ie, is it possible
+ // for layers to overlap without intersections (in 2d space) and yet still
+ // have their bounds rects not completely enclose each other?
+ nsTArray<gfxPoint> points;
+ for (uint32_t i = 0; i < 4; i++) {
+ if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
+ points.AppendElement(otherTransformedRect.mPoints[i]);
+ }
+ if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
+ points.AppendElement(ourTransformedRect.mPoints[i]);
+ }
+ }
+
+ // Look for intersections between lines (in 2d space) and use these as
+ // depth testing points.
+ for (uint32_t i = 0; i < 4; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ gfxPoint intersection;
+ gfxLineSegment one(ourTransformedRect.mPoints[i],
+ ourTransformedRect.mPoints[(i + 1) % 4]);
+ gfxLineSegment two(otherTransformedRect.mPoints[j],
+ otherTransformedRect.mPoints[(j + 1) % 4]);
+ if (one.Intersects(two, intersection)) {
+ points.AppendElement(intersection);
+ }
+ }
+ }
+
+ // No intersections, no defined order between these layers.
+ if (points.IsEmpty()) {
+ return Undefined;
+ }
+
+ // Find the relative Z depths of each intersection point and check that the layers are in the same order.
+ gfxFloat highest = 0;
+ for (uint32_t i = 0; i < points.Length(); i++) {
+ gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
+ gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
+
+ gfxFloat difference = otherDepth - ourDepth;
+
+ if (fabs(difference) > fabs(highest)) {
+ highest = difference;
+ }
+ }
+ // If layers have the same depth keep the original order
+ if (fabs(highest) < 0.1 || highest >= 0) {
+ return ABeforeB;
+ } else {
+ return BBeforeA;
+ }
+}
+
+#ifdef DEBUG
+// #define USE_XTERM_COLORING
+#ifdef USE_XTERM_COLORING
+// List of color values, which can be added to the xterm foreground offset or
+// background offset to generate a xterm color code.
+// NOTE: The colors that we don't explicitly use (by name) are commented out,
+// to avoid triggering Wunused-const-variable build warnings.
+static const int XTERM_FOREGROUND_COLOR_OFFSET = 30;
+static const int XTERM_BACKGROUND_COLOR_OFFSET = 40;
+static const int BLACK = 0;
+//static const int RED = 1;
+static const int GREEN = 2;
+//static const int YELLOW = 3;
+//static const int BLUE = 4;
+//static const int MAGENTA = 5;
+//static const int CYAN = 6;
+//static const int WHITE = 7;
+
+static const int RESET = 0;
+// static const int BRIGHT = 1;
+// static const int DIM = 2;
+// static const int UNDERLINE = 3;
+// static const int BLINK = 4;
+// static const int REVERSE = 7;
+// static const int HIDDEN = 8;
+
+static void SetTextColor(uint32_t aColor)
+{
+ char command[13];
+
+ /* Command is the control command to the terminal */
+ SprintfLiteral(command, "%c[%d;%d;%dm", 0x1B, RESET,
+ aColor + XTERM_FOREGROUND_COLOR_OFFSET,
+ BLACK + XTERM_BACKGROUND_COLOR_OFFSET);
+ printf("%s", command);
+}
+
+static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
+{
+ SetTextColor(aColor);
+ fprintf(aFile, "%p", aLayer);
+ SetTextColor(GREEN);
+}
+#else
+
+const char *colors[] = { "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White" };
+
+static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
+{
+ fprintf(aFile, "%p(%s)", aLayer, colors[aColor]);
+}
+#endif
+
+static void print_layer(FILE* aFile, Layer* aLayer)
+{
+ print_layer_internal(aFile, aLayer, aLayer->GetDebugColorIndex());
+}
+
+static void DumpLayerList(nsTArray<Layer*>& aLayers)
+{
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ print_layer(stderr, aLayers.ElementAt(i));
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n");
+}
+
+static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
+{
+ const nsTArray<DirectedGraph<Layer*>::Edge>& edges = aGraph.GetEdgeList();
+
+ for (uint32_t i = 0; i < edges.Length(); i++) {
+ fprintf(stderr, "From: ");
+ print_layer(stderr, edges.ElementAt(i).mFrom);
+ fprintf(stderr, ", To: ");
+ print_layer(stderr, edges.ElementAt(i).mTo);
+ fprintf(stderr, "\n");
+ }
+}
+#endif
+
+// The maximum number of layers that we will attempt to sort. Anything
+// greater than this will be left unsorted. We should consider enabling
+// depth buffering for the scene in this case.
+#define MAX_SORTABLE_LAYERS 100
+
+
+uint32_t gColorIndex = 1;
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
+{
+ uint32_t nodeCount = aLayers.Length();
+ if (nodeCount > MAX_SORTABLE_LAYERS) {
+ return;
+ }
+ DirectedGraph<Layer*> graph;
+
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ if (aLayers.ElementAt(i)->GetDebugColorIndex() == 0) {
+ aLayers.ElementAt(i)->SetDebugColorIndex(gColorIndex++);
+ if (gColorIndex > 7) {
+ gColorIndex = 1;
+ }
+ }
+ }
+ fprintf(stderr, " --- Layers before sorting: --- \n");
+ DumpLayerList(aLayers);
+ }
+#endif
+
+ // Iterate layers and determine edges.
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ for (uint32_t j = i + 1; j < nodeCount; j++) {
+ Layer* a = aLayers.ElementAt(i);
+ Layer* b = aLayers.ElementAt(j);
+ LayerSortOrder order = CompareDepth(a, b);
+ if (order == ABeforeB) {
+ graph.AddEdge(a, b);
+ } else if (order == BBeforeA) {
+ graph.AddEdge(b, a);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ fprintf(stderr, " --- Edge List: --- \n");
+ DumpEdgeList(graph);
+ }
+#endif
+
+ // Build a new array using the graph.
+ nsTArray<Layer*> noIncoming;
+ nsTArray<Layer*> sortedList;
+
+ // Make a list of all layers with no incoming edges.
+ noIncoming.AppendElements(aLayers);
+ const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
+ for (uint32_t i = 0; i < edges.Length(); i++) {
+ noIncoming.RemoveElement(edges.ElementAt(i).mTo);
+ }
+
+ // Move each item without incoming edges into the sorted list,
+ // and remove edges from it.
+ do {
+ if (!noIncoming.IsEmpty()) {
+ uint32_t last = noIncoming.Length() - 1;
+
+ Layer* layer = noIncoming.ElementAt(last);
+ MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList
+
+ noIncoming.RemoveElementAt(last);
+ sortedList.AppendElement(layer);
+
+ nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
+ graph.GetEdgesFrom(layer, outgoing);
+ for (uint32_t i = 0; i < outgoing.Length(); i++) {
+ DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
+ graph.RemoveEdge(edge);
+ if (!graph.NumEdgesTo(edge.mTo)) {
+ // If this node also has no edges now, add it to the list
+ noIncoming.AppendElement(edge.mTo);
+ }
+ }
+ }
+
+ // If there are no nodes without incoming edges, but there
+ // are still edges, then we have a cycle.
+ if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
+ // Find the node with the least incoming edges.
+ uint32_t minEdges = UINT_MAX;
+ Layer* minNode = nullptr;
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ uint32_t edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
+ if (edgeCount && edgeCount < minEdges) {
+ minEdges = edgeCount;
+ minNode = aLayers.ElementAt(i);
+ if (minEdges == 1) {
+ break;
+ }
+ }
+ }
+
+ if (minNode) {
+ // Remove all of them!
+ graph.RemoveEdgesTo(minNode);
+ noIncoming.AppendElement(minNode);
+ }
+ }
+ } while (!noIncoming.IsEmpty());
+ NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
+#ifdef DEBUG
+ if (gfxEnv::DumpLayerSortList()) {
+ fprintf(stderr, " --- Layers after sorting: --- \n");
+ DumpLayerList(sortedList);
+ }
+#endif
+
+ aLayers.Clear();
+ aLayers.AppendElements(sortedList);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/LayerSorter.h b/gfx/layers/LayerSorter.h
new file mode 100644
index 000000000..b9f776e83
--- /dev/null
+++ b/gfx/layers/LayerSorter.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSORTER_H
+#define GFX_LAYERSORTER_H
+
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSORTER_H */
diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp
new file mode 100644
index 000000000..5604fafee
--- /dev/null
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -0,0 +1,679 @@
+/*-*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeInvalidation.h"
+
+#include <stdint.h> // for uint32_t
+#include "ImageContainer.h" // for ImageContainer
+#include "ImageLayers.h" // for ImageLayer, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "Units.h" // for ParentLayerIntRect
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsDataHashtable.h" // for nsDataHashtable
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsHashKeys.h" // for nsPtrHashKey
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for IntRect
+#include "nsTArray.h" // for AutoTArray, nsTArray_Impl
+#include "mozilla/Poison.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+struct LayerPropertiesBase;
+UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false);
+
+/**
+ * Get accumulated transform of from the context creating layer to the
+ * given layer.
+ */
+static Matrix4x4
+GetTransformIn3DContext(Layer* aLayer) {
+ Matrix4x4 transform = aLayer->GetLocalTransform();
+ for (Layer* layer = aLayer->GetParent();
+ layer && layer->Extend3DContext();
+ layer = layer->GetParent()) {
+ transform = transform * layer->GetLocalTransform();
+ }
+ return transform;
+}
+
+/**
+ * Get a transform for the given layer depending on extending 3D
+ * context.
+ *
+ * @return local transform for layers not participating 3D rendering
+ * context, or the accmulated transform in the context for else.
+ */
+static Matrix4x4
+GetTransformForInvalidation(Layer* aLayer) {
+ return (!aLayer->Is3DContextLeaf() && !aLayer->Extend3DContext() ?
+ aLayer->GetLocalTransform() : GetTransformIn3DContext(aLayer));
+}
+
+static IntRect
+TransformRect(const IntRect& aRect, const Matrix4x4& aTransform)
+{
+ if (aRect.IsEmpty()) {
+ return IntRect();
+ }
+
+ Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
+ rect.RoundOut();
+
+ IntRect intRect;
+ if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
+ return IntRect();
+ }
+
+ return intRect;
+}
+
+static void
+AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const Matrix4x4& aTransform)
+{
+ for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
+ aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
+ }
+ aDest.SimplifyOutward(20);
+}
+
+static void
+AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource)
+{
+ aDest.Or(aDest, aSource);
+ aDest.SimplifyOutward(20);
+}
+
+/**
+ * Walks over this layer, and all descendant layers.
+ * If any of these are a ContainerLayer that reports invalidations to a PresShell,
+ * then report that the entire bounds have changed.
+ */
+static void
+NotifySubdocumentInvalidation(Layer* aLayer, NotifySubDocInvalidationFunc aCallback)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [aCallback] (Layer* layer)
+ {
+ layer->ClearInvalidRect();
+ if (layer->GetMaskLayer()) {
+ NotifySubdocumentInvalidation(layer->GetMaskLayer(), aCallback);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = layer->GetAncestorMaskLayerAt(i);
+ NotifySubdocumentInvalidation(maskLayer, aCallback);
+ }
+ },
+ [aCallback] (Layer* layer)
+ {
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (container) {
+ aCallback(container, container->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+ });
+}
+
+struct LayerPropertiesBase : public LayerProperties
+{
+ explicit LayerPropertiesBase(Layer* aLayer)
+ : mLayer(aLayer)
+ , mMaskLayer(nullptr)
+ , mVisibleRegion(mLayer->GetLocalVisibleRegion().ToUnknownRegion())
+ , mPostXScale(aLayer->GetPostXScale())
+ , mPostYScale(aLayer->GetPostYScale())
+ , mOpacity(aLayer->GetLocalOpacity())
+ , mUseClipRect(!!aLayer->GetLocalClipRect())
+ {
+ MOZ_COUNT_CTOR(LayerPropertiesBase);
+ if (aLayer->GetMaskLayer()) {
+ mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
+ }
+ for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
+ mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
+ }
+ if (mUseClipRect) {
+ mClipRect = *aLayer->GetLocalClipRect();
+ }
+ mTransform = GetTransformForInvalidation(aLayer);
+ }
+ LayerPropertiesBase()
+ : mLayer(nullptr)
+ , mMaskLayer(nullptr)
+ {
+ MOZ_COUNT_CTOR(LayerPropertiesBase);
+ }
+ ~LayerPropertiesBase()
+ {
+ MOZ_COUNT_DTOR(LayerPropertiesBase);
+ }
+
+protected:
+ LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
+ LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
+
+public:
+ virtual nsIntRegion ComputeDifferences(Layer* aRoot,
+ NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged);
+
+ virtual void MoveBy(const IntPoint& aOffset);
+
+ nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ // Bug 1251615: This canary is sometimes hit. We're still not sure why.
+ mCanary.Check();
+ bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
+ mLayer->GetPostXScale() != mPostXScale ||
+ mLayer->GetPostYScale() != mPostYScale;
+ const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect();
+ nsIntRegion result;
+
+ bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
+ if (!ancestorMaskChanged) {
+ for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
+ if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) {
+ ancestorMaskChanged = true;
+ break;
+ }
+ }
+ }
+
+ Layer* otherMask = mLayer->GetMaskLayer();
+ if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
+ ancestorMaskChanged ||
+ (mUseClipRect != !!otherClip) ||
+ mLayer->GetLocalOpacity() != mOpacity ||
+ transformChanged)
+ {
+ aGeometryChanged = true;
+ result = OldTransformedBounds();
+ AddRegion(result, NewTransformedBounds());
+
+ // We can't bail out early because we need to update mChildrenChanged.
+ }
+
+ AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
+ AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform);
+
+ if (mMaskLayer && otherMask) {
+ AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
+ }
+
+ for (size_t i = 0;
+ i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
+ i++)
+ {
+ AddTransformedRegion(result,
+ mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
+ }
+
+ if (mUseClipRect && otherClip) {
+ if (!mClipRect.IsEqualInterior(*otherClip)) {
+ aGeometryChanged = true;
+ nsIntRegion tmp;
+ tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
+ AddRegion(result, tmp);
+ }
+ }
+
+ mLayer->ClearInvalidRect();
+ return result;
+ }
+
+ void CheckCanary()
+ {
+ mCanary.Check();
+ mLayer->CheckCanary();
+ }
+
+ virtual IntRect NewTransformedBounds()
+ {
+ return TransformRect(mLayer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds(),
+ GetTransformForInvalidation(mLayer));
+ }
+
+ virtual IntRect OldTransformedBounds()
+ {
+ return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ return IntRect();
+ }
+
+ RefPtr<Layer> mLayer;
+ UniquePtr<LayerPropertiesBase> mMaskLayer;
+ nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
+ nsIntRegion mVisibleRegion;
+ Matrix4x4 mTransform;
+ float mPostXScale;
+ float mPostYScale;
+ float mOpacity;
+ ParentLayerIntRect mClipRect;
+ bool mUseClipRect;
+ mozilla::CorruptionCanary mCanary;
+};
+
+struct ContainerLayerProperties : public LayerPropertiesBase
+{
+ explicit ContainerLayerProperties(ContainerLayer* aLayer)
+ : LayerPropertiesBase(aLayer)
+ , mPreXScale(aLayer->GetPreXScale())
+ , mPreYScale(aLayer->GetPreYScale())
+ {
+ for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ child->CheckCanary();
+ mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));
+ }
+ }
+
+protected:
+ ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
+ ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
+
+public:
+ nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged) override
+ {
+ // Make sure we got our virtual call right
+ mSubtypeCanary.Check();
+ ContainerLayer* container = mLayer->AsContainerLayer();
+ nsIntRegion invalidOfLayer; // Invalid regions of this layer.
+ nsIntRegion result; // Invliad regions for children only.
+
+ container->CheckCanary();
+
+ bool childrenChanged = false;
+
+ if (mPreXScale != container->GetPreXScale() ||
+ mPreYScale != container->GetPreYScale()) {
+ aGeometryChanged = true;
+ invalidOfLayer = OldTransformedBounds();
+ AddRegion(invalidOfLayer, NewTransformedBounds());
+ childrenChanged = true;
+
+ // Can't bail out early, we need to update the child container layers
+ }
+
+ // A low frame rate is especially visible to users when scrolling, so we
+ // particularly want to avoid unnecessary invalidation at that time. For us
+ // here, that means avoiding unnecessary invalidation of child items when
+ // other children are added to or removed from our container layer, since
+ // that may be caused by children being scrolled in or out of view. We are
+ // less concerned with children changing order.
+ // TODO: Consider how we could avoid unnecessary invalidation when children
+ // change order, and whether the overhead would be worth it.
+
+ nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length());
+ for (uint32_t i = 0; i < mChildren.Length(); ++i) {
+ mChildren[i]->CheckCanary();
+ oldIndexMap.Put(mChildren[i]->mLayer, i);
+ }
+
+ uint32_t i = 0; // cursor into the old child list mChildren
+ for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
+ bool invalidateChildsCurrentArea = false;
+ if (i < mChildren.Length()) {
+ uint32_t childsOldIndex;
+ if (oldIndexMap.Get(child, &childsOldIndex)) {
+ if (childsOldIndex >= i) {
+ // Invalidate the old areas of layers that used to be between the
+ // current |child| and the previous |child| that was also in the
+ // old list mChildren (if any of those children have been reordered
+ // rather than removed, we will invalidate their new area when we
+ // encounter them in the new list):
+ for (uint32_t j = i; j < childsOldIndex; ++j) {
+ AddRegion(result, mChildren[j]->OldTransformedBounds());
+ childrenChanged |= true;
+ }
+ if (childsOldIndex >= mChildren.Length()) {
+ MOZ_CRASH("Out of bounds");
+ }
+ // Invalidate any regions of the child that have changed:
+ nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged);
+ i = childsOldIndex + 1;
+ if (!region.IsEmpty()) {
+ AddRegion(result, region);
+ childrenChanged |= true;
+ }
+ } else {
+ // We've already seen this child in mChildren (which means it must
+ // have been reordered) and invalidated its old area. We need to
+ // invalidate its new area too:
+ invalidateChildsCurrentArea = true;
+ }
+ } else {
+ // |child| is new
+ invalidateChildsCurrentArea = true;
+ }
+ } else {
+ // |child| is new, or was reordered to a higher index
+ invalidateChildsCurrentArea = true;
+ }
+ if (invalidateChildsCurrentArea) {
+ aGeometryChanged = true;
+ AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(),
+ GetTransformForInvalidation(child));
+ if (aCallback) {
+ NotifySubdocumentInvalidation(child, aCallback);
+ } else {
+ ClearInvalidations(child);
+ }
+ }
+ childrenChanged |= invalidateChildsCurrentArea;
+ }
+
+ // Process remaining removed children.
+ while (i < mChildren.Length()) {
+ childrenChanged |= true;
+ AddRegion(result, mChildren[i]->OldTransformedBounds());
+ i++;
+ }
+
+ if (aCallback) {
+ aCallback(container, result);
+ }
+
+ if (childrenChanged) {
+ container->SetChildrenChanged(true);
+ }
+
+ if (!mLayer->Extend3DContext()) {
+ // |result| contains invalid regions only of children.
+ result.Transform(GetTransformForInvalidation(mLayer));
+ }
+ // else, effective transforms have applied on children.
+
+ result.OrWith(invalidOfLayer);
+
+ return result;
+ }
+
+ IntRect NewTransformedBounds() override
+ {
+ if (mLayer->Extend3DContext()) {
+ IntRect result;
+ for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+ result = result.Union(child->NewTransformedBounds());
+ }
+ return result;
+ }
+
+ return LayerPropertiesBase::NewTransformedBounds();
+ }
+
+ IntRect OldTransformedBounds() override
+ {
+ if (mLayer->Extend3DContext()) {
+ IntRect result;
+ for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+ result = result.Union(child->OldTransformedBounds());
+ }
+ return result;
+ }
+ return LayerPropertiesBase::OldTransformedBounds();
+ }
+
+ // The old list of children:
+ mozilla::CorruptionCanary mSubtypeCanary;
+ nsTArray<UniquePtr<LayerPropertiesBase>> mChildren;
+ float mPreXScale;
+ float mPreYScale;
+};
+
+struct ColorLayerProperties : public LayerPropertiesBase
+{
+ explicit ColorLayerProperties(ColorLayer *aLayer)
+ : LayerPropertiesBase(aLayer)
+ , mColor(aLayer->GetColor())
+ , mBounds(aLayer->GetBounds())
+ { }
+
+protected:
+ ColorLayerProperties(const ColorLayerProperties& a) = delete;
+ ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
+
+public:
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
+
+ if (mColor != color->GetColor()) {
+ aGeometryChanged = true;
+ return NewTransformedBounds();
+ }
+
+ nsIntRegion boundsDiff;
+ boundsDiff.Xor(mBounds, color->GetBounds());
+
+ nsIntRegion result;
+ AddTransformedRegion(result, boundsDiff, mTransform);
+
+ return result;
+ }
+
+ Color mColor;
+ IntRect mBounds;
+};
+
+static ImageHost* GetImageHost(Layer* aLayer)
+{
+ LayerComposite* composite = aLayer->AsLayerComposite();
+ if (composite) {
+ return static_cast<ImageHost*>(composite->GetCompositableHost());
+ }
+ return nullptr;
+}
+
+struct ImageLayerProperties : public LayerPropertiesBase
+{
+ explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
+ : LayerPropertiesBase(aImage)
+ , mContainer(aImage->GetContainer())
+ , mImageHost(GetImageHost(aImage))
+ , mSamplingFilter(aImage->GetSamplingFilter())
+ , mScaleToSize(aImage->GetScaleToSize())
+ , mScaleMode(aImage->GetScaleMode())
+ , mLastProducerID(-1)
+ , mLastFrameID(-1)
+ , mIsMask(aIsMask)
+ {
+ if (mImageHost) {
+ mLastProducerID = mImageHost->GetLastProducerID();
+ mLastFrameID = mImageHost->GetLastFrameID();
+ }
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
+
+ if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
+ aGeometryChanged = true;
+ IntRect result = NewTransformedBounds();
+ result = result.Union(OldTransformedBounds());
+ return result;
+ }
+
+ ImageContainer* container = imageLayer->GetContainer();
+ ImageHost* host = GetImageHost(imageLayer);
+ if (mContainer != container ||
+ mSamplingFilter != imageLayer->GetSamplingFilter() ||
+ mScaleToSize != imageLayer->GetScaleToSize() ||
+ mScaleMode != imageLayer->GetScaleMode() ||
+ host != mImageHost ||
+ (host && host->GetProducerID() != mLastProducerID) ||
+ (host && host->GetFrameID() != mLastFrameID)) {
+ aGeometryChanged = true;
+
+ if (mIsMask) {
+ // Mask layers have an empty visible region, so we have to
+ // use the image size instead.
+ IntSize size;
+ if (container) {
+ size = container->GetCurrentSize();
+ }
+ if (host) {
+ size = host->GetImageSize();
+ }
+ IntRect rect(0, 0, size.width, size.height);
+ return TransformRect(rect, GetTransformForInvalidation(mLayer));
+ }
+ return NewTransformedBounds();
+ }
+
+ return IntRect();
+ }
+
+ RefPtr<ImageContainer> mContainer;
+ RefPtr<ImageHost> mImageHost;
+ SamplingFilter mSamplingFilter;
+ gfx::IntSize mScaleToSize;
+ ScaleMode mScaleMode;
+ int32_t mLastProducerID;
+ int32_t mLastFrameID;
+ bool mIsMask;
+};
+
+struct CanvasLayerProperties : public LayerPropertiesBase
+{
+ explicit CanvasLayerProperties(CanvasLayer* aCanvas)
+ : LayerPropertiesBase(aCanvas)
+ , mImageHost(GetImageHost(aCanvas))
+ {
+ mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
+ }
+
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
+ {
+ CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
+
+ ImageHost* host = GetImageHost(canvasLayer);
+ if (host && host->GetFrameID() != mFrameID) {
+ aGeometryChanged = true;
+
+ return NewTransformedBounds();
+ }
+
+ return IntRect();
+ }
+
+ RefPtr<ImageHost> mImageHost;
+ int32_t mFrameID;
+};
+
+UniquePtr<LayerPropertiesBase>
+CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */)
+{
+ if (!aRoot) {
+ return MakeUnique<LayerPropertiesBase>();
+ }
+
+ MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
+
+ aRoot->CheckCanary();
+
+ switch (aRoot->GetType()) {
+ case Layer::TYPE_CONTAINER:
+ case Layer::TYPE_REF:
+ return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
+ case Layer::TYPE_COLOR:
+ return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
+ case Layer::TYPE_IMAGE:
+ return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
+ case Layer::TYPE_CANVAS:
+ return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
+ case Layer::TYPE_READBACK:
+ case Layer::TYPE_SHADOW:
+ case Layer::TYPE_PAINTED:
+ return MakeUnique<LayerPropertiesBase>(aRoot);
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
+ return MakeUnique<LayerPropertiesBase>(aRoot);
+}
+
+/* static */ UniquePtr<LayerProperties>
+LayerProperties::CloneFrom(Layer* aRoot)
+{
+ return CloneLayerTreePropertiesInternal(aRoot);
+}
+
+/* static */ void
+LayerProperties::ClearInvalidations(Layer *aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ layer->ClearInvalidRect();
+ if (layer->GetMaskLayer()) {
+ ClearInvalidations(layer->GetMaskLayer());
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ ClearInvalidations(layer->GetAncestorMaskLayerAt(i));
+ }
+
+ }
+ );
+}
+
+nsIntRegion
+LayerPropertiesBase::ComputeDifferences(Layer* aRoot, NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged = nullptr)
+{
+ NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
+ if (mLayer != aRoot) {
+ if (aCallback) {
+ NotifySubdocumentInvalidation(aRoot, aCallback);
+ } else {
+ ClearInvalidations(aRoot);
+ }
+ IntRect result = TransformRect(aRoot->GetLocalVisibleRegion().ToUnknownRegion().GetBounds(),
+ aRoot->GetLocalTransform());
+ result = result.Union(OldTransformedBounds());
+ if (aGeometryChanged != nullptr) {
+ *aGeometryChanged = true;
+ }
+ return result;
+ } else {
+ bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
+ nsIntRegion invalid = ComputeChange(aCallback, geometryChanged);
+ if (aGeometryChanged != nullptr) {
+ *aGeometryChanged = geometryChanged;
+ }
+ return invalid;
+ }
+}
+
+void
+LayerPropertiesBase::MoveBy(const IntPoint& aOffset)
+{
+ mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/LayerTreeInvalidation.h b/gfx/layers/LayerTreeInvalidation.h
new file mode 100644
index 000000000..4c08f5245
--- /dev/null
+++ b/gfx/layers/LayerTreeInvalidation.h
@@ -0,0 +1,77 @@
+/*-*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYER_TREE_INVALIDATION_H
+#define GFX_LAYER_TREE_INVALIDATION_H
+
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/Point.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+class ContainerLayer;
+
+/**
+ * Callback for ContainerLayer invalidations.
+ *
+ * @param aContainer ContainerLayer being invalidated.
+ * @param aRegion Invalidated region in the ContainerLayer's coordinate
+ * space.
+ */
+typedef void (*NotifySubDocInvalidationFunc)(ContainerLayer* aLayer,
+ const nsIntRegion& aRegion);
+
+/**
+ * A set of cached layer properties (including those of child layers),
+ * used for comparing differences in layer trees.
+ */
+struct LayerProperties
+{
+protected:
+ LayerProperties() {}
+
+ LayerProperties(const LayerProperties& a) = delete;
+ LayerProperties& operator=(const LayerProperties& a) = delete;
+
+public:
+ virtual ~LayerProperties() {}
+
+ /**
+ * Copies the current layer tree properties into
+ * a new LayerProperties object.
+ *
+ * @param Layer tree to copy, or nullptr if we have no
+ * initial layer tree.
+ */
+ static UniquePtr<LayerProperties> CloneFrom(Layer* aRoot);
+
+ /**
+ * Clear all invalidation status from this layer tree.
+ */
+ static void ClearInvalidations(Layer* aRoot);
+
+ /**
+ * Compares a set of existing layer tree properties to the current layer
+ * tree and generates the changed rectangle.
+ *
+ * @param aRoot Root layer of the layer tree to compare against.
+ * @param aCallback If specified, callback to call when ContainerLayers
+ * are invalidated.
+ * @return Painted area changed by the layer tree changes.
+ */
+ virtual nsIntRegion ComputeDifferences(Layer* aRoot,
+ NotifySubDocInvalidationFunc aCallback,
+ bool* aGeometryChanged = nullptr) = 0;
+
+ virtual void MoveBy(const gfx::IntPoint& aOffset) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYER_TREE_INVALIDATON_H */
diff --git a/gfx/layers/LayerUserData.h b/gfx/layers/LayerUserData.h
new file mode 100644
index 000000000..a988b4b38
--- /dev/null
+++ b/gfx/layers/LayerUserData.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERUSERDATA_H
+#define GFX_LAYERUSERDATA_H
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Base class for userdata objects attached to layers and layer managers.
+ *
+ * We define it here in a separate header so clients only need to include
+ * this header for their class definitions, rather than pulling in Layers.h.
+ * Everything else in Layers.h should be forward-declarable.
+ */
+class LayerUserData {
+public:
+ virtual ~LayerUserData() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERUSERDATA_H */
diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp
new file mode 100644
index 000000000..ff5ab9b50
--- /dev/null
+++ b/gfx/layers/Layers.cpp
@@ -0,0 +1,2505 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Layers.h"
+#include <algorithm> // for max, min
+#include "apz/src/AsyncPanZoomController.h"
+#include "CompositableHost.h" // for CompositableHost
+#include "ImageContainer.h" // for ImageContainer, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "LayerSorter.h" // for SortLayersBy3DZOrder
+#include "LayersLogging.h" // for AppendToString
+#include "LayerUserData.h"
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "UnitTransforms.h" // for ViewAs
+#include "gfxEnv.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h"
+#include "gfxUtils.h" // for gfxUtils, etc
+#include "gfx2DGlue.h"
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/Telemetry.h" // for Accumulate
+#include "mozilla/ToString.h"
+#include "mozilla/dom/Animation.h" // for ComputedTimingFunction
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersMessages.h" // for TransformFunction, etc
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowableLayer
+#include "nsAString.h"
+#include "nsCSSValue.h" // for nsCSSValue::Array, etc
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsStyleStruct.h" // for nsTimingFunction, etc
+#include "protobuf/LayerScopePacket.pb.h"
+#include "mozilla/Compression.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+uint8_t gLayerManagerLayerBuilder;
+
+namespace mozilla {
+namespace layers {
+
+FILE*
+FILEOrDefault(FILE* aFile)
+{
+ return aFile ? aFile : stderr;
+}
+
+typedef FrameMetrics::ViewID ViewID;
+
+using namespace mozilla::gfx;
+using namespace mozilla::Compression;
+
+//--------------------------------------------------
+// LayerManager
+
+/* static */ mozilla::LogModule*
+LayerManager::GetLog()
+{
+ static LazyLogModule sLog("Layers");
+ return sLog;
+}
+
+FrameMetrics::ViewID
+LayerManager::GetRootScrollableLayerId()
+{
+ if (!mRoot) {
+ return FrameMetrics::NULL_SCROLL_ID;
+ }
+
+ LayerMetricsWrapper layerMetricsRoot = LayerMetricsWrapper(mRoot);
+
+ LayerMetricsWrapper rootScrollableLayerMetrics =
+ BreadthFirstSearch<ForwardIterator>(
+ layerMetricsRoot,
+ [](LayerMetricsWrapper aLayerMetrics)
+ {
+ return aLayerMetrics.Metrics().IsScrollable();
+ }
+ );
+
+ return rootScrollableLayerMetrics.IsValid() ?
+ rootScrollableLayerMetrics.Metrics().GetScrollId() :
+ FrameMetrics::NULL_SCROLL_ID;
+}
+
+LayerMetricsWrapper
+LayerManager::GetRootContentLayer()
+{
+ if (!mRoot) {
+ return LayerMetricsWrapper();
+ }
+
+ LayerMetricsWrapper root(mRoot);
+
+ return BreadthFirstSearch<ForwardIterator>(root,
+ [](LayerMetricsWrapper aLayerMetrics)
+ {
+ return aLayerMetrics.Metrics().IsRootContent();
+ }
+ );
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateOptimalDrawTarget(const gfx::IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSize,
+ aFormat);
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateOptimalMaskDrawTarget(const gfx::IntSize &aSize)
+{
+ return CreateOptimalDrawTarget(aSize, SurfaceFormat::A8);
+}
+
+already_AddRefed<DrawTarget>
+LayerManager::CreateDrawTarget(const IntSize &aSize,
+ SurfaceFormat aFormat)
+{
+ return gfxPlatform::GetPlatform()->
+ CreateOffscreenCanvasDrawTarget(aSize, aFormat);
+}
+
+already_AddRefed<PersistentBufferProvider>
+LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat)
+{
+ RefPtr<PersistentBufferProviderBasic> bufferProvider =
+ PersistentBufferProviderBasic::Create(aSize, aFormat,
+ gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
+
+ if (!bufferProvider) {
+ bufferProvider = PersistentBufferProviderBasic::Create(aSize, aFormat,
+ gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
+ }
+
+ return bufferProvider.forget();
+}
+
+#ifdef DEBUG
+void
+LayerManager::Mutated(Layer* aLayer)
+{
+}
+#endif // DEBUG
+
+already_AddRefed<ImageContainer>
+LayerManager::CreateImageContainer(ImageContainer::Mode flag)
+{
+ RefPtr<ImageContainer> container = new ImageContainer(flag);
+ return container.forget();
+}
+
+bool
+LayerManager::AreComponentAlphaLayersEnabled()
+{
+ return gfxPrefs::ComponentAlphaEnabled();
+}
+
+/*static*/ void
+LayerManager::LayerUserDataDestroy(void* data)
+{
+ delete static_cast<LayerUserData*>(data);
+}
+
+UniquePtr<LayerUserData>
+LayerManager::RemoveUserData(void* aKey)
+{
+ UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+ return d;
+}
+
+//--------------------------------------------------
+// Layer
+
+Layer::Layer(LayerManager* aManager, void* aImplData) :
+ mManager(aManager),
+ mParent(nullptr),
+ mNextSibling(nullptr),
+ mPrevSibling(nullptr),
+ mImplData(aImplData),
+ mMaskLayer(nullptr),
+ mPostXScale(1.0f),
+ mPostYScale(1.0f),
+ mOpacity(1.0),
+ mMixBlendMode(CompositionOp::OP_OVER),
+ mForceIsolatedGroup(false),
+ mContentFlags(0),
+ mUseTileSourceRect(false),
+ mIsFixedPosition(false),
+ mTransformIsPerspective(false),
+ mFixedPositionData(nullptr),
+ mStickyPositionData(nullptr),
+ mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
+ mScrollbarDirection(ScrollDirection::NONE),
+ mScrollbarThumbRatio(0.0f),
+ mIsScrollbarContainer(false),
+#ifdef DEBUG
+ mDebugColorIndex(0),
+#endif
+ mAnimationGeneration(0)
+{
+ MOZ_COUNT_CTOR(Layer);
+}
+
+Layer::~Layer()
+{
+ MOZ_COUNT_DTOR(Layer);
+}
+
+Animation*
+Layer::AddAnimation()
+{
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AddAnimation", this));
+
+ MOZ_ASSERT(!mPendingAnimations, "should have called ClearAnimations first");
+
+ Animation* anim = mAnimations.AppendElement();
+
+ Mutated();
+ return anim;
+}
+
+void
+Layer::ClearAnimations()
+{
+ mPendingAnimations = nullptr;
+
+ if (mAnimations.IsEmpty() && mAnimationData.IsEmpty()) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClearAnimations", this));
+ mAnimations.Clear();
+ mAnimationData.Clear();
+ Mutated();
+}
+
+Animation*
+Layer::AddAnimationForNextTransaction()
+{
+ MOZ_ASSERT(mPendingAnimations,
+ "should have called ClearAnimationsForNextTransaction first");
+
+ Animation* anim = mPendingAnimations->AppendElement();
+
+ return anim;
+}
+
+void
+Layer::ClearAnimationsForNextTransaction()
+{
+ // Ensure we have a non-null mPendingAnimations to mark a future clear.
+ if (!mPendingAnimations) {
+ mPendingAnimations = new AnimationArray;
+ }
+
+ mPendingAnimations->Clear();
+}
+
+static inline void
+SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
+{
+ aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
+}
+
+static nsCSSValueSharedList*
+CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
+{
+ nsAutoPtr<nsCSSValueList> result;
+ nsCSSValueList** resultTail = getter_Transfers(result);
+ for (uint32_t i = 0; i < aFunctions.Length(); i++) {
+ RefPtr<nsCSSValue::Array> arr;
+ switch (aFunctions[i].type()) {
+ case TransformFunction::TRotationX:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotationY:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotationZ:
+ {
+ const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotation:
+ {
+ const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
+ resultTail);
+ SetCSSAngle(angle, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TRotation3D:
+ {
+ float x = aFunctions[i].get_Rotation3D().x();
+ float y = aFunctions[i].get_Rotation3D().y();
+ float z = aFunctions[i].get_Rotation3D().z();
+ const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
+ SetCSSAngle(angle, arr->Item(4));
+ break;
+ }
+ case TransformFunction::TScale:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
+ break;
+ }
+ case TransformFunction::TTranslation:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_translate3d,
+ resultTail);
+ arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
+ arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
+ arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
+ break;
+ }
+ case TransformFunction::TSkewX:
+ {
+ const CSSAngle& x = aFunctions[i].get_SkewX().x();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
+ resultTail);
+ SetCSSAngle(x, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TSkewY:
+ {
+ const CSSAngle& y = aFunctions[i].get_SkewY().y();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
+ resultTail);
+ SetCSSAngle(y, arr->Item(1));
+ break;
+ }
+ case TransformFunction::TSkew:
+ {
+ const CSSAngle& x = aFunctions[i].get_Skew().x();
+ const CSSAngle& y = aFunctions[i].get_Skew().y();
+ arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skew,
+ resultTail);
+ SetCSSAngle(x, arr->Item(1));
+ SetCSSAngle(y, arr->Item(2));
+ break;
+ }
+ case TransformFunction::TTransformMatrix:
+ {
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
+ resultTail);
+ const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
+ arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
+ arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
+ arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
+ arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
+ arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
+ arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
+ arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
+ arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
+ arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
+ arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
+ arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
+ arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
+ arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
+ arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
+ arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
+ arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
+ break;
+ }
+ case TransformFunction::TPerspective:
+ {
+ float perspective = aFunctions[i].get_Perspective().value();
+ arr =
+ StyleAnimationValue::AppendTransformFunction(eCSSKeyword_perspective,
+ resultTail);
+ arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
+ break;
+ }
+ default:
+ NS_ASSERTION(false, "All functions should be implemented?");
+ }
+ }
+ if (aFunctions.Length() == 0) {
+ result = new nsCSSValueList();
+ result->mValue.SetNoneValue();
+ }
+ return new nsCSSValueSharedList(result.forget());
+}
+
+void
+Layer::SetAnimations(const AnimationArray& aAnimations)
+{
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this));
+
+ mAnimations = aAnimations;
+ mAnimationData.Clear();
+ for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+ Animation& animation = mAnimations[i];
+ // Adjust fill mode to fill forwards so that if the main thread is delayed
+ // in clearing this animation we don't introduce flicker by jumping back to
+ // the old underlying value
+ switch (static_cast<dom::FillMode>(animation.fillMode())) {
+ case dom::FillMode::None:
+ animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Forwards);
+ break;
+ case dom::FillMode::Backwards:
+ animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
+ break;
+ default:
+ break;
+ }
+
+ AnimData* data = mAnimationData.AppendElement();
+ InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
+ data->mFunctions;
+ const InfallibleTArray<AnimationSegment>& segments = animation.segments();
+ for (uint32_t j = 0; j < segments.Length(); j++) {
+ TimingFunction tf = segments.ElementAt(j).sampleFn();
+
+ Maybe<ComputedTimingFunction> ctf =
+ AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
+ functions.AppendElement(ctf);
+ }
+
+ // Precompute the StyleAnimationValues that we need if this is a transform
+ // animation.
+ InfallibleTArray<StyleAnimationValue>& startValues = data->mStartValues;
+ InfallibleTArray<StyleAnimationValue>& endValues = data->mEndValues;
+ for (uint32_t j = 0; j < segments.Length(); j++) {
+ const AnimationSegment& segment = segments[j];
+ StyleAnimationValue* startValue = startValues.AppendElement();
+ StyleAnimationValue* endValue = endValues.AppendElement();
+ if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
+ const InfallibleTArray<TransformFunction>& startFunctions =
+ segment.startState().get_ArrayOfTransformFunction();
+ startValue->SetTransformValue(CreateCSSValueList(startFunctions));
+
+ const InfallibleTArray<TransformFunction>& endFunctions =
+ segment.endState().get_ArrayOfTransformFunction();
+ endValue->SetTransformValue(CreateCSSValueList(endFunctions));
+ } else {
+ NS_ASSERTION(segment.endState().type() == Animatable::Tfloat,
+ "Unknown Animatable type");
+ startValue->SetFloatValue(segment.startState().get_float());
+ endValue->SetFloatValue(segment.endState().get_float());
+ }
+ }
+ }
+
+ Mutated();
+}
+
+void
+Layer::StartPendingAnimations(const TimeStamp& aReadyTime)
+{
+ ForEachNode<ForwardIterator>(
+ this,
+ [&aReadyTime](Layer *layer)
+ {
+ bool updated = false;
+ for (size_t animIdx = 0, animEnd = layer->mAnimations.Length();
+ animIdx < animEnd; animIdx++) {
+ Animation& anim = layer->mAnimations[animIdx];
+ if (anim.startTime().IsNull()) {
+ anim.startTime() = aReadyTime - anim.initialCurrentTime();
+ updated = true;
+ }
+ }
+ if (updated) {
+ layer->Mutated();
+ }
+ });
+}
+
+void
+Layer::SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller)
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+ mApzcs[aIndex] = controller;
+}
+
+AsyncPanZoomController*
+Layer::GetAsyncPanZoomController(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+#ifdef DEBUG
+ if (mApzcs[aIndex]) {
+ MOZ_ASSERT(GetFrameMetrics(aIndex).IsScrollable());
+ }
+#endif
+ return mApzcs[aIndex];
+}
+
+void
+Layer::ScrollMetadataChanged()
+{
+ mApzcs.SetLength(GetScrollMetadataCount());
+}
+
+void
+Layer::ApplyPendingUpdatesToSubtree()
+{
+ ForEachNode<ForwardIterator>(
+ this,
+ [] (Layer *layer)
+ {
+ layer->ApplyPendingUpdatesForThisTransaction();
+ });
+
+ // Once we're done recursing through the whole tree, clear the pending
+ // updates from the manager.
+ Manager()->ClearPendingScrollInfoUpdate();
+}
+
+bool
+Layer::IsOpaqueForVisibility()
+{
+ return GetEffectiveOpacity() == 1.0f &&
+ GetEffectiveMixBlendMode() == CompositionOp::OP_OVER;
+}
+
+bool
+Layer::CanUseOpaqueSurface()
+{
+ // If the visible content in the layer is opaque, there is no need
+ // for an alpha channel.
+ if (GetContentFlags() & CONTENT_OPAQUE)
+ return true;
+ // Also, if this layer is the bottommost layer in a container which
+ // doesn't need an alpha channel, we can use an opaque surface for this
+ // layer too. Any transparent areas must be covered by something else
+ // in the container.
+ ContainerLayer* parent = GetParent();
+ return parent && parent->GetFirstChild() == this &&
+ parent->CanUseOpaqueSurface();
+}
+
+// NB: eventually these methods will be defined unconditionally, and
+// can be moved into Layers.h
+const Maybe<ParentLayerIntRect>&
+Layer::GetLocalClipRect()
+{
+ if (LayerComposite* shadow = AsLayerComposite()) {
+ return shadow->GetShadowClipRect();
+ }
+ return GetClipRect();
+}
+
+const LayerIntRegion&
+Layer::GetLocalVisibleRegion()
+{
+ if (LayerComposite* shadow = AsLayerComposite()) {
+ return shadow->GetShadowVisibleRegion();
+ }
+ return GetVisibleRegion();
+}
+
+Matrix4x4
+Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
+ Matrix* aResidualTransform)
+{
+ if (aResidualTransform) {
+ *aResidualTransform = Matrix();
+ }
+
+ if (!mManager->IsSnappingEffectiveTransforms()) {
+ return aTransform;
+ }
+
+ Matrix matrix2D;
+ if (aTransform.CanDraw2D(&matrix2D) &&
+ !matrix2D.HasNonTranslation() &&
+ matrix2D.HasNonIntegerTranslation()) {
+ auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
+ Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x,
+ snappedTranslation.y);
+ Matrix4x4 result = Matrix4x4::From2D(snappedMatrix);
+ if (aResidualTransform) {
+ // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
+ // (I.e., appying snappedMatrix after aResidualTransform gives the
+ // ideal transform.)
+ *aResidualTransform =
+ Matrix::Translation(matrix2D._31 - snappedTranslation.x,
+ matrix2D._32 - snappedTranslation.y);
+ }
+ return result;
+ }
+
+ return SnapTransformTranslation3D(aTransform, aResidualTransform);
+}
+
+Matrix4x4
+Layer::SnapTransformTranslation3D(const Matrix4x4& aTransform,
+ Matrix* aResidualTransform)
+{
+ if(aTransform.IsSingular() ||
+ aTransform.HasPerspectiveComponent() ||
+ aTransform.HasNonTranslation() ||
+ !aTransform.HasNonIntegerTranslation()) {
+ // For a singular transform, there is no reversed matrix, so we
+ // don't snap it.
+ // For a perspective transform, the content is transformed in
+ // non-linear, so we don't snap it too.
+ return aTransform;
+ }
+
+ // Snap for 3D Transforms
+
+ Point3D transformedOrigin = aTransform.TransformPoint(Point3D());
+
+ // Compute the transformed snap by rounding the values of
+ // transformed origin.
+ auto transformedSnapXY = IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
+ Matrix4x4 inverse = aTransform;
+ inverse.Invert();
+ // see Matrix4x4::ProjectPoint()
+ Float transformedSnapZ =
+ inverse._33 == 0 ? 0 : (-(transformedSnapXY.x * inverse._13 +
+ transformedSnapXY.y * inverse._23 +
+ inverse._43) / inverse._33);
+ Point3D transformedSnap =
+ Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
+ if (transformedOrigin == transformedSnap) {
+ return aTransform;
+ }
+
+ // Compute the snap from the transformed snap.
+ Point3D snap = inverse.TransformPoint(transformedSnap);
+ if (snap.z > 0.001 || snap.z < -0.001) {
+ // Allow some level of accumulated computation error.
+ MOZ_ASSERT(inverse._33 == 0.0);
+ return aTransform;
+ }
+
+ // The difference between the origin and snap is the residual transform.
+ if (aResidualTransform) {
+ // The residual transform is to translate the snap to the origin
+ // of the content buffer.
+ *aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
+ }
+
+ // Translate transformed origin to transformed snap since the
+ // residual transform would trnslate the snap to the origin.
+ Point3D transformedShift = transformedSnap - transformedOrigin;
+ Matrix4x4 result = aTransform;
+ result.PostTranslate(transformedShift.x,
+ transformedShift.y,
+ transformedShift.z);
+
+ // For non-2d transform, residual translation could be more than
+ // 0.5 pixels for every axis.
+
+ return result;
+}
+
+Matrix4x4
+Layer::SnapTransform(const Matrix4x4& aTransform,
+ const gfxRect& aSnapRect,
+ Matrix* aResidualTransform)
+{
+ if (aResidualTransform) {
+ *aResidualTransform = Matrix();
+ }
+
+ Matrix matrix2D;
+ Matrix4x4 result;
+ if (mManager->IsSnappingEffectiveTransforms() &&
+ aTransform.Is2D(&matrix2D) &&
+ gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
+ matrix2D.PreservesAxisAlignedRectangles()) {
+ auto transformedTopLeft = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopLeft())));
+ auto transformedTopRight = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopRight())));
+ auto transformedBottomRight = IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.BottomRight())));
+
+ Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
+ transformedTopLeft, transformedTopRight, transformedBottomRight);
+
+ result = Matrix4x4::From2D(snappedMatrix);
+ if (aResidualTransform && !snappedMatrix.IsSingular()) {
+ // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
+ // (i.e., appying snappedMatrix after aResidualTransform gives the
+ // ideal transform.
+ Matrix snappedMatrixInverse = snappedMatrix;
+ snappedMatrixInverse.Invert();
+ *aResidualTransform = matrix2D * snappedMatrixInverse;
+ }
+ } else {
+ result = aTransform;
+ }
+ return result;
+}
+
+static bool
+AncestorLayerMayChangeTransform(Layer* aLayer)
+{
+ for (Layer* l = aLayer; l; l = l->GetParent()) {
+ if (l->GetContentFlags() & Layer::CONTENT_MAY_CHANGE_TRANSFORM) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Layer::MayResample()
+{
+ Matrix transform2d;
+ return !GetEffectiveTransform().Is2D(&transform2d) ||
+ ThebesMatrix(transform2d).HasNonIntegerTranslation() ||
+ AncestorLayerMayChangeTransform(this);
+}
+
+RenderTargetIntRect
+Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect)
+{
+ ContainerLayer* container = GetParent();
+ ContainerLayer* containerChild = nullptr;
+ NS_ASSERTION(GetParent(), "This can't be called on the root!");
+
+ // Find the layer creating the 3D context.
+ while (container->Extend3DContext() &&
+ !container->UseIntermediateSurface()) {
+ containerChild = container;
+ container = container->GetParent();
+ MOZ_ASSERT(container);
+ }
+
+ // Find the nearest layer with a clip, or this layer.
+ // ContainerState::SetupScrollingMetadata() may install a clip on
+ // the layer.
+ Layer *clipLayer =
+ containerChild && containerChild->GetLocalClipRect() ?
+ containerChild : this;
+
+ // Establish initial clip rect: it's either the one passed in, or
+ // if the parent has an intermediate surface, it's the extents of that surface.
+ RenderTargetIntRect currentClip;
+ if (container->UseIntermediateSurface()) {
+ currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size());
+ } else {
+ currentClip = aCurrentScissorRect;
+ }
+
+ if (!clipLayer->GetLocalClipRect()) {
+ return currentClip;
+ }
+
+ if (GetLocalVisibleRegion().IsEmpty() &&
+ !(AsLayerComposite() && AsLayerComposite()->NeedToDrawCheckerboarding())) {
+ // When our visible region is empty, our parent may not have created the
+ // intermediate surface that we would require for correct clipping; however,
+ // this does not matter since we are invisible.
+ // Make sure we still compute a clip rect if we want to draw checkboarding
+ // for this layer, since we want to do this even if the layer is invisible.
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+
+ const RenderTargetIntRect clipRect =
+ ViewAs<RenderTargetPixel>(*clipLayer->GetLocalClipRect(),
+ PixelCastJustification::RenderTargetIsParentLayerForRoot);
+ if (clipRect.IsEmpty()) {
+ // We might have a non-translation transform in the container so we can't
+ // use the code path below.
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+
+ RenderTargetIntRect scissor = clipRect;
+ if (!container->UseIntermediateSurface()) {
+ gfx::Matrix matrix;
+ DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix);
+ // See DefaultComputeEffectiveTransforms below
+ NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(),
+ "Non preserves axis aligned transform with clipped child should have forced intermediate surface");
+ gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height);
+ gfxRect trScissor = gfx::ThebesRect(matrix.TransformBounds(r));
+ trScissor.Round();
+ IntRect tmp;
+ if (!gfxUtils::GfxRectToIntRect(trScissor, &tmp)) {
+ return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
+ }
+ scissor = ViewAs<RenderTargetPixel>(tmp);
+
+ // Find the nearest ancestor with an intermediate surface
+ do {
+ container = container->GetParent();
+ } while (container && !container->UseIntermediateSurface());
+ }
+
+ if (container) {
+ scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft());
+ }
+ return currentClip.Intersect(scissor);
+}
+
+Maybe<ParentLayerIntRect>
+Layer::GetScrolledClipRect() const
+{
+ return mScrolledClip ? Some(mScrolledClip->GetClipRect()) : Nothing();
+}
+
+const ScrollMetadata&
+Layer::GetScrollMetadata(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < GetScrollMetadataCount());
+ return mScrollMetadata[aIndex];
+}
+
+const FrameMetrics&
+Layer::GetFrameMetrics(uint32_t aIndex) const
+{
+ return GetScrollMetadata(aIndex).GetMetrics();
+}
+
+bool
+Layer::HasScrollableFrameMetrics() const
+{
+ for (uint32_t i = 0; i < GetScrollMetadataCount(); i++) {
+ if (GetFrameMetrics(i).IsScrollable()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Layer::IsScrollInfoLayer() const
+{
+ // A scrollable container layer with no children
+ return AsContainerLayer()
+ && HasScrollableFrameMetrics()
+ && !GetFirstChild();
+}
+
+Matrix4x4
+Layer::GetTransform() const
+{
+ Matrix4x4 transform = mTransform;
+ transform.PostScale(GetPostXScale(), GetPostYScale(), 1.0f);
+ if (const ContainerLayer* c = AsContainerLayer()) {
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ }
+ return transform;
+}
+
+const CSSTransformMatrix
+Layer::GetTransformTyped() const
+{
+ return ViewAs<CSSTransformMatrix>(GetTransform());
+}
+
+Matrix4x4
+Layer::GetLocalTransform()
+{
+ if (LayerComposite* shadow = AsLayerComposite())
+ return shadow->GetShadowTransform();
+ else
+ return GetTransform();
+}
+
+const LayerToParentLayerMatrix4x4
+Layer::GetLocalTransformTyped()
+{
+ return ViewAs<LayerToParentLayerMatrix4x4>(GetLocalTransform());
+}
+
+bool
+Layer::HasTransformAnimation() const
+{
+ for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+ if (mAnimations[i].property() == eCSSProperty_transform) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+Layer::ApplyPendingUpdatesForThisTransaction()
+{
+ if (mPendingTransform && *mPendingTransform != mTransform) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
+ mTransform = *mPendingTransform;
+ Mutated();
+ }
+ mPendingTransform = nullptr;
+
+ if (mPendingAnimations) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
+ mPendingAnimations->SwapElements(mAnimations);
+ mPendingAnimations = nullptr;
+ Mutated();
+ }
+
+ for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+ FrameMetrics& fm = mScrollMetadata[i].GetMetrics();
+ Maybe<ScrollUpdateInfo> update = Manager()->GetPendingScrollInfoUpdate(fm.GetScrollId());
+ if (update) {
+ fm.UpdatePendingScrollInfo(update.value());
+ Mutated();
+ }
+ }
+}
+
+float
+Layer::GetLocalOpacity()
+{
+ float opacity = mOpacity;
+ if (LayerComposite* shadow = AsLayerComposite())
+ opacity = shadow->GetShadowOpacity();
+ return std::min(std::max(opacity, 0.0f), 1.0f);
+}
+
+float
+Layer::GetEffectiveOpacity()
+{
+ float opacity = GetLocalOpacity();
+ for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
+ c = c->GetParent()) {
+ opacity *= c->GetLocalOpacity();
+ }
+ return opacity;
+}
+
+CompositionOp
+Layer::GetEffectiveMixBlendMode()
+{
+ if(mMixBlendMode != CompositionOp::OP_OVER)
+ return mMixBlendMode;
+ for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
+ c = c->GetParent()) {
+ if(c->mMixBlendMode != CompositionOp::OP_OVER)
+ return c->mMixBlendMode;
+ }
+
+ return mMixBlendMode;
+}
+
+void
+Layer::ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface)
+{
+ if (GetMaskLayer()) {
+ ComputeEffectiveTransformForMaskLayer(GetMaskLayer(), aTransformToSurface);
+ }
+ for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+ Layer* maskLayer = GetAncestorMaskLayerAt(i);
+ ComputeEffectiveTransformForMaskLayer(maskLayer, aTransformToSurface);
+ }
+}
+
+/* static */ void
+Layer::ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer, const gfx::Matrix4x4& aTransformToSurface)
+{
+ aMaskLayer->mEffectiveTransform = aTransformToSurface;
+
+#ifdef DEBUG
+ bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+#endif
+ // The mask layer can have an async transform applied to it in some
+ // situations, so be sure to use its GetLocalTransform() rather than
+ // its GetTransform().
+ aMaskLayer->mEffectiveTransform = aMaskLayer->GetLocalTransform() *
+ aMaskLayer->mEffectiveTransform;
+}
+
+RenderTargetRect
+Layer::TransformRectToRenderTarget(const LayerIntRect& aRect)
+{
+ LayerRect rect(aRect);
+ RenderTargetRect quad = RenderTargetRect::FromUnknownRect(
+ GetEffectiveTransform().TransformBounds(rect.ToUnknownRect()));
+ return quad;
+}
+
+bool
+Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
+ IntPoint* aLayerOffset)
+{
+ MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
+
+ if (!GetParent()) {
+ return false;
+ }
+
+ IntPoint offset;
+ aResult = GetLocalVisibleRegion().ToUnknownRegion();
+ for (Layer* layer = this; layer; layer = layer->GetParent()) {
+ gfx::Matrix matrix;
+ if (!layer->GetLocalTransform().Is2D(&matrix) ||
+ !matrix.IsTranslation()) {
+ return false;
+ }
+
+ // The offset of |layer| to its parent.
+ auto currentLayerOffset = IntPoint::Round(matrix.GetTranslation());
+
+ // Translate the accumulated visible region of |this| by the offset of
+ // |layer|.
+ aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
+
+ // If the parent layer clips its lower layers, clip the visible region
+ // we're accumulating.
+ if (layer->GetLocalClipRect()) {
+ aResult.AndWith(layer->GetLocalClipRect()->ToUnknownRect());
+ }
+
+ // Now we need to walk across the list of siblings for this parent layer,
+ // checking to see if any of these layer trees obscure |this|. If so,
+ // remove these areas from the visible region as well. This will pick up
+ // chrome overlays like a tab modal prompt.
+ Layer* sibling;
+ for (sibling = layer->GetNextSibling(); sibling;
+ sibling = sibling->GetNextSibling()) {
+ gfx::Matrix siblingMatrix;
+ if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
+ !siblingMatrix.IsTranslation()) {
+ continue;
+ }
+
+ // Retreive the translation from sibling to |layer|. The accumulated
+ // visible region is currently oriented with |layer|.
+ auto siblingOffset = IntPoint::Round(siblingMatrix.GetTranslation());
+ nsIntRegion siblingVisibleRegion(sibling->GetLocalVisibleRegion().ToUnknownRegion());
+ // Translate the siblings region to |layer|'s origin.
+ siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
+ // Apply the sibling's clip.
+ // Layer clip rects are not affected by the layer's transform.
+ Maybe<ParentLayerIntRect> clipRect = sibling->GetLocalClipRect();
+ if (clipRect) {
+ siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
+ }
+ // Subtract the sibling visible region from the visible region of |this|.
+ aResult.SubOut(siblingVisibleRegion);
+ }
+
+ // Keep track of the total offset for aLayerOffset. We use this in plugin
+ // positioning code.
+ offset += currentLayerOffset;
+ }
+
+ *aLayerOffset = IntPoint(offset.x, offset.y);
+ return true;
+}
+
+Maybe<ParentLayerIntRect>
+Layer::GetCombinedClipRect() const
+{
+ Maybe<ParentLayerIntRect> clip = GetClipRect();
+
+ clip = IntersectMaybeRects(clip, GetScrolledClipRect());
+
+ for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
+ clip = IntersectMaybeRects(clip, mScrollMetadata[i].GetClipRect());
+ }
+
+ return clip;
+}
+
+ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData),
+ mFirstChild(nullptr),
+ mLastChild(nullptr),
+ mPreXScale(1.0f),
+ mPreYScale(1.0f),
+ mInheritedXScale(1.0f),
+ mInheritedYScale(1.0f),
+ mPresShellResolution(1.0f),
+ mScaleToResolution(false),
+ mUseIntermediateSurface(false),
+ mSupportsComponentAlphaChildren(false),
+ mMayHaveReadbackChild(false),
+ mChildrenChanged(false),
+ mEventRegionsOverride(EventRegionsOverride::NoOverride)
+{
+ MOZ_COUNT_CTOR(ContainerLayer);
+ mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
+}
+
+ContainerLayer::~ContainerLayer()
+{
+ MOZ_COUNT_DTOR(ContainerLayer);
+}
+
+bool
+ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
+{
+ if(aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if(aChild->GetParent()) {
+ NS_ERROR("aChild already in the tree");
+ return false;
+ }
+ if (aChild->GetNextSibling() || aChild->GetPrevSibling()) {
+ NS_ERROR("aChild already has siblings?");
+ return false;
+ }
+ if (aAfter && (aAfter->Manager() != Manager() ||
+ aAfter->GetParent() != this))
+ {
+ NS_ERROR("aAfter is not our child");
+ return false;
+ }
+
+ aChild->SetParent(this);
+ if (aAfter == mLastChild) {
+ mLastChild = aChild;
+ }
+ if (!aAfter) {
+ aChild->SetNextSibling(mFirstChild);
+ if (mFirstChild) {
+ mFirstChild->SetPrevSibling(aChild);
+ }
+ mFirstChild = aChild;
+ NS_ADDREF(aChild);
+ DidInsertChild(aChild);
+ return true;
+ }
+
+ Layer* next = aAfter->GetNextSibling();
+ aChild->SetNextSibling(next);
+ aChild->SetPrevSibling(aAfter);
+ if (next) {
+ next->SetPrevSibling(aChild);
+ }
+ aAfter->SetNextSibling(aChild);
+ NS_ADDREF(aChild);
+ DidInsertChild(aChild);
+ return true;
+}
+
+bool
+ContainerLayer::RemoveChild(Layer *aChild)
+{
+ if (aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if (aChild->GetParent() != this) {
+ NS_ERROR("aChild not our child");
+ return false;
+ }
+
+ Layer* prev = aChild->GetPrevSibling();
+ Layer* next = aChild->GetNextSibling();
+ if (prev) {
+ prev->SetNextSibling(next);
+ } else {
+ this->mFirstChild = next;
+ }
+ if (next) {
+ next->SetPrevSibling(prev);
+ } else {
+ this->mLastChild = prev;
+ }
+
+ aChild->SetNextSibling(nullptr);
+ aChild->SetPrevSibling(nullptr);
+ aChild->SetParent(nullptr);
+
+ this->DidRemoveChild(aChild);
+ NS_RELEASE(aChild);
+ return true;
+}
+
+
+bool
+ContainerLayer::RepositionChild(Layer* aChild, Layer* aAfter)
+{
+ if (aChild->Manager() != Manager()) {
+ NS_ERROR("Child has wrong manager");
+ return false;
+ }
+ if (aChild->GetParent() != this) {
+ NS_ERROR("aChild not our child");
+ return false;
+ }
+ if (aAfter && (aAfter->Manager() != Manager() ||
+ aAfter->GetParent() != this))
+ {
+ NS_ERROR("aAfter is not our child");
+ return false;
+ }
+ if (aChild == aAfter) {
+ NS_ERROR("aChild cannot be the same as aAfter");
+ return false;
+ }
+
+ Layer* prev = aChild->GetPrevSibling();
+ Layer* next = aChild->GetNextSibling();
+ if (prev == aAfter) {
+ // aChild is already in the correct position, nothing to do.
+ return true;
+ }
+ if (prev) {
+ prev->SetNextSibling(next);
+ } else {
+ mFirstChild = next;
+ }
+ if (next) {
+ next->SetPrevSibling(prev);
+ } else {
+ mLastChild = prev;
+ }
+ if (!aAfter) {
+ aChild->SetPrevSibling(nullptr);
+ aChild->SetNextSibling(mFirstChild);
+ if (mFirstChild) {
+ mFirstChild->SetPrevSibling(aChild);
+ }
+ mFirstChild = aChild;
+ return true;
+ }
+
+ Layer* afterNext = aAfter->GetNextSibling();
+ if (afterNext) {
+ afterNext->SetPrevSibling(aChild);
+ } else {
+ mLastChild = aChild;
+ }
+ aAfter->SetNextSibling(aChild);
+ aChild->SetPrevSibling(aAfter);
+ aChild->SetNextSibling(afterNext);
+ return true;
+}
+
+void
+ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
+ mInheritedXScale, mInheritedYScale,
+ mPresShellResolution, mScaleToResolution,
+ mEventRegionsOverride);
+}
+
+bool
+ContainerLayer::Creates3DContextWithExtendingChildren()
+{
+ if (Extend3DContext()) {
+ return false;
+ }
+ for (Layer* child = GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ if (child->Extend3DContext()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+ContainerLayer::HasMultipleChildren()
+{
+ uint32_t count = 0;
+ for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+ const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
+ if (clipRect && clipRect->IsEmpty())
+ continue;
+ if (child->GetLocalVisibleRegion().IsEmpty())
+ continue;
+ ++count;
+ if (count > 1)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Collect all leaf descendants of the current 3D context.
+ */
+void
+ContainerLayer::Collect3DContextLeaves(nsTArray<Layer*>& aToSort)
+{
+ ForEachNode<ForwardIterator>(
+ (Layer*) this,
+ [this, &aToSort](Layer* layer)
+ {
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (layer == this || (container && container->Extend3DContext() &&
+ !container->UseIntermediateSurface())) {
+ return TraversalFlag::Continue;
+ }
+ else {
+ aToSort.AppendElement(layer);
+ return TraversalFlag::Skip;
+ }
+ }
+ );
+}
+
+void
+ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+{
+ AutoTArray<Layer*, 10> toSort;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ ContainerLayer* container = l->AsContainerLayer();
+ if (container && container->Extend3DContext() &&
+ !container->UseIntermediateSurface()) {
+ container->Collect3DContextLeaves(toSort);
+ } else {
+ if (toSort.Length() > 0) {
+ SortLayersBy3DZOrder(toSort);
+ aArray.AppendElements(Move(toSort));
+ // XXX The move analysis gets confused here, because toSort gets moved
+ // here, and then gets used again outside of the loop. To clarify that
+ // we realize that the array is going to be empty to the move checker,
+ // we clear it again here. (This method renews toSort for the move
+ // analysis)
+ toSort.ClearAndRetainStorage();
+ }
+ aArray.AppendElement(l);
+ }
+ }
+ if (toSort.Length() > 0) {
+ SortLayersBy3DZOrder(toSort);
+ aArray.AppendElements(Move(toSort));
+ }
+}
+
+void
+ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
+{
+ Matrix residual;
+ Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+
+ // Keep 3D transforms for leaves to keep z-order sorting correct.
+ if (!Extend3DContext() && !Is3DContextLeaf()) {
+ idealTransform.ProjectTo2D();
+ }
+
+ bool useIntermediateSurface;
+ if (HasMaskLayers() ||
+ GetForceIsolatedGroup()) {
+ useIntermediateSurface = true;
+#ifdef MOZ_DUMP_PAINTING
+ } else if (gfxEnv::DumpPaintIntermediate() && !Extend3DContext()) {
+ useIntermediateSurface = true;
+#endif
+ } else {
+ /* Don't use an intermediate surface for opacity when it's within a 3d
+ * context, since we'd rather keep the 3d effects. This matches the
+ * WebKit/blink behaviour, but is changing in the latest spec.
+ */
+ float opacity = GetEffectiveOpacity();
+ CompositionOp blendMode = GetEffectiveMixBlendMode();
+ if ((HasMultipleChildren() || Creates3DContextWithExtendingChildren()) &&
+ ((opacity != 1.0f && !Extend3DContext()) ||
+ (blendMode != CompositionOp::OP_OVER))) {
+ useIntermediateSurface = true;
+ } else if (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren()) {
+ useIntermediateSurface = true;
+ } else {
+ useIntermediateSurface = false;
+ gfx::Matrix contTransform;
+ bool checkClipRect = false;
+ bool checkMaskLayers = false;
+
+ if (!idealTransform.Is2D(&contTransform)) {
+ // In 3D case, always check if we should use IntermediateSurface.
+ checkClipRect = true;
+ checkMaskLayers = true;
+ } else {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ if (!contTransform.PreservesAxisAlignedRectangles()) {
+#else
+ if (gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) {
+#endif
+ checkClipRect = true;
+ }
+ /* In 2D case, only translation and/or positive scaling can be done w/o using IntermediateSurface.
+ * Otherwise, when rotation or flip happen, we should check whether to use IntermediateSurface.
+ */
+ if (contTransform.HasNonAxisAlignedTransform() || contTransform.HasNegativeScaling()) {
+ checkMaskLayers = true;
+ }
+ }
+
+ if (checkClipRect || checkMaskLayers) {
+ for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
+ const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
+ /* We can't (easily) forward our transform to children with a non-empty clip
+ * rect since it would need to be adjusted for the transform. See
+ * the calculations performed by CalculateScissorRect above.
+ * Nor for a child with a mask layer.
+ */
+ if (checkClipRect && (clipRect && !clipRect->IsEmpty() && !child->GetLocalVisibleRegion().IsEmpty())) {
+ useIntermediateSurface = true;
+ break;
+ }
+ if (checkMaskLayers && child->HasMaskLayers()) {
+ useIntermediateSurface = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ NS_ASSERTION(!Extend3DContext() || !useIntermediateSurface, "Can't have an intermediate surface with preserve-3d!");
+
+ if (useIntermediateSurface) {
+ mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
+ } else {
+ mEffectiveTransform = idealTransform;
+ }
+
+ // For layers extending 3d context, its ideal transform should be
+ // applied on children.
+ if (!Extend3DContext()) {
+ // Without this projection, non-container children would get a 3D
+ // transform while 2D is expected.
+ idealTransform.ProjectTo2D();
+ }
+ mUseIntermediateSurface = useIntermediateSurface && !GetLocalVisibleRegion().IsEmpty();
+ if (useIntermediateSurface) {
+ ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
+ } else {
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+void
+ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
+{
+ if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
+ !Manager()->AreComponentAlphaLayersEnabled()) {
+ mSupportsComponentAlphaChildren = false;
+ if (aNeedsSurfaceCopy) {
+ *aNeedsSurfaceCopy = false;
+ }
+ return;
+ }
+
+ mSupportsComponentAlphaChildren = false;
+ bool needsSurfaceCopy = false;
+ CompositionOp blendMode = GetEffectiveMixBlendMode();
+ if (UseIntermediateSurface()) {
+ if (GetLocalVisibleRegion().GetNumRects() == 1 &&
+ (GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ mSupportsComponentAlphaChildren = true;
+ } else {
+ gfx::Matrix transform;
+ if (HasOpaqueAncestorLayer(this) &&
+ GetEffectiveTransform().Is2D(&transform) &&
+ !gfx::ThebesMatrix(transform).HasNonIntegerTranslation() &&
+ blendMode == gfx::CompositionOp::OP_OVER) {
+ mSupportsComponentAlphaChildren = true;
+ needsSurfaceCopy = true;
+ }
+ }
+ } else if (blendMode == gfx::CompositionOp::OP_OVER) {
+ mSupportsComponentAlphaChildren =
+ (GetContentFlags() & Layer::CONTENT_OPAQUE) ||
+ (GetParent() && GetParent()->SupportsComponentAlphaChildren());
+ }
+
+ if (aNeedsSurfaceCopy) {
+ *aNeedsSurfaceCopy = mSupportsComponentAlphaChildren && needsSurfaceCopy;
+ }
+}
+
+void
+ContainerLayer::ComputeEffectiveTransformsForChildren(const Matrix4x4& aTransformToSurface)
+{
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ l->ComputeEffectiveTransforms(aTransformToSurface);
+ }
+}
+
+/* static */ bool
+ContainerLayer::HasOpaqueAncestorLayer(Layer* aLayer)
+{
+ for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
+ if (l->GetContentFlags() & Layer::CONTENT_OPAQUE)
+ return true;
+ }
+ return false;
+}
+
+void
+ContainerLayer::DidRemoveChild(Layer* aLayer)
+{
+ PaintedLayer* tl = aLayer->AsPaintedLayer();
+ if (tl && tl->UsedForReadback()) {
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (l->GetType() == TYPE_READBACK) {
+ static_cast<ReadbackLayer*>(l)->NotifyPaintedLayerRemoved(tl);
+ }
+ }
+ }
+ if (aLayer->GetType() == TYPE_READBACK) {
+ static_cast<ReadbackLayer*>(aLayer)->NotifyRemoved();
+ }
+}
+
+void
+ContainerLayer::DidInsertChild(Layer* aLayer)
+{
+ if (aLayer->GetType() == TYPE_READBACK) {
+ mMayHaveReadbackChild = true;
+ }
+}
+
+void
+RefLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = RefLayerAttributes(GetReferentId(), mEventRegionsOverride);
+}
+
+/**
+ * StartFrameTimeRecording, together with StopFrameTimeRecording
+ * enable recording of frame intervals.
+ *
+ * To allow concurrent consumers, a cyclic array is used which serves all
+ * consumers, practically stateless with regard to consumers.
+ *
+ * To save resources, the buffer is allocated on first call to StartFrameTimeRecording
+ * and recording is paused if no consumer which called StartFrameTimeRecording is able
+ * to get valid results (because the cyclic buffer was overwritten since that call).
+ *
+ * To determine availability of the data upon StopFrameTimeRecording:
+ * - mRecording.mNextIndex increases on each PostPresent, and never resets.
+ * - Cyclic buffer position is realized as mNextIndex % bufferSize.
+ * - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is called,
+ * the required start index is passed as an arg, and we're able to calculate the required
+ * length. If this length is bigger than bufferSize, it means data was overwritten.
+ * otherwise, we can return the entire sequence.
+ * - To determine if we need to pause, mLatestStartIndex is updated to mNextIndex
+ * on each call to StartFrameTimeRecording. If this index gets overwritten,
+ * it means that all earlier start indices obtained via StartFrameTimeRecording
+ * were also overwritten, hence, no point in recording, so pause.
+ * - mCurrentRunStartIndex indicates the oldest index of the recording after which
+ * the recording was not paused. If StopFrameTimeRecording is invoked with a start index
+ * older than this, it means that some frames were not recorded, so data is invalid.
+ */
+uint32_t
+LayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+ if (mRecording.mIsPaused) {
+ mRecording.mIsPaused = false;
+
+ if (!mRecording.mIntervals.Length()) { // Initialize recording buffers
+ mRecording.mIntervals.SetLength(aBufferSize);
+ }
+
+ // After being paused, recent values got invalid. Update them to now.
+ mRecording.mLastFrameTime = TimeStamp::Now();
+
+ // Any recording which started before this is invalid, since we were paused.
+ mRecording.mCurrentRunStartIndex = mRecording.mNextIndex;
+ }
+
+ // If we'll overwrite this index, there are no more consumers with aStartIndex
+ // for which we're able to provide the full recording, so no point in keep recording.
+ mRecording.mLatestStartIndex = mRecording.mNextIndex;
+ return mRecording.mNextIndex;
+}
+
+void
+LayerManager::RecordFrame()
+{
+ if (!mRecording.mIsPaused) {
+ TimeStamp now = TimeStamp::Now();
+ uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length();
+ mRecording.mIntervals[i] = static_cast<float>((now - mRecording.mLastFrameTime)
+ .ToMilliseconds());
+ mRecording.mNextIndex++;
+ mRecording.mLastFrameTime = now;
+
+ if (mRecording.mNextIndex > (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) {
+ // We've just overwritten the most recent recording start -> pause.
+ mRecording.mIsPaused = true;
+ }
+ }
+}
+
+void
+LayerManager::PostPresent()
+{
+ if (!mTabSwitchStart.IsNull()) {
+ Telemetry::Accumulate(Telemetry::FX_TAB_SWITCH_TOTAL_MS,
+ uint32_t((TimeStamp::Now() - mTabSwitchStart).ToMilliseconds()));
+ mTabSwitchStart = TimeStamp();
+ }
+}
+
+void
+LayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals)
+{
+ uint32_t bufferSize = mRecording.mIntervals.Length();
+ uint32_t length = mRecording.mNextIndex - aStartIndex;
+ if (mRecording.mIsPaused || length > bufferSize || aStartIndex < mRecording.mCurrentRunStartIndex) {
+ // aStartIndex is too old. Also if aStartIndex was issued before mRecordingNextIndex overflowed (uint32_t)
+ // and stopped after the overflow (would happen once every 828 days of constant 60fps).
+ length = 0;
+ }
+
+ if (!length) {
+ aFrameIntervals.Clear();
+ return; // empty recording, return empty arrays.
+ }
+ // Set length in advance to avoid possibly repeated reallocations
+ aFrameIntervals.SetLength(length);
+
+ uint32_t cyclicPos = aStartIndex % bufferSize;
+ for (uint32_t i = 0; i < length; i++, cyclicPos++) {
+ if (cyclicPos == bufferSize) {
+ cyclicPos = 0;
+ }
+ aFrameIntervals[i] = mRecording.mIntervals[cyclicPos];
+ }
+}
+
+void
+LayerManager::BeginTabSwitch()
+{
+ mTabSwitchStart = TimeStamp::Now();
+}
+
+static void PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite);
+
+#ifdef MOZ_DUMP_PAINTING
+template <typename T>
+void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf)
+{
+ nsCString string(aObj->Name());
+ string.Append('-');
+ string.AppendInt((uint64_t)aObj);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
+ }
+ gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "\";");
+ }
+}
+
+void WriteSnapshotToDumpFile(Layer* aLayer, DataSourceSurface* aSurf)
+{
+ WriteSnapshotToDumpFile_internal(aLayer, aSurf);
+}
+
+void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf)
+{
+ WriteSnapshotToDumpFile_internal(aManager, aSurf);
+}
+
+void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget)
+{
+ RefPtr<SourceSurface> surf = aTarget->Snapshot();
+ RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
+ WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
+}
+#endif
+
+void
+Layer::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml, bool aSorted)
+{
+#ifdef MOZ_DUMP_PAINTING
+ bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsLayerComposite() &&
+ AsLayerComposite()->GetCompositableHost();
+ bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
+ AsShadowableLayer()->GetCompositableClient();
+ nsCString layerId(Name());
+ layerId.Append('-');
+ layerId.AppendInt((uint64_t)this);
+#endif
+ if (aDumpHtml) {
+ aStream << nsPrintfCString("<li><a id=\"%p\" ", this).get();
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpCompositorTexture || dumpClientTexture) {
+ aStream << nsPrintfCString("href=\"javascript:ViewImage('%s')\"", layerId.BeginReading()).get();
+ }
+#endif
+ aStream << ">";
+ }
+ DumpSelf(aStream, aPrefix);
+
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpCompositorTexture) {
+ AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
+ } else if (dumpClientTexture) {
+ if (aDumpHtml) {
+ aStream << nsPrintfCString("<script>array[\"%s\"]=\"", layerId.BeginReading()).get();
+ }
+ AsShadowableLayer()->GetCompositableClient()->Dump(aStream, aPrefix,
+ aDumpHtml, TextureDumpMode::DoNotCompress);
+ if (aDumpHtml) {
+ aStream << "\";</script>";
+ }
+ }
+#endif
+
+ if (aDumpHtml) {
+ aStream << "</a>";
+#ifdef MOZ_DUMP_PAINTING
+ if (dumpClientTexture) {
+ aStream << nsPrintfCString("<br><img id=\"%s\">\n", layerId.BeginReading()).get();
+ }
+#endif
+ }
+
+ if (Layer* mask = GetMaskLayer()) {
+ aStream << nsPrintfCString("%s Mask layer:\n", aPrefix).get();
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mask->Dump(aStream, pfx.get(), aDumpHtml);
+ }
+
+ for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+ aStream << nsPrintfCString("%s Ancestor mask layer %d:\n", aPrefix, uint32_t(i)).get();
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ GetAncestorMaskLayerAt(i)->Dump(aStream, pfx.get(), aDumpHtml);
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
+ const nsCString& str = mExtraDumpInfo[i];
+ aStream << aPrefix << " Info:\n" << str.get();
+ }
+#endif
+
+ if (ContainerLayer* container = AsContainerLayer()) {
+ AutoTArray<Layer*, 12> children;
+ if (aSorted) {
+ container->SortChildrenBy3DZOrder(children);
+ } else {
+ for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
+ children.AppendElement(l);
+ }
+ }
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+
+ for (Layer* child : children) {
+ child->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
+ }
+
+ if (aDumpHtml) {
+ aStream << "</ul>";
+ }
+ }
+
+ if (aDumpHtml) {
+ aStream << "</li>";
+ }
+}
+
+void
+Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix)
+{
+ PrintInfo(aStream, aPrefix);
+ aStream << "\n";
+}
+
+void
+Layer::Dump(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ DumpPacket(aPacket, aParent);
+
+ if (Layer* kid = GetFirstChild()) {
+ kid->Dump(aPacket, this);
+ }
+
+ if (Layer* next = GetNextSibling()) {
+ next->Dump(aPacket, aParent);
+ }
+}
+
+void
+Layer::SetDisplayListLog(const char* log)
+{
+ if (gfxUtils::DumpDisplayList()) {
+ mDisplayListLog = log;
+ }
+}
+
+void
+Layer::GetDisplayListLog(nsCString& log)
+{
+ log.SetLength(0);
+
+ if (gfxUtils::DumpDisplayList()) {
+ // This function returns a plain text string which consists of two things
+ // 1. DisplayList log.
+ // 2. Memory address of this layer.
+ // We know the target layer of each display item by information in #1.
+ // Here is an example of a Text display item line log in #1
+ // Text p=0xa9850c00 f=0x0xaa405b00(.....
+ // f keeps the address of the target client layer of a display item.
+ // For LayerScope, display-item-to-client-layer mapping is not enough since
+ // LayerScope, which lives in the chrome process, knows only composite layers.
+ // As so, we need display-item-to-client-layer-to-layer-composite
+ // mapping. That's the reason we insert #2 into the log
+ log.AppendPrintf("0x%p\n%s",(void*) this, mDisplayListLog.get());
+ }
+}
+
+void
+Layer::Log(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ LogSelf(aPrefix);
+
+ if (Layer* kid = GetFirstChild()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ kid->Log(pfx.get());
+ }
+
+ if (Layer* next = GetNextSibling())
+ next->Log(aPrefix);
+}
+
+void
+Layer::LogSelf(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ std::stringstream ss;
+ PrintInfo(ss, aPrefix);
+ MOZ_LAYERS_LOG(("%s", ss.str().c_str()));
+
+ if (mMaskLayer) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " \\ MaskLayer ";
+ mMaskLayer->LogSelf(pfx.get());
+ }
+}
+
+void
+Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this).get();
+
+ layers::PrintInfo(aStream, AsLayerComposite());
+
+ if (mClipRect) {
+ AppendToString(aStream, *mClipRect, " [clip=", "]");
+ }
+ if (mScrolledClip) {
+ AppendToString(aStream, mScrolledClip->GetClipRect(), " [scrolled-clip=", "]");
+ }
+ if (1.0 != mPostXScale || 1.0 != mPostYScale) {
+ aStream << nsPrintfCString(" [postScale=%g, %g]", mPostXScale, mPostYScale).get();
+ }
+ if (!mTransform.IsIdentity()) {
+ AppendToString(aStream, mTransform, " [transform=", "]");
+ }
+ if (!GetEffectiveTransform().IsIdentity()) {
+ AppendToString(aStream, GetEffectiveTransform(), " [effective-transform=", "]");
+ }
+ if (mTransformIsPerspective) {
+ aStream << " [perspective]";
+ }
+ if (!mLayerBounds.IsEmpty()) {
+ AppendToString(aStream, mLayerBounds, " [bounds=", "]");
+ }
+ if (!mVisibleRegion.IsEmpty()) {
+ AppendToString(aStream, mVisibleRegion.ToUnknownRegion(), " [visible=", "]");
+ } else {
+ aStream << " [not visible]";
+ }
+ if (!mEventRegions.IsEmpty()) {
+ AppendToString(aStream, mEventRegions, " ", "");
+ }
+ if (1.0 != mOpacity) {
+ aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get();
+ }
+ if (IsOpaque()) {
+ aStream << " [opaqueContent]";
+ }
+ if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
+ aStream << " [componentAlpha]";
+ }
+ if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ aStream << " [backfaceHidden]";
+ }
+ if (Extend3DContext()) {
+ aStream << " [extend3DContext]";
+ }
+ if (Combines3DTransformWithAncestors()) {
+ aStream << " [combines3DTransformWithAncestors]";
+ }
+ if (Is3DContextLeaf()) {
+ aStream << " [is3DContextLeaf]";
+ }
+ if (IsScrollbarContainer()) {
+ aStream << " [scrollbar]";
+ }
+ if (GetScrollbarDirection() == VERTICAL) {
+ aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
+ }
+ if (GetScrollbarDirection() == HORIZONTAL) {
+ aStream << nsPrintfCString(" [hscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
+ }
+ if (GetIsFixedPosition()) {
+ LayerPoint anchor = GetFixedPositionAnchor();
+ aStream << nsPrintfCString(" [isFixedPosition scrollId=%lld sides=0x%x anchor=%s]",
+ GetFixedPositionScrollContainerId(),
+ GetFixedPositionSides(),
+ ToString(anchor).c_str()).get();
+ }
+ if (GetIsStickyPosition()) {
+ aStream << nsPrintfCString(" [isStickyPosition scrollId=%d outer=(%.3f,%.3f)-(%.3f,%.3f) "
+ "inner=(%.3f,%.3f)-(%.3f,%.3f)]", mStickyPositionData->mScrollId,
+ mStickyPositionData->mOuter.x, mStickyPositionData->mOuter.y,
+ mStickyPositionData->mOuter.XMost(), mStickyPositionData->mOuter.YMost(),
+ mStickyPositionData->mInner.x, mStickyPositionData->mInner.y,
+ mStickyPositionData->mInner.XMost(), mStickyPositionData->mInner.YMost()).get();
+ }
+ if (mMaskLayer) {
+ aStream << nsPrintfCString(" [mMaskLayer=%p]", mMaskLayer.get()).get();
+ }
+ for (uint32_t i = 0; i < mScrollMetadata.Length(); i++) {
+ if (!mScrollMetadata[i].IsDefault()) {
+ aStream << nsPrintfCString(" [metrics%d=", i).get();
+ AppendToString(aStream, mScrollMetadata[i], "", "]");
+ }
+ }
+}
+
+// The static helper function sets the transform matrix into the packet
+static void
+DumpTransform(layerscope::LayersPacket::Layer::Matrix* aLayerMatrix, const Matrix4x4& aMatrix)
+{
+ aLayerMatrix->set_is2d(aMatrix.Is2D());
+ if (aMatrix.Is2D()) {
+ Matrix m = aMatrix.As2D();
+ aLayerMatrix->set_isid(m.IsIdentity());
+ if (!m.IsIdentity()) {
+ aLayerMatrix->add_m(m._11), aLayerMatrix->add_m(m._12);
+ aLayerMatrix->add_m(m._21), aLayerMatrix->add_m(m._22);
+ aLayerMatrix->add_m(m._31), aLayerMatrix->add_m(m._32);
+ }
+ } else {
+ aLayerMatrix->add_m(aMatrix._11), aLayerMatrix->add_m(aMatrix._12);
+ aLayerMatrix->add_m(aMatrix._13), aLayerMatrix->add_m(aMatrix._14);
+ aLayerMatrix->add_m(aMatrix._21), aLayerMatrix->add_m(aMatrix._22);
+ aLayerMatrix->add_m(aMatrix._23), aLayerMatrix->add_m(aMatrix._24);
+ aLayerMatrix->add_m(aMatrix._31), aLayerMatrix->add_m(aMatrix._32);
+ aLayerMatrix->add_m(aMatrix._33), aLayerMatrix->add_m(aMatrix._34);
+ aLayerMatrix->add_m(aMatrix._41), aLayerMatrix->add_m(aMatrix._42);
+ aLayerMatrix->add_m(aMatrix._43), aLayerMatrix->add_m(aMatrix._44);
+ }
+}
+
+// The static helper function sets the IntRect into the packet
+template <typename T, typename Sub, typename Point, typename SizeT, typename MarginT>
+static void
+DumpRect(layerscope::LayersPacket::Layer::Rect* aLayerRect,
+ const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect)
+{
+ aLayerRect->set_x(aRect.x);
+ aLayerRect->set_y(aRect.y);
+ aLayerRect->set_w(aRect.width);
+ aLayerRect->set_h(aRect.height);
+}
+
+// The static helper function sets the nsIntRegion into the packet
+static void
+DumpRegion(layerscope::LayersPacket::Layer::Region* aLayerRegion, const nsIntRegion& aRegion)
+{
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ DumpRect(aLayerRegion->add_r(), iter.Get());
+ }
+}
+
+void
+Layer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ // Add a new layer (UnknownLayer)
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->add_layer();
+ // Basic information
+ layer->set_type(LayersPacket::Layer::UnknownLayer);
+ layer->set_ptr(reinterpret_cast<uint64_t>(this));
+ layer->set_parentptr(reinterpret_cast<uint64_t>(aParent));
+ // Shadow
+ if (LayerComposite* lc = AsLayerComposite()) {
+ LayersPacket::Layer::Shadow* s = layer->mutable_shadow();
+ if (const Maybe<ParentLayerIntRect>& clipRect = lc->GetShadowClipRect()) {
+ DumpRect(s->mutable_clip(), *clipRect);
+ }
+ if (!lc->GetShadowBaseTransform().IsIdentity()) {
+ DumpTransform(s->mutable_transform(), lc->GetShadowBaseTransform());
+ }
+ if (!lc->GetShadowVisibleRegion().IsEmpty()) {
+ DumpRegion(s->mutable_vregion(), lc->GetShadowVisibleRegion().ToUnknownRegion());
+ }
+ }
+ // Clip
+ if (mClipRect) {
+ DumpRect(layer->mutable_clip(), *mClipRect);
+ }
+ // Transform
+ if (!mTransform.IsIdentity()) {
+ DumpTransform(layer->mutable_transform(), mTransform);
+ }
+ // Visible region
+ if (!mVisibleRegion.ToUnknownRegion().IsEmpty()) {
+ DumpRegion(layer->mutable_vregion(), mVisibleRegion.ToUnknownRegion());
+ }
+ // EventRegions
+ if (!mEventRegions.IsEmpty()) {
+ const EventRegions &e = mEventRegions;
+ if (!e.mHitRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_hitregion(), e.mHitRegion);
+ }
+ if (!e.mDispatchToContentHitRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_dispatchregion(), e.mDispatchToContentHitRegion);
+ }
+ if (!e.mNoActionRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_noactionregion(), e.mNoActionRegion);
+ }
+ if (!e.mHorizontalPanRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_hpanregion(), e.mHorizontalPanRegion);
+ }
+ if (!e.mVerticalPanRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_vpanregion(), e.mVerticalPanRegion);
+ }
+ }
+ // Opacity
+ layer->set_opacity(mOpacity);
+ // Content opaque
+ layer->set_copaque(static_cast<bool>(GetContentFlags() & CONTENT_OPAQUE));
+ // Component alpha
+ layer->set_calpha(static_cast<bool>(GetContentFlags() & CONTENT_COMPONENT_ALPHA));
+ // Vertical or horizontal bar
+ if (GetScrollbarDirection() != NONE) {
+ layer->set_direct(GetScrollbarDirection() == VERTICAL ?
+ LayersPacket::Layer::VERTICAL :
+ LayersPacket::Layer::HORIZONTAL);
+ layer->set_barid(GetScrollbarTargetContainerId());
+ }
+
+ // Mask layer
+ if (mMaskLayer) {
+ layer->set_mask(reinterpret_cast<uint64_t>(mMaskLayer.get()));
+ }
+
+ // DisplayList log.
+ if (mDisplayListLog.Length() > 0) {
+ layer->set_displaylistloglength(mDisplayListLog.Length());
+ auto compressedData =
+ MakeUnique<char[]>(LZ4::maxCompressedSize(mDisplayListLog.Length()));
+ int compressedSize = LZ4::compress((char*)mDisplayListLog.get(),
+ mDisplayListLog.Length(),
+ compressedData.get());
+ layer->set_displaylistlog(compressedData.get(), compressedSize);
+ }
+}
+
+bool
+Layer::IsBackfaceHidden()
+{
+ if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ Layer* container = AsContainerLayer() ? this : GetParent();
+ if (container) {
+ // The effective transform can include non-preserve-3d parent
+ // transforms, since we don't always require an intermediate.
+ if (container->Extend3DContext() || container->Is3DContextLeaf()) {
+ return container->GetEffectiveTransform().IsBackfaceVisible();
+ }
+ return container->GetBaseTransform().IsBackfaceVisible();
+ }
+ }
+ return false;
+}
+
+UniquePtr<LayerUserData>
+Layer::RemoveUserData(void* aKey)
+{
+ UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+ return d;
+}
+
+void
+PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (!mValidRegion.IsEmpty()) {
+ AppendToString(aStream, mValidRegion, " [valid=", "]");
+ }
+}
+
+void
+PaintedLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::PaintedLayer);
+ if (!mValidRegion.IsEmpty()) {
+ DumpRegion(layer->mutable_valid(), mValidRegion);
+ }
+}
+
+void
+ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (UseIntermediateSurface()) {
+ aStream << " [usesTmpSurf]";
+ }
+ if (1.0 != mPreXScale || 1.0 != mPreYScale) {
+ aStream << nsPrintfCString(" [preScale=%g, %g]", mPreXScale, mPreYScale).get();
+ }
+ if (mScaleToResolution) {
+ aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get();
+ }
+ if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) {
+ aStream << " [force-dtc]";
+ }
+ if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) {
+ aStream << " [force-ehr]";
+ }
+}
+
+void
+ContainerLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ContainerLayer);
+}
+
+void
+ColorLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ AppendToString(aStream, mColor, " [color=", "]");
+ AppendToString(aStream, mBounds, " [bounds=", "]");
+}
+
+void
+ColorLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ColorLayer);
+ layer->set_color(mColor.ToABGR());
+}
+
+CanvasLayer::CanvasLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData)
+ , mPreTransCallback(nullptr)
+ , mPreTransCallbackData(nullptr)
+ , mPostTransCallback(nullptr)
+ , mPostTransCallbackData(nullptr)
+ , mSamplingFilter(gfx::SamplingFilter::GOOD)
+ , mDirty(false)
+{}
+
+CanvasLayer::~CanvasLayer()
+{}
+
+void
+CanvasLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (mSamplingFilter != SamplingFilter::GOOD) {
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+ }
+}
+
+// This help function is used to assign the correct enum value
+// to the packet
+static void
+DumpFilter(layerscope::LayersPacket::Layer* aLayer,
+ const SamplingFilter& aSamplingFilter)
+{
+ using namespace layerscope;
+ switch (aSamplingFilter) {
+ case SamplingFilter::GOOD:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_GOOD);
+ break;
+ case SamplingFilter::LINEAR:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_LINEAR);
+ break;
+ case SamplingFilter::POINT:
+ aLayer->set_filter(LayersPacket::Layer::FILTER_POINT);
+ break;
+ default:
+ // ignore it
+ break;
+ }
+}
+
+void
+CanvasLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::CanvasLayer);
+ DumpFilter(layer, mSamplingFilter);
+}
+
+void
+ImageLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ if (mSamplingFilter != SamplingFilter::GOOD) {
+ AppendToString(aStream, mSamplingFilter, " [filter=", "]");
+ }
+}
+
+void
+ImageLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ImageLayer);
+ DumpFilter(layer, mSamplingFilter);
+}
+
+void
+RefLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ ContainerLayer::PrintInfo(aStream, aPrefix);
+ if (0 != mId) {
+ AppendToString(aStream, mId, " [id=", "]");
+ }
+}
+
+void
+RefLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::RefLayer);
+ layer->set_refid(mId);
+}
+
+void
+ReadbackLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ Layer::PrintInfo(aStream, aPrefix);
+ AppendToString(aStream, mSize, " [size=", "]");
+ if (mBackgroundLayer) {
+ AppendToString(aStream, mBackgroundLayer, " [backgroundLayer=", "]");
+ AppendToString(aStream, mBackgroundLayerOffset, " [backgroundOffset=", "]");
+ } else if (mBackgroundColor.a == 1.f) {
+ AppendToString(aStream, mBackgroundColor, " [backgroundColor=", "]");
+ } else {
+ aStream << " [nobackground]";
+ }
+}
+
+void
+ReadbackLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+ Layer::DumpPacket(aPacket, aParent);
+ // Get this layer data
+ using namespace layerscope;
+ LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+ layer->set_type(LayersPacket::Layer::ReadbackLayer);
+ LayersPacket::Layer::Size* size = layer->mutable_size();
+ size->set_w(mSize.width);
+ size->set_h(mSize.height);
+}
+
+//--------------------------------------------------
+// LayerManager
+
+void
+LayerManager::Dump(std::stringstream& aStream, const char* aPrefix,
+ bool aDumpHtml, bool aSorted)
+{
+#ifdef MOZ_DUMP_PAINTING
+ if (aDumpHtml) {
+ aStream << "<ul><li>";
+ }
+#endif
+ DumpSelf(aStream, aPrefix, aSorted);
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (!GetRoot()) {
+ aStream << nsPrintfCString("%s(null)", pfx.get()).get();
+ if (aDumpHtml) {
+ aStream << "</li></ul>";
+ }
+ return;
+ }
+
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+ GetRoot()->Dump(aStream, pfx.get(), aDumpHtml);
+ if (aDumpHtml) {
+ aStream << "</ul></li></ul>";
+ }
+ aStream << "\n";
+}
+
+void
+LayerManager::DumpSelf(std::stringstream& aStream, const char* aPrefix, bool aSorted)
+{
+ PrintInfo(aStream, aPrefix);
+ aStream << " --- in " << (aSorted ? "3D-sorted rendering order" : "content order");
+ aStream << "\n";
+}
+
+void
+LayerManager::Dump(bool aSorted)
+{
+ std::stringstream ss;
+ Dump(ss, "", false, aSorted);
+ print_stderr(ss);
+}
+
+void
+LayerManager::Dump(layerscope::LayersPacket* aPacket)
+{
+ DumpPacket(aPacket);
+
+ if (GetRoot()) {
+ GetRoot()->Dump(aPacket, this);
+ }
+}
+
+void
+LayerManager::Log(const char* aPrefix)
+{
+ if (!IsLogEnabled())
+ return;
+
+ LogSelf(aPrefix);
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ if (!GetRoot()) {
+ MOZ_LAYERS_LOG(("%s(null)", pfx.get()));
+ return;
+ }
+
+ GetRoot()->Log(pfx.get());
+}
+
+void
+LayerManager::LogSelf(const char* aPrefix)
+{
+ nsAutoCString str;
+ std::stringstream ss;
+ PrintInfo(ss, aPrefix);
+ MOZ_LAYERS_LOG(("%s", ss.str().c_str()));
+}
+
+void
+LayerManager::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix << nsPrintfCString("%sLayerManager (0x%p)", Name(), this).get();
+}
+
+void
+LayerManager::DumpPacket(layerscope::LayersPacket* aPacket)
+{
+ using namespace layerscope;
+ // Add a new layer data (LayerManager)
+ LayersPacket::Layer* layer = aPacket->add_layer();
+ layer->set_type(LayersPacket::Layer::LayerManager);
+ layer->set_ptr(reinterpret_cast<uint64_t>(this));
+ // Layer Tree Root
+ layer->set_parentptr(0);
+}
+
+/*static*/ bool
+LayerManager::IsLogEnabled()
+{
+ return MOZ_LOG_TEST(GetLog(), LogLevel::Debug);
+}
+
+void
+LayerManager::SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo)
+{
+ mPendingScrollUpdates[aScrollId] = aUpdateInfo;
+}
+
+Maybe<ScrollUpdateInfo>
+LayerManager::GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId)
+{
+ auto it = mPendingScrollUpdates.find(aScrollId);
+ if (it != mPendingScrollUpdates.end()) {
+ return Some(it->second);
+ }
+ return Nothing();
+}
+
+void
+LayerManager::ClearPendingScrollInfoUpdate()
+{
+ mPendingScrollUpdates.clear();
+}
+
+void
+PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite)
+{
+ if (!aLayerComposite) {
+ return;
+ }
+ if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
+ AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
+ }
+ if (!aLayerComposite->GetShadowBaseTransform().IsIdentity()) {
+ AppendToString(aStream, aLayerComposite->GetShadowBaseTransform(), " [shadow-transform=", "]");
+ }
+ if (!aLayerComposite->GetShadowVisibleRegion().IsEmpty()) {
+ AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion().ToUnknownRegion(), " [shadow-visible=", "]");
+ }
+}
+
+void
+SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget)
+{
+ bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA);
+ if (aTarget->IsCurrentGroupOpaque()) {
+ aTarget->SetPermitSubpixelAA(permitSubpixelAA);
+ return;
+ }
+
+ const IntRect& bounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
+ gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y),
+ Float(bounds.width), Float(bounds.height)));
+ transformedBounds.RoundOut();
+ IntRect intTransformedBounds;
+ transformedBounds.ToIntRect(&intTransformedBounds);
+ permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
+ aTarget->GetOpaqueRect().Contains(intTransformedBounds);
+ aTarget->SetPermitSubpixelAA(permitSubpixelAA);
+}
+
+IntRect
+ToOutsideIntRect(const gfxRect &aRect)
+{
+ return IntRect::RoundOut(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h
new file mode 100644
index 000000000..805d41d48
--- /dev/null
+++ b/gfx/layers/Layers.h
@@ -0,0 +1,2619 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_H
+#define GFX_LAYERS_H
+
+#include <map>
+#include <stdint.h> // for uint32_t, uint64_t, uint8_t
+#include <stdio.h> // for FILE
+#include <sys/types.h> // for int32_t, int64_t
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect
+#include "gfxContext.h"
+#include "gfxTypes.h"
+#include "gfxPoint.h" // for gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/EventForwards.h" // for nsPaintEvent
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/Poison.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/BaseMargin.h" // for BaseMargin
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/TiledRegion.h" // for TiledIntRegion
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/gfx/UserData.h" // for UserData, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAutoPtr.h" // for nsAutoPtr, nsRefPtr, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsCSSPropertyID.h" // for nsCSSPropertyID
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsString.h" // for nsCString
+#include "nsTArray.h" // for nsTArray
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nscore.h" // for nsACString, nsAString
+#include "mozilla/Logging.h" // for PRLogModuleInfo
+#include "nsIWidget.h" // For plugin window configuration information structs
+#include "ImageContainer.h"
+
+class gfxContext;
+
+extern uint8_t gLayerManagerLayerBuilder;
+
+namespace mozilla {
+
+class ComputedTimingFunction;
+class FrameLayerBuilder;
+class StyleAnimationValue;
+
+namespace gl {
+class GLContext;
+} // namespace gl
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace dom {
+class OverfillCallback;
+} // namespace dom
+
+namespace layers {
+
+class Animation;
+class AnimationData;
+class AsyncCanvasRenderer;
+class AsyncPanZoomController;
+class BasicLayerManager;
+class ClientLayerManager;
+class Layer;
+class LayerMetricsWrapper;
+class PaintedLayer;
+class ContainerLayer;
+class ImageLayer;
+class ColorLayer;
+class CanvasLayer;
+class ReadbackLayer;
+class ReadbackProcessor;
+class RefLayer;
+class LayerComposite;
+class ShadowableLayer;
+class ShadowLayerForwarder;
+class LayerManagerComposite;
+class SpecificLayerAttributes;
+class Compositor;
+class FrameUniformityData;
+class PersistentBufferProvider;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+#define MOZ_LAYER_DECL_NAME(n, e) \
+ virtual const char* Name() const override { return n; } \
+ virtual LayerType GetType() const override { return e; }
+
+// Defined in LayerUserData.h; please include that file instead.
+class LayerUserData;
+
+/*
+ * Motivation: For truly smooth animation and video playback, we need to
+ * be able to compose frames and render them on a dedicated thread (i.e.
+ * off the main thread where DOM manipulation, script execution and layout
+ * induce difficult-to-bound latency). This requires Gecko to construct
+ * some kind of persistent scene structure (graph or tree) that can be
+ * safely transmitted across threads. We have other scenarios (e.g. mobile
+ * browsing) where retaining some rendered data between paints is desired
+ * for performance, so again we need a retained scene structure.
+ *
+ * Our retained scene structure is a layer tree. Each layer represents
+ * content which can be composited onto a destination surface; the root
+ * layer is usually composited into a window, and non-root layers are
+ * composited into their parent layers. Layers have attributes (e.g.
+ * opacity and clipping) that influence their compositing.
+ *
+ * We want to support a variety of layer implementations, including
+ * a simple "immediate mode" implementation that doesn't retain any
+ * rendered data between paints (i.e. uses cairo in just the way that
+ * Gecko used it before layers were introduced). But we also don't want
+ * to have bifurcated "layers"/"non-layers" rendering paths in Gecko.
+ * Therefore the layers API is carefully designed to permit maximally
+ * efficient implementation in an "immediate mode" style. See the
+ * BasicLayerManager for such an implementation.
+ */
+
+/**
+ * A LayerManager controls a tree of layers. All layers in the tree
+ * must use the same LayerManager.
+ *
+ * All modifications to a layer tree must happen inside a transaction.
+ * Only the state of the layer tree at the end of a transaction is
+ * rendered. Transactions cannot be nested
+ *
+ * Each transaction has two phases:
+ * 1) Construction: layers are created, inserted, removed and have
+ * properties set on them in this phase.
+ * BeginTransaction and BeginTransactionWithTarget start a transaction in
+ * the Construction phase.
+ * 2) Drawing: PaintedLayers are rendered into in this phase, in tree
+ * order. When the client has finished drawing into the PaintedLayers, it should
+ * call EndTransaction to complete the transaction.
+ *
+ * All layer API calls happen on the main thread.
+ *
+ * Layers are refcounted. The layer manager holds a reference to the
+ * root layer, and each container layer holds a reference to its children.
+ */
+class LayerManager {
+ NS_INLINE_DECL_REFCOUNTING(LayerManager)
+
+protected:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+public:
+ LayerManager()
+ : mDestroyed(false)
+ , mSnapEffectiveTransforms(true)
+ , mId(0)
+ , mInTransaction(false)
+ , mPaintedPixelCount(0)
+ {}
+
+ /**
+ * Release layers and resources held by this layer manager, and mark
+ * it as destroyed. Should do any cleanup necessary in preparation
+ * for its widget going away. After this call, only user data calls
+ * are valid on the layer manager.
+ */
+ virtual void Destroy()
+ {
+ mDestroyed = true;
+ mUserData.Destroy();
+ mRoot = nullptr;
+ }
+ bool IsDestroyed() { return mDestroyed; }
+
+ virtual ShadowLayerForwarder* AsShadowForwarder()
+ { return nullptr; }
+
+ virtual LayerManagerComposite* AsLayerManagerComposite()
+ { return nullptr; }
+
+ virtual ClientLayerManager* AsClientLayerManager()
+ { return nullptr; }
+
+ virtual BasicLayerManager* AsBasicLayerManager()
+ { return nullptr; }
+
+ /**
+ * Returns true if this LayerManager is owned by an nsIWidget,
+ * and is used for drawing into the widget.
+ */
+ virtual bool IsWidgetLayerManager() { return true; }
+ virtual bool IsInactiveLayerManager() { return false; }
+
+ /**
+ * Start a new transaction. Nested transactions are not allowed so
+ * there must be no transaction currently in progress.
+ * This transaction will update the state of the window from which
+ * this LayerManager was obtained.
+ */
+ virtual bool BeginTransaction() = 0;
+ /**
+ * Start a new transaction. Nested transactions are not allowed so
+ * there must be no transaction currently in progress.
+ * This transaction will render the contents of the layer tree to
+ * the given target context. The rendering will be complete when
+ * EndTransaction returns.
+ */
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) = 0;
+
+ enum EndTransactionFlags {
+ END_DEFAULT = 0,
+ END_NO_IMMEDIATE_REDRAW = 1 << 0, // Do not perform the drawing phase
+ END_NO_COMPOSITE = 1 << 1, // Do not composite after drawing painted layer contents.
+ END_NO_REMOTE_COMPOSITE = 1 << 2 // Do not schedule a composition with a remote Compositor, if one exists.
+ };
+
+ FrameLayerBuilder* GetLayerBuilder() {
+ return reinterpret_cast<FrameLayerBuilder*>(GetUserData(&gLayerManagerLayerBuilder));
+ }
+
+ /**
+ * Attempts to end an "empty transaction". There must have been no
+ * changes to the layer tree since the BeginTransaction().
+ * It's possible for this to fail; PaintedLayers may need to be updated
+ * due to VRAM data being lost, for example. In such cases this method
+ * returns false, and the caller must proceed with a normal layer tree
+ * update and EndTransaction.
+ */
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) = 0;
+
+ /**
+ * Function called to draw the contents of each PaintedLayer.
+ * aRegionToDraw contains the region that needs to be drawn.
+ * This would normally be a subregion of the visible region.
+ * The callee must draw all of aRegionToDraw. Drawing outside
+ * aRegionToDraw will be clipped out or ignored.
+ * The callee must draw all of aRegionToDraw.
+ * This region is relative to 0,0 in the PaintedLayer.
+ *
+ * aDirtyRegion should contain the total region that is be due to be painted
+ * during the transaction, even though only aRegionToDraw should be drawn
+ * during this call. aRegionToDraw must be entirely contained within
+ * aDirtyRegion. If the total dirty region is unknown it is okay to pass a
+ * subregion of the total dirty region, e.g. just aRegionToDraw, though it
+ * may not be as efficient.
+ *
+ * aRegionToInvalidate contains a region whose contents have been
+ * changed by the layer manager and which must therefore be invalidated.
+ * For example, this could be non-empty if a retained layer internally
+ * switches from RGBA to RGB or back ... we might want to repaint it to
+ * consistently use subpixel-AA or not.
+ * This region is relative to 0,0 in the PaintedLayer.
+ * aRegionToInvalidate may contain areas that are outside
+ * aRegionToDraw; the callee must ensure that these areas are repainted
+ * in the current layer manager transaction or in a later layer
+ * manager transaction.
+ *
+ * aContext must not be used after the call has returned.
+ * We guarantee that buffered contents in the visible
+ * region are valid once drawing is complete.
+ *
+ * The origin of aContext is 0,0 in the PaintedLayer.
+ */
+ typedef void (* DrawPaintedLayerCallback)(PaintedLayer* aLayer,
+ gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aDirtyRegion,
+ DrawRegionClip aClip,
+ const nsIntRegion& aRegionToInvalidate,
+ void* aCallbackData);
+
+ /**
+ * Finish the construction phase of the transaction, perform the
+ * drawing phase, and end the transaction.
+ * During the drawing phase, all PaintedLayers in the tree are
+ * drawn in tree order, exactly once each, except for those layers
+ * where it is known that the visible region is empty.
+ */
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) = 0;
+
+ /**
+ * Schedule a composition with the remote Compositor, if one exists
+ * for this LayerManager. Useful in conjunction with the END_NO_REMOTE_COMPOSITE
+ * flag to EndTransaction.
+ */
+ virtual void Composite() {}
+
+ virtual bool HasShadowManagerInternal() const { return false; }
+ bool HasShadowManager() const { return HasShadowManagerInternal(); }
+ virtual void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations) {}
+ bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
+
+
+ /**
+ * Returns true if the layer manager can't render component alpha
+ * layers, and layer building should do it's best to avoid
+ * creating them.
+ */
+ virtual bool ShouldAvoidComponentAlphaLayers() { return false; }
+
+ /**
+ * Returns true if this LayerManager can properly support layers with
+ * SurfaceMode::SURFACE_COMPONENT_ALPHA. LayerManagers that can't will use
+ * transparent surfaces (and lose subpixel-AA for text).
+ */
+ virtual bool AreComponentAlphaLayersEnabled();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the root layer. The root layer is initially null. If there is
+ * no root layer, EndTransaction won't draw anything.
+ */
+ virtual void SetRoot(Layer* aLayer) = 0;
+ /**
+ * Can be called anytime
+ */
+ Layer* GetRoot() { return mRoot; }
+
+ /**
+ * Does a breadth-first search from the root layer to find the first
+ * scrollable layer, and returns its ViewID. Note that there may be
+ * other layers in the tree which share the same ViewID.
+ * Can be called any time.
+ */
+ FrameMetrics::ViewID GetRootScrollableLayerId();
+
+ /**
+ * Returns a LayerMetricsWrapper containing the Root
+ * Content Documents layer.
+ */
+ LayerMetricsWrapper GetRootContentLayer();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Called when a managee has mutated.
+ * Subclasses overriding this method must first call their
+ * superclass's impl
+ */
+#ifdef DEBUG
+ // In debug builds, we check some properties of |aLayer|.
+ virtual void Mutated(Layer* aLayer);
+#else
+ virtual void Mutated(Layer* aLayer) { }
+#endif
+
+ /**
+ * Hints that can be used during PaintedLayer creation to influence the type
+ * or properties of the layer created.
+ *
+ * NONE: No hint.
+ * SCROLLABLE: This layer may represent scrollable content.
+ */
+ enum PaintedLayerCreationHint {
+ NONE, SCROLLABLE
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a PaintedLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a PaintedLayer for this manager's layer tree, with a creation hint
+ * parameter to help optimise the type of layer created.
+ */
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint) {
+ return CreatePaintedLayer();
+ }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ContainerLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create an ImageLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ColorLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a CanvasLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a ReadbackLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nullptr; }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Create a RefLayer for this manager's layer tree.
+ */
+ virtual already_AddRefed<RefLayer> CreateRefLayer() { return nullptr; }
+
+
+ /**
+ * Can be called anytime, from any thread.
+ *
+ * Creates an Image container which forwards its images to the compositor within
+ * layer transactions on the main thread or asynchronously using the ImageBridge IPDL protocol.
+ * In the case of asynchronous, If the protocol is not available, the returned ImageContainer
+ * will forward images within layer transactions.
+ */
+ static already_AddRefed<ImageContainer> CreateImageContainer(ImageContainer::Mode flag
+ = ImageContainer::SYNCHRONOUS);
+
+ /**
+ * Type of layer manager his is. This is to be used sparsely in order to
+ * avoid a lot of Layers backend specific code. It should be used only when
+ * Layers backend specific functionality is necessary.
+ */
+ virtual LayersBackend GetBackendType() = 0;
+
+ /**
+ * Type of layers backend that will be used to composite this layer tree.
+ * When compositing is done remotely, then this returns the layers type
+ * of the compositor.
+ */
+ virtual LayersBackend GetCompositorBackendType() { return GetBackendType(); }
+
+ /**
+ * Creates a DrawTarget which is optimized for inter-operating with this
+ * layer manager.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalDrawTarget(const IntSize &aSize,
+ SurfaceFormat imageFormat);
+
+ /**
+ * Creates a DrawTarget for alpha masks which is optimized for inter-
+ * operating with this layer manager. In contrast to CreateOptimalDrawTarget,
+ * this surface is optimised for drawing alpha only and we assume that
+ * drawing the mask is fairly simple.
+ */
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalMaskDrawTarget(const IntSize &aSize);
+
+ /**
+ * Creates a DrawTarget for use with canvas which is optimized for
+ * inter-operating with this layermanager.
+ */
+ virtual already_AddRefed<mozilla::gfx::DrawTarget>
+ CreateDrawTarget(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat);
+
+ /**
+ * Creates a PersistentBufferProvider for use with canvas which is optimized for
+ * inter-operating with this layermanager.
+ */
+ virtual already_AddRefed<PersistentBufferProvider>
+ CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
+ mozilla::gfx::SurfaceFormat aFormat);
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) { return true; }
+
+ /**
+ * returns the maximum texture size on this layer backend, or INT32_MAX
+ * if there is no maximum
+ */
+ virtual int32_t GetMaxTextureSize() const = 0;
+
+ /**
+ * Return the name of the layer manager's backend.
+ */
+ virtual void GetBackendName(nsAString& aName) = 0;
+
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ {
+ mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerUserDataDestroy);
+ }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ UniquePtr<LayerUserData> RemoveUserData(void* aKey);
+
+ /**
+ * This getter can be used anytime.
+ */
+ bool HasUserData(void* aKey)
+ {
+ return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
+ }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey) const
+ {
+ return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey)));
+ }
+
+ /**
+ * Must be called outside of a layers transaction.
+ *
+ * For the subtree rooted at |aSubtree|, this attempts to free up
+ * any free-able resources like retained buffers, but may do nothing
+ * at all. After this call, the layer tree is left in an undefined
+ * state; the layers in |aSubtree|'s subtree may no longer have
+ * buffers with valid content and may no longer be able to draw
+ * their visible and valid regions.
+ *
+ * In general, a painting or forwarding transaction on |this| must
+ * complete on the tree before it returns to a valid state.
+ *
+ * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree|
+ * is null. |aSubtree|'s manager must be this.
+ */
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) {}
+
+ /**
+ * Flag the next paint as the first for a document.
+ */
+ virtual void SetIsFirstPaint() {}
+
+ /**
+ * Make sure that the previous transaction has been entirely
+ * completed.
+ *
+ * Note: This may sychronously wait on a remote compositor
+ * to complete rendering.
+ */
+ virtual void FlushRendering() { }
+
+ /**
+ * Checks if we need to invalidate the OS widget to trigger
+ * painting when updating this layer manager.
+ */
+ virtual bool NeedsWidgetInvalidation() { return true; }
+
+ virtual const char* Name() const { return "???"; }
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * aStream.
+ */
+ void Dump(std::stringstream& aStream, const char* aPrefix="",
+ bool aDumpHtml=false, bool aSorted=false);
+ /**
+ * Dump information about just this layer manager itself to aStream
+ */
+ void DumpSelf(std::stringstream& aStream, const char* aPrefix="", bool aSorted=false);
+ void Dump(bool aSorted=false);
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * layerscope packet.
+ */
+ void Dump(layerscope::LayersPacket* aPacket);
+
+ /**
+ * Log information about this layer manager and its managed tree to
+ * the NSPR log (if enabled for "Layers").
+ */
+ void Log(const char* aPrefix="");
+ /**
+ * Log information about just this layer manager itself to the NSPR
+ * log (if enabled for "Layers").
+ */
+ void LogSelf(const char* aPrefix="");
+
+ /**
+ * Record (and return) frame-intervals and paint-times for frames which were presented
+ * between calling StartFrameTimeRecording and StopFrameTimeRecording.
+ *
+ * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
+ * (elements were overwritten since Start), result is considered invalid and hence empty.
+ * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
+ * Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
+ * - Note: the first frame-interval may be longer than expected because last frame
+ * might have been presented some time before calling StartFrameTimeRecording.
+ */
+
+ /**
+ * Returns a handle which represents current recording start position.
+ */
+ virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize);
+
+ /**
+ * Clears, then populates aFrameIntervals with the recorded frame timing
+ * data. The array will be empty if data was overwritten since
+ * aStartIndex was obtained.
+ */
+ virtual void StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals);
+
+ void RecordFrame();
+ void PostPresent();
+
+ void BeginTabSwitch();
+
+ static bool IsLogEnabled();
+ static mozilla::LogModule* GetLog();
+
+ bool IsCompositingCheap(LayersBackend aBackend)
+ {
+ // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to
+ // avoid loading the compositor!
+ return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend;
+ }
+
+ virtual bool IsCompositingCheap() { return true; }
+
+ bool IsInTransaction() const { return mInTransaction; }
+ virtual void GetFrameUniformity(FrameUniformityData* aOutData) { }
+ virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) { return true; }
+ virtual void RunOverfillCallback(const uint32_t aOverfill) { }
+
+ virtual void SetRegionToClear(const nsIntRegion& aRegion)
+ {
+ mRegionToClear = aRegion;
+ }
+
+ virtual float RequestProperty(const nsAString& property) { return -1; }
+
+ const TimeStamp& GetAnimationReadyTime() const {
+ return mAnimationReadyTime;
+ }
+
+ virtual bool AsyncPanZoomEnabled() const {
+ return false;
+ }
+
+ static void LayerUserDataDestroy(void* data);
+
+ void AddPaintedPixelCount(int32_t aCount) {
+ mPaintedPixelCount += aCount;
+ }
+
+ uint32_t GetAndClearPaintedPixelCount() {
+ uint32_t count = mPaintedPixelCount;
+ mPaintedPixelCount = 0;
+ return count;
+ }
+
+protected:
+ RefPtr<Layer> mRoot;
+ gfx::UserData mUserData;
+ bool mDestroyed;
+ bool mSnapEffectiveTransforms;
+
+ nsIntRegion mRegionToClear;
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~LayerManager() {}
+
+ // Print interesting information about this into aStreamo. Internally
+ // used to implement Dump*() and Log*().
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ // Print interesting information about this into layerscope packet.
+ // Internally used to implement Dump().
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket);
+
+ uint64_t mId;
+ bool mInTransaction;
+ // The time when painting most recently finished. This is recorded so that
+ // we can time any play-pending animations from this point.
+ TimeStamp mAnimationReadyTime;
+ // The count of pixels that were painted in the current transaction.
+ uint32_t mPaintedPixelCount;
+private:
+ struct FramesTimingRecording
+ {
+ // Stores state and data for frame intervals and paint times recording.
+ // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details.
+ FramesTimingRecording()
+ : mNextIndex(0)
+ , mLatestStartIndex(0)
+ , mCurrentRunStartIndex(0)
+ , mIsPaused(true)
+ {}
+ nsTArray<float> mIntervals;
+ TimeStamp mLastFrameTime;
+ uint32_t mNextIndex;
+ uint32_t mLatestStartIndex;
+ uint32_t mCurrentRunStartIndex;
+ bool mIsPaused;
+ };
+ FramesTimingRecording mRecording;
+
+ TimeStamp mTabSwitchStart;
+
+public:
+ /*
+ * Methods to store/get/clear a "pending scroll info update" object on a
+ * per-scrollid basis. This is used for empty transactions that push over
+ * scroll position updates to the APZ code.
+ */
+ void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
+ const ScrollUpdateInfo& aUpdateInfo);
+ Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId);
+ void ClearPendingScrollInfoUpdate();
+private:
+ std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates;
+};
+
+typedef InfallibleTArray<Animation> AnimationArray;
+
+struct AnimData {
+ InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
+ InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
+ InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
+};
+
+/**
+ * A Layer represents anything that can be rendered onto a destination
+ * surface.
+ */
+class Layer {
+ NS_INLINE_DECL_REFCOUNTING(Layer)
+
+public:
+ // Keep these in alphabetical order
+ enum LayerType {
+ TYPE_CANVAS,
+ TYPE_COLOR,
+ TYPE_CONTAINER,
+ TYPE_IMAGE,
+ TYPE_READBACK,
+ TYPE_REF,
+ TYPE_SHADOW,
+ TYPE_PAINTED
+ };
+
+ /**
+ * Returns the LayerManager this Layer belongs to. Note that the layer
+ * manager might be in a destroyed state, at which point it's only
+ * valid to set/get user data from it.
+ */
+ LayerManager* Manager() { return mManager; }
+
+ enum {
+ /**
+ * If this is set, the caller is promising that by the end of this
+ * transaction the entire visible region (as specified by
+ * SetVisibleRegion) will be filled with opaque content.
+ */
+ CONTENT_OPAQUE = 0x01,
+ /**
+ * If this is set, the caller is notifying that the contents of this layer
+ * require per-component alpha for optimal fidelity. However, there is no
+ * guarantee that component alpha will be supported for this layer at
+ * paint time.
+ * This should never be set at the same time as CONTENT_OPAQUE.
+ */
+ CONTENT_COMPONENT_ALPHA = 0x02,
+
+ /**
+ * If this is set then one of the descendant layers of this one has
+ * CONTENT_COMPONENT_ALPHA set.
+ */
+ CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04,
+
+ /**
+ * If this is set then this layer is part of a preserve-3d group, and should
+ * be sorted with sibling layers that are also part of the same group.
+ */
+ CONTENT_EXTEND_3D_CONTEXT = 0x08,
+ /**
+ * This indicates that the transform may be changed on during an empty
+ * transaction where there is no possibility of redrawing the content, so the
+ * implementation should be ready for that.
+ */
+ CONTENT_MAY_CHANGE_TRANSFORM = 0x10,
+
+ /**
+ * Disable subpixel AA for this layer. This is used if the display isn't suited
+ * for subpixel AA like hidpi or rotated content.
+ */
+ CONTENT_DISABLE_SUBPIXEL_AA = 0x20,
+
+ /**
+ * If this is set then the layer contains content that may look objectionable
+ * if not handled as an active layer (such as text with an animated transform).
+ * This is for internal layout/FrameLayerBuilder usage only until flattening
+ * code is obsoleted. See bug 633097
+ */
+ CONTENT_DISABLE_FLATTENING = 0x40,
+
+ /**
+ * This layer is hidden if the backface of the layer is visible
+ * to user.
+ */
+ CONTENT_BACKFACE_HIDDEN = 0x80
+ };
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * This lets layout make some promises about what will be drawn into the
+ * visible region of the PaintedLayer. This enables internal quality
+ * and performance optimizations.
+ */
+ void SetContentFlags(uint32_t aFlags)
+ {
+ NS_ASSERTION((aFlags & (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA)) !=
+ (CONTENT_OPAQUE | CONTENT_COMPONENT_ALPHA),
+ "Can't be opaque and require component alpha");
+ if (mContentFlags != aFlags) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ContentFlags", this));
+ mContentFlags = aFlags;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * The union of the bounds of all the display item that got flattened
+ * into this layer. This is intended to be an approximation to the
+ * size of the layer if the nearest scrollable ancestor had an infinitely
+ * large displayport. Computing this more exactly is too expensive,
+ * but this approximation is sufficient for what we need to use it for.
+ */
+ virtual void SetLayerBounds(const gfx::IntRect& aLayerBounds)
+ {
+ if (!mLayerBounds.IsEqualEdges(aLayerBounds)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) LayerBounds", this));
+ mLayerBounds = aLayerBounds;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer which region will be visible. The visible region
+ * is a region which contains all the contents of the layer that can
+ * actually affect the rendering of the window. It can exclude areas
+ * that are covered by opaque contents of other layers, and it can
+ * exclude areas where this layer simply contains no content at all.
+ * (This can be an overapproximation to the "true" visible region.)
+ *
+ * There is no general guarantee that drawing outside the bounds of the
+ * visible region will be ignored. So if a layer draws outside the bounds
+ * of its visible region, it needs to ensure that what it draws is valid.
+ */
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ // IsEmpty is required otherwise we get invalidation glitches.
+ // See bug 1288464 for investigating why.
+ if (!mVisibleRegion.IsEqual(aRegion) || aRegion.IsEmpty()) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) VisibleRegion was %s is %s", this,
+ mVisibleRegion.ToString().get(), aRegion.ToString().get()));
+ mVisibleRegion = aRegion;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the (sub)document metrics used to render the Layer subtree
+ * rooted at this. Note that a layer may have multiple FrameMetrics
+ * objects; calling this function will remove all of them and replace
+ * them with the provided FrameMetrics. See the documentation for
+ * SetFrameMetrics(const nsTArray<FrameMetrics>&) for more details.
+ */
+ void SetScrollMetadata(const ScrollMetadata& aScrollMetadata)
+ {
+ Manager()->ClearPendingScrollInfoUpdate();
+ if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
+ mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata);
+ ScrollMetadataChanged();
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the (sub)document metrics used to render the Layer subtree
+ * rooted at this. There might be multiple metrics on this layer
+ * because the layer may, for example, be contained inside multiple
+ * nested scrolling subdocuments. In general a Layer having multiple
+ * ScrollMetadata objects is conceptually equivalent to having a stack
+ * of ContainerLayers that have been flattened into this Layer.
+ * See the documentation in LayerMetricsWrapper.h for a more detailed
+ * explanation of this conceptual equivalence.
+ *
+ * Note also that there is actually a many-to-many relationship between
+ * Layers and ScrollMetadata, because multiple Layers may have identical
+ * ScrollMetadata objects. This happens when those layers belong to the
+ * same scrolling subdocument and therefore end up with the same async
+ * transform when they are scrolled by the APZ code.
+ */
+ void SetScrollMetadata(const nsTArray<ScrollMetadata>& aMetadataArray)
+ {
+ Manager()->ClearPendingScrollInfoUpdate();
+ if (mScrollMetadata != aMetadataArray) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this));
+ mScrollMetadata = aMetadataArray;
+ ScrollMetadataChanged();
+ Mutated();
+ }
+ }
+
+ /*
+ * Compositor event handling
+ * =========================
+ * When a touch-start event (or similar) is sent to the AsyncPanZoomController,
+ * it needs to decide whether the event should be sent to the main thread.
+ * Each layer has a list of event handling regions. When the compositor needs
+ * to determine how to handle a touch event, it scans the layer tree from top
+ * to bottom in z-order (traversing children before their parents). Points
+ * outside the clip region for a layer cause that layer (and its subtree)
+ * to be ignored. If a layer has a mask layer, and that mask layer's alpha
+ * value is zero at the event point, then the layer and its subtree should
+ * be ignored.
+ * For each layer, if the point is outside its hit region, we ignore the layer
+ * and move onto the next. If the point is inside its hit region but
+ * outside the dispatch-to-content region, we can initiate a gesture without
+ * consulting the content thread. Otherwise we must dispatch the event to
+ * content.
+ * Note that if a layer or any ancestor layer has a ForceEmptyHitRegion
+ * override in GetEventRegionsOverride() then the hit-region must be treated
+ * as empty. Similarly, if there is a ForceDispatchToContent override then
+ * the dispatch-to-content region must be treated as encompassing the entire
+ * hit region, and therefore we must consult the content thread before
+ * initiating a gesture. (If both flags are set, ForceEmptyHitRegion takes
+ * priority.)
+ */
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the event handling region.
+ */
+ void SetEventRegions(const EventRegions& aRegions)
+ {
+ if (mEventRegions != aRegions) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) eventregions were %s, now %s", this,
+ mEventRegions.ToString().get(), aRegions.ToString().get()));
+ mEventRegions = aRegions;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the opacity which will be applied to this layer as it
+ * is composited to the destination.
+ */
+ void SetOpacity(float aOpacity)
+ {
+ if (mOpacity != aOpacity) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Opacity", this));
+ mOpacity = aOpacity;
+ Mutated();
+ }
+ }
+
+ void SetMixBlendMode(gfx::CompositionOp aMixBlendMode)
+ {
+ if (mMixBlendMode != aMixBlendMode) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MixBlendMode", this));
+ mMixBlendMode = aMixBlendMode;
+ Mutated();
+ }
+ }
+
+ void SetForceIsolatedGroup(bool aForceIsolatedGroup)
+ {
+ if(mForceIsolatedGroup != aForceIsolatedGroup) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this));
+ mForceIsolatedGroup = aForceIsolatedGroup;
+ Mutated();
+ }
+ }
+
+ bool GetForceIsolatedGroup() const
+ {
+ return mForceIsolatedGroup;
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set a clip rect which will be applied to this layer as it is
+ * composited to the destination. The coordinates are relative to
+ * the parent layer (i.e. the contents of this layer
+ * are transformed before this clip rect is applied).
+ * For the root layer, the coordinates are relative to the widget,
+ * in device pixels.
+ * If aRect is null no clipping will be performed.
+ */
+ void SetClipRect(const Maybe<ParentLayerIntRect>& aRect)
+ {
+ if (mClipRect) {
+ if (!aRect) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is <none>", this,
+ mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height));
+ mClipRect.reset();
+ Mutated();
+ } else {
+ if (!aRect->IsEqualEdges(*mClipRect)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is %d,%d,%d,%d", this,
+ mClipRect->x, mClipRect->y, mClipRect->width, mClipRect->height,
+ aRect->x, aRect->y, aRect->width, aRect->height));
+ mClipRect = aRect;
+ Mutated();
+ }
+ }
+ } else {
+ if (aRect) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was <none> is %d,%d,%d,%d", this,
+ aRect->x, aRect->y, aRect->width, aRect->height));
+ mClipRect = aRect;
+ Mutated();
+ }
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set an optional scrolled clip on the layer.
+ * The scrolled clip, if present, consists of a clip rect and an optional mask.
+ * This scrolled clip is always scrolled by all scroll frames associated with
+ * this layer. (By contrast, the scroll clips stored in ScrollMetadata are
+ * only scrolled by scroll frames above that ScrollMetadata, and the layer's
+ * mClipRect is always fixed to the layer contents (which may or may not be
+ * scrolled by some of the scroll frames associated with the layer, depending
+ * on whether the layer is fixed).)
+ */
+ void SetScrolledClip(const Maybe<LayerClip>& aScrolledClip)
+ {
+ if (mScrolledClip != aScrolledClip) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrolledClip", this));
+ mScrolledClip = aScrolledClip;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set a layer to mask this layer.
+ *
+ * The mask layer should be applied using its effective transform (after it
+ * is calculated by ComputeEffectiveTransformForMaskLayer), this should use
+ * this layer's parent's transform and the mask layer's transform, but not
+ * this layer's. That is, the mask layer is specified relative to this layer's
+ * position in it's parent layer's coord space.
+ * Currently, only 2D translations are supported for the mask layer transform.
+ *
+ * Ownership of aMaskLayer passes to this.
+ * Typical use would be an ImageLayer with an alpha image used for masking.
+ * See also ContainerState::BuildMaskLayer in FrameLayerBuilder.cpp.
+ */
+ void SetMaskLayer(Layer* aMaskLayer)
+ {
+#ifdef DEBUG
+ if (aMaskLayer) {
+ bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+ NS_ASSERTION(maskIs2D, "Mask layer has invalid transform.");
+ }
+#endif
+
+ if (mMaskLayer != aMaskLayer) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MaskLayer", this));
+ mMaskLayer = aMaskLayer;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Add mask layers associated with LayerClips.
+ */
+ void SetAncestorMaskLayers(const nsTArray<RefPtr<Layer>>& aLayers) {
+ if (aLayers != mAncestorMaskLayers) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AncestorMaskLayers", this));
+ mAncestorMaskLayers = aLayers;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Add a mask layer associated with a LayerClip.
+ */
+ void AddAncestorMaskLayer(const RefPtr<Layer>& aLayer) {
+ mAncestorMaskLayers.AppendElement(aLayer);
+ Mutated();
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer what its transform should be. The transformation
+ * is applied when compositing the layer into its parent container.
+ */
+ void SetBaseTransform(const gfx::Matrix4x4& aMatrix)
+ {
+ NS_ASSERTION(!aMatrix.IsSingular(),
+ "Shouldn't be trying to draw with a singular matrix!");
+ mPendingTransform = nullptr;
+ if (mTransform == aMatrix) {
+ return;
+ }
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) BaseTransform", this));
+ mTransform = aMatrix;
+ Mutated();
+ }
+
+ /**
+ * Can be called at any time.
+ *
+ * Like SetBaseTransform(), but can be called before the next
+ * transform (i.e. outside an open transaction). Semantically, this
+ * method enqueues a new transform value to be set immediately after
+ * the next transaction is opened.
+ */
+ void SetBaseTransformForNextTransaction(const gfx::Matrix4x4& aMatrix)
+ {
+ mPendingTransform = new gfx::Matrix4x4(aMatrix);
+ }
+
+ void SetPostScale(float aXScale, float aYScale)
+ {
+ if (mPostXScale == aXScale && mPostYScale == aYScale) {
+ return;
+ }
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PostScale", this));
+ mPostXScale = aXScale;
+ mPostYScale = aYScale;
+ Mutated();
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * A layer is "fixed position" when it draws content from a content
+ * (not chrome) document, the topmost content document has a root scrollframe
+ * with a displayport, but the layer does not move when that displayport scrolls.
+ */
+ void SetIsFixedPosition(bool aFixedPosition)
+ {
+ if (mIsFixedPosition != aFixedPosition) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) IsFixedPosition", this));
+ mIsFixedPosition = aFixedPosition;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * This flag is true when the transform on the layer is a perspective
+ * transform. The compositor treats perspective transforms specially
+ * for async scrolling purposes.
+ */
+ void SetTransformIsPerspective(bool aTransformIsPerspective)
+ {
+ if (mTransformIsPerspective != aTransformIsPerspective) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) TransformIsPerspective", this));
+ mTransformIsPerspective = aTransformIsPerspective;
+ Mutated();
+ }
+ }
+
+ // Call AddAnimation to add a new animation to this layer from layout code.
+ // Caller must fill in all the properties of the returned animation.
+ // A later animation overrides an earlier one.
+ Animation* AddAnimation();
+ // ClearAnimations clears animations on this layer.
+ void ClearAnimations();
+ // This is only called when the layer tree is updated. Do not call this from
+ // layout code. To add an animation to this layer, use AddAnimation.
+ void SetAnimations(const AnimationArray& aAnimations);
+ // Go through all animations in this layer and its children and, for
+ // any animations with a null start time, update their start time such
+ // that at |aReadyTime| the animation's current time corresponds to its
+ // 'initial current time' value.
+ void StartPendingAnimations(const TimeStamp& aReadyTime);
+
+ // These are a parallel to AddAnimation and clearAnimations, except
+ // they add pending animations that apply only when the next
+ // transaction is begun. (See also
+ // SetBaseTransformForNextTransaction.)
+ Animation* AddAnimationForNextTransaction();
+ void ClearAnimationsForNextTransaction();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer represents a fixed position element, this data is stored on the
+ * layer for use by the compositor.
+ *
+ * - |aScrollId| identifies the scroll frame that this element is fixed
+ * with respect to.
+ *
+ * - |aAnchor| is the point on the layer that is considered the "anchor"
+ * point, that is, the point which remains in the same position when
+ * compositing the layer tree with a transformation (such as when
+ * asynchronously scrolling and zooming).
+ *
+ * - |aSides| is the set of sides to which the element is fixed relative to.
+ * This is used if the viewport size is changed in the compositor and
+ * fixed position items need to shift accordingly. This value is made up
+ * combining appropriate values from mozilla::SideBits.
+ */
+ void SetFixedPositionData(FrameMetrics::ViewID aScrollId,
+ const LayerPoint& aAnchor,
+ int32_t aSides)
+ {
+ if (!mFixedPositionData ||
+ mFixedPositionData->mScrollId != aScrollId ||
+ mFixedPositionData->mAnchor != aAnchor ||
+ mFixedPositionData->mSides != aSides) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FixedPositionData", this));
+ if (!mFixedPositionData) {
+ mFixedPositionData = MakeUnique<FixedPositionData>();
+ }
+ mFixedPositionData->mScrollId = aScrollId;
+ mFixedPositionData->mAnchor = aAnchor;
+ mFixedPositionData->mSides = aSides;
+ Mutated();
+ }
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer is "sticky position", |aScrollId| holds the scroll identifier
+ * of the scrollable content that contains it. The difference between the two
+ * rectangles |aOuter| and |aInner| is treated as two intervals in each
+ * dimension, with the current scroll position at the origin. For each
+ * dimension, while that component of the scroll position lies within either
+ * interval, the layer should not move relative to its scrolling container.
+ */
+ void SetStickyPositionData(FrameMetrics::ViewID aScrollId, LayerRect aOuter,
+ LayerRect aInner)
+ {
+ if (!mStickyPositionData ||
+ !mStickyPositionData->mOuter.IsEqualEdges(aOuter) ||
+ !mStickyPositionData->mInner.IsEqualEdges(aInner)) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) StickyPositionData", this));
+ if (!mStickyPositionData) {
+ mStickyPositionData = new StickyPositionData;
+ }
+ mStickyPositionData->mScrollId = aScrollId;
+ mStickyPositionData->mOuter = aOuter;
+ mStickyPositionData->mInner = aInner;
+ Mutated();
+ }
+ }
+
+ enum ScrollDirection {
+ NONE,
+ VERTICAL,
+ HORIZONTAL
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * If a layer is a scrollbar layer, |aScrollId| holds the scroll identifier
+ * of the scrollable content that the scrollbar is for.
+ */
+ void SetScrollbarData(FrameMetrics::ViewID aScrollId, ScrollDirection aDir, float aThumbRatio)
+ {
+ if (mScrollbarTargetId != aScrollId ||
+ mScrollbarDirection != aDir ||
+ mScrollbarThumbRatio != aThumbRatio)
+ {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this));
+ mScrollbarTargetId = aScrollId;
+ mScrollbarDirection = aDir;
+ mScrollbarThumbRatio = aThumbRatio;
+ Mutated();
+ }
+ }
+
+ // Set during construction for the container layer of scrollbar components.
+ void SetIsScrollbarContainer()
+ {
+ if (!mIsScrollbarContainer) {
+ mIsScrollbarContainer = true;
+ Mutated();
+ }
+ }
+
+ // These getters can be used anytime.
+ float GetOpacity() { return mOpacity; }
+ gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; }
+ const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; }
+ const Maybe<LayerClip>& GetScrolledClip() const { return mScrolledClip; }
+ Maybe<ParentLayerIntRect> GetScrolledClipRect() const;
+ uint32_t GetContentFlags() { return mContentFlags; }
+ const gfx::IntRect& GetLayerBounds() const { return mLayerBounds; }
+ const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
+ const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const;
+ const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const;
+ uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); }
+ const nsTArray<ScrollMetadata>& GetAllScrollMetadata() { return mScrollMetadata; }
+ bool HasScrollableFrameMetrics() const;
+ bool IsScrollInfoLayer() const;
+ const EventRegions& GetEventRegions() const { return mEventRegions; }
+ ContainerLayer* GetParent() { return mParent; }
+ Layer* GetNextSibling() {
+ if (mNextSibling) {
+ mNextSibling->CheckCanary();
+ }
+ return mNextSibling;
+ }
+ const Layer* GetNextSibling() const {
+ if (mNextSibling) {
+ mNextSibling->CheckCanary();
+ }
+ return mNextSibling;
+ }
+ Layer* GetPrevSibling() { return mPrevSibling; }
+ const Layer* GetPrevSibling() const { return mPrevSibling; }
+ virtual Layer* GetFirstChild() const { return nullptr; }
+ virtual Layer* GetLastChild() const { return nullptr; }
+ gfx::Matrix4x4 GetTransform() const;
+ // Same as GetTransform(), but returns the transform as a strongly-typed
+ // matrix. Eventually this will replace GetTransform().
+ const CSSTransformMatrix GetTransformTyped() const;
+ const gfx::Matrix4x4& GetBaseTransform() const { return mTransform; }
+ // Note: these are virtual because ContainerLayerComposite overrides them.
+ virtual float GetPostXScale() const { return mPostXScale; }
+ virtual float GetPostYScale() const { return mPostYScale; }
+ bool GetIsFixedPosition() { return mIsFixedPosition; }
+ bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
+ bool GetIsStickyPosition() { return mStickyPositionData; }
+ FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mFixedPositionData ? mFixedPositionData->mScrollId : FrameMetrics::NULL_SCROLL_ID; }
+ LayerPoint GetFixedPositionAnchor() { return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint(); }
+ int32_t GetFixedPositionSides() { return mFixedPositionData ? mFixedPositionData->mSides : eSideBitsNone; }
+ FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; }
+ const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
+ const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
+ FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
+ ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
+ float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; }
+ bool IsScrollbarContainer() { return mIsScrollbarContainer; }
+ Layer* GetMaskLayer() const { return mMaskLayer; }
+ void CheckCanary() const { mCanary.Check(); }
+
+ // Ancestor mask layers are associated with FrameMetrics, but for simplicity
+ // in maintaining the layer tree structure we attach them to the layer.
+ size_t GetAncestorMaskLayerCount() const {
+ return mAncestorMaskLayers.Length();
+ }
+ Layer* GetAncestorMaskLayerAt(size_t aIndex) const {
+ return mAncestorMaskLayers.ElementAt(aIndex);
+ }
+ const nsTArray<RefPtr<Layer>>& GetAllAncestorMaskLayers() const {
+ return mAncestorMaskLayers;
+ }
+
+ bool HasMaskLayers() const {
+ return GetMaskLayer() || mAncestorMaskLayers.Length() > 0;
+ }
+
+ /*
+ * Get the combined clip rect of the Layer clip and all clips on FrameMetrics.
+ * This is intended for use in Layout. The compositor needs to apply async
+ * transforms to find the combined clip.
+ */
+ Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
+
+ /**
+ * Retrieve the root level visible region for |this| taking into account
+ * clipping applied to parent layers of |this| as well as subtracting
+ * visible regions of higher siblings of this layer and each ancestor.
+ *
+ * Note translation values for offsets of visible regions and accumulated
+ * aLayerOffset are integer rounded using IntPoint::Round.
+ *
+ * @param aResult - the resulting visible region of this layer.
+ * @param aLayerOffset - this layer's total offset from the root layer.
+ * @return - false if during layer tree traversal a parent or sibling
+ * transform is found to be non-translational. This method returns early
+ * in this case, results will not be valid. Returns true on successful
+ * traversal.
+ */
+ bool GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
+ nsIntPoint* aLayerOffset);
+
+ // Note that all lengths in animation data are either in CSS pixels or app
+ // units and must be converted to device pixels by the compositor.
+ AnimationArray& GetAnimations() { return mAnimations; }
+ InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; }
+
+ uint64_t GetAnimationGeneration() { return mAnimationGeneration; }
+ void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; }
+
+ bool HasTransformAnimation() const;
+
+ /**
+ * Returns the local transform for this layer: either mTransform or,
+ * for shadow layers, GetShadowBaseTransform(), in either case with the
+ * pre- and post-scales applied.
+ */
+ gfx::Matrix4x4 GetLocalTransform();
+
+ /**
+ * Same as GetLocalTransform(), but returns a strongly-typed matrix.
+ * Eventually, this will replace GetLocalTransform().
+ */
+ const LayerToParentLayerMatrix4x4 GetLocalTransformTyped();
+
+ /**
+ * Returns the local opacity for this layer: either mOpacity or,
+ * for shadow layers, GetShadowOpacity()
+ */
+ float GetLocalOpacity();
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * Apply pending changes to layers before drawing them, if those
+ * pending changes haven't been overridden by later changes.
+ */
+ void ApplyPendingUpdatesToSubtree();
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * Write layer-subtype-specific attributes into aAttrs. Used to
+ * synchronize layer attributes to their shadows'.
+ */
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { }
+
+ // Returns true if it's OK to save the contents of aLayer in an
+ // opaque surface (a surface without an alpha channel).
+ // If we can use a surface without an alpha channel, we should, because
+ // it will often make painting of antialiased text faster and higher
+ // quality.
+ bool CanUseOpaqueSurface();
+
+ SurfaceMode GetSurfaceMode()
+ {
+ if (CanUseOpaqueSurface())
+ return SurfaceMode::SURFACE_OPAQUE;
+ if (mContentFlags & CONTENT_COMPONENT_ALPHA)
+ return SurfaceMode::SURFACE_COMPONENT_ALPHA;
+ return SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
+
+ // Returns true if this layer can be treated as opaque for visibility
+ // computation. A layer may be non-opaque for visibility even if it
+ // is not transparent, for example, if it has a mix-blend-mode.
+ bool IsOpaqueForVisibility();
+
+ /**
+ * This setter can be used anytime. The user data for all keys is
+ * initially null. Ownership pases to the layer manager.
+ */
+ void SetUserData(void* aKey, LayerUserData* aData)
+ {
+ mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerManager::LayerUserDataDestroy);
+ }
+ /**
+ * This can be used anytime. Ownership passes to the caller!
+ */
+ UniquePtr<LayerUserData> RemoveUserData(void* aKey);
+ /**
+ * This getter can be used anytime.
+ */
+ bool HasUserData(void* aKey)
+ {
+ return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
+ }
+ /**
+ * This getter can be used anytime. Ownership is retained by the layer
+ * manager.
+ */
+ LayerUserData* GetUserData(void* aKey) const
+ {
+ return static_cast<LayerUserData*>(mUserData.Get(static_cast<gfx::UserDataKey*>(aKey)));
+ }
+
+ /**
+ * |Disconnect()| is used by layers hooked up over IPC. It may be
+ * called at any time, and may not be called at all. Using an
+ * IPC-enabled layer after Destroy() (drawing etc.) results in a
+ * safe no-op; no crashy or uaf etc.
+ *
+ * XXX: this interface is essentially LayerManager::Destroy, but at
+ * Layer granularity. It might be beneficial to unify them.
+ */
+ virtual void Disconnect() {}
+
+ /**
+ * Dynamic downcast to a PaintedLayer. Returns null if this is not
+ * a PaintedLayer.
+ */
+ virtual PaintedLayer* AsPaintedLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a ContainerLayer. Returns null if this is not
+ * a ContainerLayer.
+ */
+ virtual ContainerLayer* AsContainerLayer() { return nullptr; }
+ virtual const ContainerLayer* AsContainerLayer() const { return nullptr; }
+
+ /**
+ * Dynamic cast to a RefLayer. Returns null if this is not a
+ * RefLayer.
+ */
+ virtual RefLayer* AsRefLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a Color. Returns null if this is not a
+ * ColorLayer.
+ */
+ virtual ColorLayer* AsColorLayer() { return nullptr; }
+
+ /**
+ * Dynamic cast to a LayerComposite. Return null if this is not a
+ * LayerComposite. Can be used anytime.
+ */
+ virtual LayerComposite* AsLayerComposite() { return nullptr; }
+
+ /**
+ * Dynamic cast to a ShadowableLayer. Return null if this is not a
+ * ShadowableLayer. Can be used anytime.
+ */
+ virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
+
+ // These getters can be used anytime. They return the effective
+ // values that should be used when drawing this layer to screen,
+ // accounting for this layer possibly being a shadow.
+ const Maybe<ParentLayerIntRect>& GetLocalClipRect();
+ const LayerIntRegion& GetLocalVisibleRegion();
+
+ bool Extend3DContext() {
+ return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT;
+ }
+ bool Combines3DTransformWithAncestors() {
+ return GetParent() &&
+ reinterpret_cast<Layer*>(GetParent())->Extend3DContext();
+ }
+ bool Is3DContextLeaf() {
+ return !Extend3DContext() && Combines3DTransformWithAncestors();
+ }
+ /**
+ * It is true if the user can see the back of the layer and the
+ * backface is hidden. The compositor should skip the layer if the
+ * result is true.
+ */
+ bool IsBackfaceHidden();
+ bool IsVisible() {
+ // For containers extending 3D context, visible region
+ // is meaningless, since they are just intermediate result of
+ // content.
+ return !GetLocalVisibleRegion().IsEmpty() || Extend3DContext();
+ }
+
+ /**
+ * Return true if current layer content is opaque.
+ * It does not guarantee that layer content is always opaque.
+ */
+ virtual bool IsOpaque() { return GetContentFlags() & CONTENT_OPAQUE; }
+
+ /**
+ * Returns the product of the opacities of this layer and all ancestors up
+ * to and excluding the nearest ancestor that has UseIntermediateSurface() set.
+ */
+ float GetEffectiveOpacity();
+
+ /**
+ * Returns the blendmode of this layer.
+ */
+ gfx::CompositionOp GetEffectiveMixBlendMode();
+
+ /**
+ * This returns the effective transform computed by
+ * ComputeEffectiveTransforms. Typically this is a transform that transforms
+ * this layer all the way to some intermediate surface or destination
+ * surface. For non-BasicLayers this will be a transform to the nearest
+ * ancestor with UseIntermediateSurface() (or to the root, if there is no
+ * such ancestor), but for BasicLayers it's different.
+ */
+ const gfx::Matrix4x4& GetEffectiveTransform() const { return mEffectiveTransform; }
+
+ /**
+ * This returns the effective transform for Layer's buffer computed by
+ * ComputeEffectiveTransforms. Typically this is a transform that transforms
+ * this layer's buffer all the way to some intermediate surface or destination
+ * surface. For non-BasicLayers this will be a transform to the nearest
+ * ancestor with UseIntermediateSurface() (or to the root, if there is no
+ * such ancestor), but for BasicLayers it's different.
+ *
+ * By default, its value is same to GetEffectiveTransform().
+ * When ImageLayer is rendered with ScaleMode::STRETCH,
+ * it becomes different from GetEffectiveTransform().
+ */
+ virtual const gfx::Matrix4x4& GetEffectiveTransformForBuffer() const
+ {
+ return mEffectiveTransform;
+ }
+
+ /**
+ * @param aTransformToSurface the composition of the transforms
+ * from the parent layer (if any) to the destination pixel grid.
+ *
+ * Computes mEffectiveTransform for this layer and all its descendants.
+ * mEffectiveTransform transforms this layer up to the destination
+ * pixel grid (whatever aTransformToSurface is relative to).
+ *
+ * We promise that when this is called on a layer, all ancestor layers
+ * have already had ComputeEffectiveTransforms called.
+ */
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0;
+
+ /**
+ * Computes the effective transform for mask layers, if this layer has any.
+ */
+ void ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface);
+ static void ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer,
+ const gfx::Matrix4x4& aTransformToSurface);
+
+ /**
+ * Calculate the scissor rect required when rendering this layer.
+ * Returns a rectangle relative to the intermediate surface belonging to the
+ * nearest ancestor that has an intermediate surface, or relative to the root
+ * viewport if no ancestor has an intermediate surface, corresponding to the
+ * clip rect for this layer intersected with aCurrentScissorRect.
+ */
+ RenderTargetIntRect CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect);
+
+ virtual const char* Name() const =0;
+ virtual LayerType GetType() const =0;
+
+ /**
+ * Only the implementation should call this. This is per-implementation
+ * private data. Normally, all layers with a given layer manager
+ * use the same type of ImplData.
+ */
+ void* ImplData() { return mImplData; }
+
+ /**
+ * Only the implementation should use these methods.
+ */
+ void SetParent(ContainerLayer* aParent) { mParent = aParent; }
+ void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; }
+ void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; }
+
+ /**
+ * Dump information about this layer manager and its managed tree to
+ * aStream.
+ */
+ void Dump(std::stringstream& aStream, const char* aPrefix="",
+ bool aDumpHtml=false, bool aSorted=false);
+ /**
+ * Dump information about just this layer manager itself to aStream.
+ */
+ void DumpSelf(std::stringstream& aStream, const char* aPrefix="");
+
+ /**
+ * Dump information about this layer and its child & sibling layers to
+ * layerscope packet.
+ */
+ void Dump(layerscope::LayersPacket* aPacket, const void* aParent);
+
+ /**
+ * Log information about this layer manager and its managed tree to
+ * the NSPR log (if enabled for "Layers").
+ */
+ void Log(const char* aPrefix="");
+ /**
+ * Log information about just this layer manager itself to the NSPR
+ * log (if enabled for "Layers").
+ */
+ void LogSelf(const char* aPrefix="");
+
+ // Print interesting information about this into aStream. Internally
+ // used to implement Dump*() and Log*(). If subclasses have
+ // additional interesting properties, they should override this with
+ // an implementation that first calls the base implementation then
+ // appends additional info to aTo.
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ // Just like PrintInfo, but this function dump information into layerscope packet,
+ // instead of a StringStream. It is also internally used to implement Dump();
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent);
+
+ /**
+ * Store display list log.
+ */
+ void SetDisplayListLog(const char *log);
+
+ /**
+ * Return display list log.
+ */
+ void GetDisplayListLog(nsCString& log);
+
+ static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
+
+ /**
+ * Returns the current area of the layer (in layer-space coordinates)
+ * marked as needed to be recomposited.
+ */
+ const virtual gfx::TiledIntRegion& GetInvalidRegion() { return mInvalidRegion; }
+ void AddInvalidRegion(const nsIntRegion& aRegion) {
+ mInvalidRegion.Add(aRegion);
+ }
+
+ /**
+ * Mark the entirety of the layer's visible region as being invalid.
+ */
+ void SetInvalidRectToVisibleRegion()
+ {
+ mInvalidRegion.SetEmpty();
+ mInvalidRegion.Add(GetVisibleRegion().ToUnknownRegion());
+ }
+
+ /**
+ * Adds to the current invalid rect.
+ */
+ void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Add(aRect); }
+
+ /**
+ * Clear the invalid rect, marking the layer as being identical to what is currently
+ * composited.
+ */
+ void ClearInvalidRect() { mInvalidRegion.SetEmpty(); }
+
+ // These functions allow attaching an AsyncPanZoomController to this layer,
+ // and can be used anytime.
+ // A layer has an APZC at index aIndex only-if GetFrameMetrics(aIndex).IsScrollable();
+ // attempting to get an APZC for a non-scrollable metrics will return null.
+ // The aIndex for these functions must be less than GetScrollMetadataCount().
+ void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller);
+ AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const;
+ // The ScrollMetadataChanged function is used internally to ensure the APZC array length
+ // matches the frame metrics array length.
+private:
+ void ScrollMetadataChanged();
+public:
+
+ void ApplyPendingUpdatesForThisTransaction();
+
+#ifdef DEBUG
+ void SetDebugColorIndex(uint32_t aIndex) { mDebugColorIndex = aIndex; }
+ uint32_t GetDebugColorIndex() { return mDebugColorIndex; }
+#endif
+
+ virtual LayerRenderState GetRenderState() { return LayerRenderState(); }
+
+ void Mutated()
+ {
+ mManager->Mutated(this);
+ }
+
+ virtual int32_t GetMaxLayerSize() { return Manager()->GetMaxTextureSize(); }
+
+ /**
+ * Returns true if this layer's effective transform is not just
+ * a translation by integers, or if this layer or some ancestor layer
+ * is marked as having a transform that may change without a full layer
+ * transaction.
+ */
+ bool MayResample();
+
+ RenderTargetRect TransformRectToRenderTarget(const LayerIntRect& aRect);
+
+ /**
+ * Add debugging information to the layer dump.
+ */
+ void AddExtraDumpInfo(const nsACString& aStr)
+ {
+#ifdef MOZ_DUMP_PAINTING
+ mExtraDumpInfo.AppendElement(aStr);
+#endif
+ }
+
+ /**
+ * Clear debugging information. Useful for recycling.
+ */
+ void ClearExtraDumpInfo()
+ {
+#ifdef MOZ_DUMP_PAINTING
+ mExtraDumpInfo.Clear();
+#endif
+ }
+
+protected:
+ Layer(LayerManager* aManager, void* aImplData);
+
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Layer();
+
+ /**
+ * We can snap layer transforms for two reasons:
+ * 1) To avoid unnecessary resampling when a transform is a translation
+ * by a non-integer number of pixels.
+ * Snapping the translation to an integer number of pixels avoids
+ * blurring the layer and can be faster to composite.
+ * 2) When a layer is used to render a rectangular object, we need to
+ * emulate the rendering of rectangular inactive content and snap the
+ * edges of the rectangle to pixel boundaries. This is both to ensure
+ * layer rendering is consistent with inactive content rendering, and to
+ * avoid seams.
+ * This function implements type 1 snapping. If aTransform is a 2D
+ * translation, and this layer's layer manager has enabled snapping
+ * (which is the default), return aTransform with the translation snapped
+ * to nearest pixels. Otherwise just return aTransform. Call this when the
+ * layer does not correspond to a single rectangular content object.
+ * This function does not try to snap if aTransform has a scale, because in
+ * that case resampling is inevitable and there's no point in trying to
+ * avoid it. In fact snapping can cause problems because pixel edges in the
+ * layer's content can be rendered unpredictably (jiggling) as the scale
+ * interacts with the snapping of the translation, especially with animated
+ * transforms.
+ * @param aResidualTransform a transform to apply before the result transform
+ * in order to get the results to completely match aTransform.
+ */
+ gfx::Matrix4x4 SnapTransformTranslation(const gfx::Matrix4x4& aTransform,
+ gfx::Matrix* aResidualTransform);
+ gfx::Matrix4x4 SnapTransformTranslation3D(const gfx::Matrix4x4& aTransform,
+ gfx::Matrix* aResidualTransform);
+ /**
+ * See comment for SnapTransformTranslation.
+ * This function implements type 2 snapping. If aTransform is a translation
+ * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
+ * and return the transform that maps aSnapRect to that rect. Otherwise
+ * just return aTransform.
+ * @param aSnapRect a rectangle whose edges should be snapped to pixel
+ * boundaries in the destination surface.
+ * @param aResidualTransform a transform to apply before the result transform
+ * in order to get the results to completely match aTransform.
+ */
+ gfx::Matrix4x4 SnapTransform(const gfx::Matrix4x4& aTransform,
+ const gfxRect& aSnapRect,
+ gfx::Matrix* aResidualTransform);
+
+ LayerManager* mManager;
+ ContainerLayer* mParent;
+ Layer* mNextSibling;
+ Layer* mPrevSibling;
+ void* mImplData;
+ RefPtr<Layer> mMaskLayer;
+ nsTArray<RefPtr<Layer>> mAncestorMaskLayers;
+ // Look for out-of-bound in the middle of the structure
+ mozilla::CorruptionCanary mCanary;
+ gfx::UserData mUserData;
+ gfx::IntRect mLayerBounds;
+ LayerIntRegion mVisibleRegion;
+ nsTArray<ScrollMetadata> mScrollMetadata;
+ EventRegions mEventRegions;
+ gfx::Matrix4x4 mTransform;
+ // A mutation of |mTransform| that we've queued to be applied at the
+ // end of the next transaction (if nothing else overrides it in the
+ // meantime).
+ nsAutoPtr<gfx::Matrix4x4> mPendingTransform;
+ float mPostXScale;
+ float mPostYScale;
+ gfx::Matrix4x4 mEffectiveTransform;
+ AnimationArray mAnimations;
+ // See mPendingTransform above.
+ nsAutoPtr<AnimationArray> mPendingAnimations;
+ InfallibleTArray<AnimData> mAnimationData;
+ float mOpacity;
+ gfx::CompositionOp mMixBlendMode;
+ bool mForceIsolatedGroup;
+ Maybe<ParentLayerIntRect> mClipRect;
+ Maybe<LayerClip> mScrolledClip;
+ gfx::IntRect mTileSourceRect;
+ gfx::TiledIntRegion mInvalidRegion;
+ nsTArray<RefPtr<AsyncPanZoomController> > mApzcs;
+ uint32_t mContentFlags;
+ bool mUseTileSourceRect;
+ bool mIsFixedPosition;
+ bool mTransformIsPerspective;
+ struct FixedPositionData {
+ FrameMetrics::ViewID mScrollId;
+ LayerPoint mAnchor;
+ int32_t mSides;
+ };
+ UniquePtr<FixedPositionData> mFixedPositionData;
+ struct StickyPositionData {
+ FrameMetrics::ViewID mScrollId;
+ LayerRect mOuter;
+ LayerRect mInner;
+ };
+ nsAutoPtr<StickyPositionData> mStickyPositionData;
+ FrameMetrics::ViewID mScrollbarTargetId;
+ ScrollDirection mScrollbarDirection;
+ // The scrollbar thumb ratio is the ratio of the thumb position (in the CSS
+ // pixels of the scrollframe's parent's space) to the scroll position (in the
+ // CSS pixels of the scrollframe's space).
+ float mScrollbarThumbRatio;
+ bool mIsScrollbarContainer;
+#ifdef DEBUG
+ uint32_t mDebugColorIndex;
+#endif
+ // If this layer is used for OMTA, then this counter is used to ensure we
+ // stay in sync with the animation manager
+ uint64_t mAnimationGeneration;
+#ifdef MOZ_DUMP_PAINTING
+ nsTArray<nsCString> mExtraDumpInfo;
+#endif
+ // Store display list log.
+ nsCString mDisplayListLog;
+};
+
+/**
+ * A Layer which we can paint into. It is a conceptually
+ * infinite surface, but each PaintedLayer has an associated "valid region"
+ * of contents that it is currently storing, which is finite. PaintedLayer
+ * implementations can store content between paints.
+ *
+ * PaintedLayers are rendered into during the drawing phase of a transaction.
+ *
+ * Currently the contents of a PaintedLayer are in the device output color
+ * space.
+ */
+class PaintedLayer : public Layer {
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Tell this layer that the content in some region has changed and
+ * will need to be repainted. This area is removed from the valid
+ * region.
+ */
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) = 0;
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set whether ComputeEffectiveTransforms should compute the
+ * "residual translation" --- the translation that should be applied *before*
+ * mEffectiveTransform to get the ideal transform for this PaintedLayer.
+ * When this is true, ComputeEffectiveTransforms will compute the residual
+ * and ensure that the layer is invalidated whenever the residual changes.
+ * When it's false, a change in the residual will not trigger invalidation
+ * and GetResidualTranslation will return 0,0.
+ * So when the residual is to be ignored, set this to false for better
+ * performance.
+ */
+ void SetAllowResidualTranslation(bool aAllow) { mAllowResidualTranslation = aAllow; }
+
+ /**
+ * Can be used anytime
+ */
+ const nsIntRegion& GetValidRegion() const { return mValidRegion; }
+
+ virtual PaintedLayer* AsPaintedLayer() override { return this; }
+
+ MOZ_LAYER_DECL_NAME("PaintedLayer", TYPE_PAINTED)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+ gfx::Matrix residual;
+ mEffectiveTransform = SnapTransformTranslation(idealTransform,
+ mAllowResidualTranslation ? &residual : nullptr);
+ // The residual can only be a translation because SnapTransformTranslation
+ // only changes the transform if it's a translation
+ NS_ASSERTION(residual.IsTranslation(),
+ "Residual transform can only be a translation");
+ if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
+ mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
+ DebugOnly<mozilla::gfx::Point> transformedOrig =
+ idealTransform.TransformPoint(mozilla::gfx::Point());
+#ifdef DEBUG
+ DebugOnly<mozilla::gfx::Point> transformed = idealTransform.TransformPoint(
+ mozilla::gfx::Point(mResidualTranslation.x, mResidualTranslation.y)
+ ) - *&transformedOrig;
+#endif
+ NS_ASSERTION(-0.5 <= (&transformed)->x && (&transformed)->x < 0.5 &&
+ -0.5 <= (&transformed)->y && (&transformed)->y < 0.5,
+ "Residual translation out of range");
+ mValidRegion.SetEmpty();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+ LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; }
+
+ bool UsedForReadback() { return mUsedForReadback; }
+ void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
+
+ /**
+ * Returns true if aLayer is optimized for the given PaintedLayerCreationHint.
+ */
+ virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint)
+ { return true; }
+
+ /**
+ * Returns the residual translation. Apply this translation when drawing
+ * into the PaintedLayer so that when mEffectiveTransform is applied afterwards
+ * by layer compositing, the results exactly match the "ideal transform"
+ * (the product of the transform of this layer and its ancestors).
+ * Returns 0,0 unless SetAllowResidualTranslation(true) has been called.
+ * The residual translation components are always in the range [-0.5, 0.5).
+ */
+ gfxPoint GetResidualTranslation() const { return mResidualTranslation; }
+
+protected:
+ PaintedLayer(LayerManager* aManager, void* aImplData,
+ LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE)
+ : Layer(aManager, aImplData)
+ , mValidRegion()
+ , mCreationHint(aCreationHint)
+ , mUsedForReadback(false)
+ , mAllowResidualTranslation(false)
+ {
+ mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
+ }
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ /**
+ * ComputeEffectiveTransforms snaps the ideal transform to get mEffectiveTransform.
+ * mResidualTranslation is the translation that should be applied *before*
+ * mEffectiveTransform to get the ideal transform.
+ */
+ gfxPoint mResidualTranslation;
+ nsIntRegion mValidRegion;
+ /**
+ * The creation hint that was used when constructing this layer.
+ */
+ const LayerManager::PaintedLayerCreationHint mCreationHint;
+ /**
+ * Set when this PaintedLayer is participating in readback, i.e. some
+ * ReadbackLayer (may) be getting its background from this layer.
+ */
+ bool mUsedForReadback;
+ /**
+ * True when
+ */
+ bool mAllowResidualTranslation;
+};
+
+/**
+ * A Layer which other layers render into. It holds references to its
+ * children.
+ */
+class ContainerLayer : public Layer {
+public:
+
+ ~ContainerLayer();
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Insert aChild into the child list of this container. aChild must
+ * not be currently in any child list or the root for the layer manager.
+ * If aAfter is non-null, it must be a child of this container and
+ * we insert after that layer. If it's null we insert at the start.
+ */
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter);
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Remove aChild from the child list of this container. aChild must
+ * be a child of this container.
+ */
+ virtual bool RemoveChild(Layer* aChild);
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Reposition aChild from the child list of this container. aChild must
+ * be a child of this container.
+ * If aAfter is non-null, it must be a child of this container and we
+ * reposition after that layer. If it's null, we reposition at the start.
+ */
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter);
+
+ void SetPreScale(float aXScale, float aYScale)
+ {
+ if (mPreXScale == aXScale && mPreYScale == aYScale) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PreScale", this));
+ mPreXScale = aXScale;
+ mPreYScale = aYScale;
+ Mutated();
+ }
+
+ void SetInheritedScale(float aXScale, float aYScale)
+ {
+ if (mInheritedXScale == aXScale && mInheritedYScale == aYScale) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) InheritedScale", this));
+ mInheritedXScale = aXScale;
+ mInheritedYScale = aYScale;
+ Mutated();
+ }
+
+ void SetScaleToResolution(bool aScaleToResolution, float aResolution)
+ {
+ if (mScaleToResolution == aScaleToResolution && mPresShellResolution == aResolution) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScaleToResolution", this));
+ mScaleToResolution = aScaleToResolution;
+ mPresShellResolution = aResolution;
+ Mutated();
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+
+ void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+
+ // These getters can be used anytime.
+
+ virtual ContainerLayer* AsContainerLayer() override { return this; }
+ virtual const ContainerLayer* AsContainerLayer() const override { return this; }
+
+ virtual Layer* GetFirstChild() const override { return mFirstChild; }
+ virtual Layer* GetLastChild() const override { return mLastChild; }
+ float GetPreXScale() const { return mPreXScale; }
+ float GetPreYScale() const { return mPreYScale; }
+ float GetInheritedXScale() const { return mInheritedXScale; }
+ float GetInheritedYScale() const { return mInheritedYScale; }
+ float GetPresShellResolution() const { return mPresShellResolution; }
+ bool ScaleToResolution() const { return mScaleToResolution; }
+
+ MOZ_LAYER_DECL_NAME("ContainerLayer", TYPE_CONTAINER)
+
+ /**
+ * ContainerLayer backends need to override ComputeEffectiveTransforms
+ * since the decision about whether to use a temporary surface for the
+ * container is backend-specific. ComputeEffectiveTransforms must also set
+ * mUseIntermediateSurface.
+ */
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override = 0;
+
+ /**
+ * Call this only after ComputeEffectiveTransforms has been invoked
+ * on this layer.
+ * Returns true if this will use an intermediate surface. This is largely
+ * backend-dependent, but it affects the operation of GetEffectiveOpacity().
+ */
+ bool UseIntermediateSurface() { return mUseIntermediateSurface; }
+
+ /**
+ * Returns the rectangle covered by the intermediate surface,
+ * in this layer's coordinate system.
+ *
+ * NOTE: Since this layer has an intermediate surface it follows
+ * that LayerPixel == RenderTargetPixel
+ */
+ RenderTargetIntRect GetIntermediateSurfaceRect()
+ {
+ NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface");
+ return RenderTargetIntRect::FromUnknownRect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+
+ /**
+ * Returns true if this container has more than one non-empty child
+ */
+ bool HasMultipleChildren();
+
+ /**
+ * Returns true if this container supports children with component alpha.
+ * Should only be called while painting a child of this layer.
+ */
+ bool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; }
+
+ /**
+ * Returns true if aLayer or any layer in its parent chain has the opaque
+ * content flag set.
+ */
+ static bool HasOpaqueAncestorLayer(Layer* aLayer);
+
+ void SetChildrenChanged(bool aVal) {
+ mChildrenChanged = aVal;
+ }
+
+ void SetEventRegionsOverride(EventRegionsOverride aVal) {
+ if (mEventRegionsOverride == aVal) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) EventRegionsOverride", this));
+ mEventRegionsOverride = aVal;
+ Mutated();
+ }
+
+ EventRegionsOverride GetEventRegionsOverride() const {
+ return mEventRegionsOverride;
+ }
+
+protected:
+ friend class ReadbackProcessor;
+
+ void DidInsertChild(Layer* aLayer);
+ void DidRemoveChild(Layer* aLayer);
+
+ void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
+
+ ContainerLayer(LayerManager* aManager, void* aImplData);
+
+ /**
+ * A default implementation of ComputeEffectiveTransforms for use by OpenGL
+ * and D3D.
+ */
+ void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
+
+ /**
+ * A default implementation to compute and set the value for SupportsComponentAlphaChildren().
+ *
+ * If aNeedsSurfaceCopy is provided, then it is set to true if the caller needs to copy the background
+ * up into the intermediate surface created, false otherwise.
+ */
+ void DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy = nullptr);
+
+ /**
+ * Loops over the children calling ComputeEffectiveTransforms on them.
+ */
+ void ComputeEffectiveTransformsForChildren(const gfx::Matrix4x4& aTransformToSurface);
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ /**
+ * True for if the container start a new 3D context extended by one
+ * or more children.
+ */
+ bool Creates3DContextWithExtendingChildren();
+
+ Layer* mFirstChild;
+ Layer* mLastChild;
+ float mPreXScale;
+ float mPreYScale;
+ // The resolution scale inherited from the parent layer. This will already
+ // be part of mTransform.
+ float mInheritedXScale;
+ float mInheritedYScale;
+ // For layers corresponding to an nsDisplayResolution, the resolution of the
+ // associated pres shell; for other layers, 1.0.
+ float mPresShellResolution;
+ // Whether the compositor should scale to mPresShellResolution.
+ bool mScaleToResolution;
+ bool mUseIntermediateSurface;
+ bool mSupportsComponentAlphaChildren;
+ bool mMayHaveReadbackChild;
+ // This is updated by ComputeDifferences. This will be true if we need to invalidate
+ // the intermediate surface.
+ bool mChildrenChanged;
+ EventRegionsOverride mEventRegionsOverride;
+};
+
+/**
+ * A Layer which just renders a solid color in its visible region. It actually
+ * can fill any area that contains the visible region, so if you need to
+ * restrict the area filled, set a clip region on this layer.
+ */
+class ColorLayer : public Layer {
+public:
+ virtual ColorLayer* AsColorLayer() override { return this; }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the color of the layer.
+ */
+ virtual void SetColor(const gfx::Color& aColor)
+ {
+ if (mColor != aColor) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Color", this));
+ mColor = aColor;
+ Mutated();
+ }
+ }
+
+ void SetBounds(const gfx::IntRect& aBounds)
+ {
+ if (!mBounds.IsEqualEdges(aBounds)) {
+ mBounds = aBounds;
+ Mutated();
+ }
+ }
+
+ const gfx::IntRect& GetBounds()
+ {
+ return mBounds;
+ }
+
+ // This getter can be used anytime.
+ virtual const gfx::Color& GetColor() { return mColor; }
+
+ MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+ mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+protected:
+ ColorLayer(LayerManager* aManager, void* aImplData)
+ : Layer(aManager, aImplData)
+ , mColor()
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ gfx::IntRect mBounds;
+ gfx::Color mColor;
+};
+
+/**
+ * A Layer for HTML Canvas elements. It's backed by either a
+ * gfxASurface or a GLContext (for WebGL layers), and has some control
+ * for intelligent updating from the source if necessary (for example,
+ * if hardware compositing is not available, for reading from the GL
+ * buffer into an image surface that we can layer composite.)
+ *
+ * After Initialize is called, the underlying canvas Surface/GLContext
+ * must not be modified during a layer transaction.
+ */
+class CanvasLayer : public Layer {
+public:
+ struct Data {
+ Data()
+ : mBufferProvider(nullptr)
+ , mGLContext(nullptr)
+ , mRenderer(nullptr)
+ , mFrontbufferGLTex(0)
+ , mSize(0,0)
+ , mHasAlpha(false)
+ , mIsGLAlphaPremult(true)
+ , mIsMirror(false)
+ { }
+
+ // One of these three must be specified for Canvas2D, but never more than one
+ PersistentBufferProvider* mBufferProvider; // A BufferProvider for the Canvas contents
+ mozilla::gl::GLContext* mGLContext; // or this, for GL.
+ AsyncCanvasRenderer* mRenderer; // or this, for OffscreenCanvas
+
+ // Frontbuffer override
+ uint32_t mFrontbufferGLTex;
+
+ // The size of the canvas content
+ gfx::IntSize mSize;
+
+ // Whether the canvas drawingbuffer has an alpha channel.
+ bool mHasAlpha;
+
+ // Whether mGLContext contains data that is alpha-premultiplied.
+ bool mIsGLAlphaPremult;
+
+ // Whether the canvas front buffer is already being rendered somewhere else.
+ // When true, do not swap buffers or Morph() to another factory on mGLContext
+ bool mIsMirror;
+ };
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Initialize this CanvasLayer with the given data. The data must
+ * have either mSurface or mGLContext initialized (but not both), as
+ * well as mSize.
+ *
+ * This must only be called once.
+ */
+ virtual void Initialize(const Data& aData) = 0;
+
+ /**
+ * Check the data is owned by this layer is still valid for rendering
+ */
+ virtual bool IsDataValid(const Data& aData) { return true; }
+
+ /**
+ * Notify this CanvasLayer that the canvas surface contents have
+ * changed (or will change) before the next transaction.
+ */
+ void Updated() { mDirty = true; SetInvalidRectToVisibleRegion(); }
+
+ /**
+ * Notify this CanvasLayer that the canvas surface contents have
+ * been painted since the last change.
+ */
+ void Painted() { mDirty = false; }
+
+ /**
+ * Returns true if the canvas surface contents have changed since the
+ * last paint.
+ */
+ bool IsDirty()
+ {
+ // We can only tell if we are dirty if we're part of the
+ // widget's retained layer tree.
+ if (!mManager || !mManager->IsWidgetLayerManager()) {
+ return true;
+ }
+ return mDirty;
+ }
+
+ /**
+ * Register a callback to be called at the start of each transaction.
+ */
+ typedef void PreTransactionCallback(void* closureData);
+ void SetPreTransactionCallback(PreTransactionCallback* callback, void* closureData)
+ {
+ mPreTransCallback = callback;
+ mPreTransCallbackData = closureData;
+ }
+
+ const nsIntRect& GetBounds() const { return mBounds; }
+
+protected:
+ void FirePreTransactionCallback()
+ {
+ if (mPreTransCallback) {
+ mPreTransCallback(mPreTransCallbackData);
+ }
+ }
+
+public:
+ /**
+ * Register a callback to be called at the end of each transaction.
+ */
+ typedef void (* DidTransactionCallback)(void* aClosureData);
+ void SetDidTransactionCallback(DidTransactionCallback aCallback, void* aClosureData)
+ {
+ mPostTransCallback = aCallback;
+ mPostTransCallbackData = aClosureData;
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the filter used to resample this image (if necessary).
+ */
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mSamplingFilter != aSamplingFilter) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this));
+ mSamplingFilter = aSamplingFilter;
+ Mutated();
+ }
+ }
+ gfx::SamplingFilter GetSamplingFilter() const { return mSamplingFilter; }
+
+ MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
+ nullptr)*
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ }
+
+ bool GetIsAsyncRenderer() const
+ {
+ return !!mAsyncRenderer;
+ }
+
+protected:
+ CanvasLayer(LayerManager* aManager, void* aImplData);
+ virtual ~CanvasLayer();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ void FireDidTransactionCallback()
+ {
+ if (mPostTransCallback) {
+ mPostTransCallback(mPostTransCallbackData);
+ }
+ }
+
+ /**
+ * 0, 0, canvaswidth, canvasheight
+ */
+ gfx::IntRect mBounds;
+ PreTransactionCallback* mPreTransCallback;
+ void* mPreTransCallbackData;
+ DidTransactionCallback mPostTransCallback;
+ void* mPostTransCallbackData;
+ gfx::SamplingFilter mSamplingFilter;
+ RefPtr<AsyncCanvasRenderer> mAsyncRenderer;
+
+private:
+ /**
+ * Set to true in Updated(), cleared during a transaction.
+ */
+ bool mDirty;
+};
+
+/**
+ * ContainerLayer that refers to a "foreign" layer tree, through an
+ * ID. Usage of RefLayer looks like
+ *
+ * Construction phase:
+ * allocate ID for layer subtree
+ * create RefLayer, SetReferentId(ID)
+ *
+ * Composition:
+ * look up subtree for GetReferentId()
+ * ConnectReferentLayer(subtree)
+ * compose
+ * ClearReferentLayer()
+ *
+ * Clients will usually want to Connect/Clear() on each transaction to
+ * avoid difficulties managing memory across multiple layer subtrees.
+ */
+class RefLayer : public ContainerLayer {
+ friend class LayerManager;
+
+private:
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ { MOZ_CRASH("GFX: RefLayer"); return false; }
+
+public:
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the ID of the layer's referent.
+ */
+ void SetReferentId(uint64_t aId)
+ {
+ MOZ_ASSERT(aId != 0);
+ if (mId != aId) {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ReferentId", this));
+ mId = aId;
+ Mutated();
+ }
+ }
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Connect this ref layer to its referent, temporarily.
+ * ClearReferentLayer() must be called after composition.
+ */
+ void ConnectReferentLayer(Layer* aLayer)
+ {
+ MOZ_ASSERT(!mFirstChild && !mLastChild);
+ MOZ_ASSERT(!aLayer->GetParent());
+ if (aLayer->Manager() != Manager()) {
+ // This can happen when e.g. rendering while dragging tabs
+ // between windows - aLayer's manager may be the manager for the
+ // old window's tab. In that case, it will be changed before the
+ // next render (see SetLayerManager). It is simply easier to
+ // ignore the rendering here than it is to pause it.
+ NS_WARNING("ConnectReferentLayer failed - Incorrect LayerManager");
+ return;
+ }
+
+ mFirstChild = mLastChild = aLayer;
+ aLayer->SetParent(this);
+ }
+
+ /**
+ * DRAWING PHASE ONLY
+ * |aLayer| is the same as the argument to ConnectReferentLayer().
+ */
+ void DetachReferentLayer(Layer* aLayer)
+ {
+ mFirstChild = mLastChild = nullptr;
+ aLayer->SetParent(nullptr);
+ }
+
+ // These getters can be used anytime.
+ virtual RefLayer* AsRefLayer() override { return this; }
+
+ virtual int64_t GetReferentId() { return mId; }
+
+ /**
+ * DRAWING PHASE ONLY
+ */
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+
+ MOZ_LAYER_DECL_NAME("RefLayer", TYPE_REF)
+
+protected:
+ RefLayer(LayerManager* aManager, void* aImplData)
+ : ContainerLayer(aManager, aImplData) , mId(0)
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ // 0 is a special value that means "no ID".
+ uint64_t mId;
+};
+
+void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget);
+
+#ifdef MOZ_DUMP_PAINTING
+void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf);
+void WriteSnapshotToDumpFile(LayerManager* aManager, gfx::DataSourceSurface* aSurf);
+void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget);
+#endif
+
+// A utility function used by different LayerManager implementations.
+gfx::IntRect ToOutsideIntRect(const gfxRect &aRect);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERS_H */
diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp
new file mode 100644
index 000000000..3d1164360
--- /dev/null
+++ b/gfx/layers/LayersLogging.cpp
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayersLogging.h"
+#include <stdint.h> // for uint8_t
+#include "ImageTypes.h" // for ImageFormat
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "nsDebug.h" // for NS_ERROR
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "base/basictypes.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, const void* p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("%p", p).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << n;
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Color& c,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "rgba(%d, %d, %d, %f)",
+ uint8_t(c.r*255.f), uint8_t(c.g*255.f), uint8_t(c.b*255.f), c.a).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsPoint& p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("(x=%d, y=%d)", p.x, p.y).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRect& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsIntPoint& p,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString("(x=%d, y=%d)", p.x, p.y).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const IntRect& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsIntRegion& r,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const EventRegions& e,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx << "{";
+ if (!e.mHitRegion.IsEmpty()) {
+ AppendToString(aStream, e.mHitRegion, " hitregion=", "");
+ }
+ if (!e.mDispatchToContentHitRegion.IsEmpty()) {
+ AppendToString(aStream, e.mDispatchToContentHitRegion, " dispatchtocontentregion=", "");
+ }
+ if (!e.mNoActionRegion.IsEmpty()) {
+ AppendToString(aStream, e.mNoActionRegion, " NoActionRegion=","");
+ }
+ if (!e.mHorizontalPanRegion.IsEmpty()) {
+ AppendToString(aStream, e.mHorizontalPanRegion, " HorizontalPanRegion=", "");
+ }
+ if (!e.mVerticalPanRegion.IsEmpty()) {
+ AppendToString(aStream, e.mVerticalPanRegion, " VerticalPanRegion=", "");
+ }
+ aStream << "}" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ScrollMetadata& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ AppendToString(aStream, m.GetMetrics(), "{ [metrics=");
+ AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
+ if (m.GetScrollParentId() != FrameMetrics::NULL_SCROLL_ID) {
+ AppendToString(aStream, m.GetScrollParentId(), "] [scrollParent=");
+ }
+ if (m.HasScrollClip()) {
+ AppendToString(aStream, m.ScrollClip().GetClipRect(), "] [clip=");
+ }
+ aStream << "] }" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const FrameMetrics& m,
+ const char* pfx, const char* sfx, bool detailed)
+{
+ aStream << pfx;
+ AppendToString(aStream, m.GetCompositionBounds(), "{ [cb=");
+ AppendToString(aStream, m.GetScrollableRect(), "] [sr=");
+ AppendToString(aStream, m.GetScrollOffset(), "] [s=");
+ if (m.GetDoSmoothScroll()) {
+ AppendToString(aStream, m.GetSmoothScrollOffset(), "] [ss=");
+ }
+ AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
+ AppendToString(aStream, m.GetCriticalDisplayPort(), "] [cdp=");
+ if (!detailed) {
+ AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
+ if (m.IsRootContent()) {
+ aStream << "] [rcd";
+ }
+ AppendToString(aStream, m.GetZoom(), "] [z=", "] }");
+ } else {
+ AppendToString(aStream, m.GetDisplayPortMargins(), " [dpm=");
+ aStream << nsPrintfCString("] um=%d", m.GetUseDisplayPortMargins()).get();
+ AppendToString(aStream, m.GetRootCompositionSize(), "] [rcs=");
+ AppendToString(aStream, m.GetViewport(), "] [v=");
+ aStream << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
+ m.GetDevPixelsPerCSSPixel().scale,
+ m.GetPresShellResolution()).get();
+ AppendToString(aStream, m.GetCumulativeResolution(), " cr=");
+ AppendToString(aStream, m.GetZoom(), " z=");
+ AppendToString(aStream, m.GetExtraResolution(), " er=");
+ aStream << nsPrintfCString(")] [u=(%d %d %lu)",
+ m.GetScrollUpdateType(), m.GetDoSmoothScroll(),
+ m.GetScrollGeneration()).get();
+ aStream << nsPrintfCString("] [i=(%ld %lld %d)] }",
+ m.GetPresShellId(), m.GetScrollId(), m.IsRootContent()).get();
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx
+ << nsPrintfCString("{ l=%" PRIu64 ", p=%u, v=%" PRIu64 " }", s.mLayersId, s.mPresShellId, s.mScrollId).get()
+ << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx
+ << nsPrintfCString("{ z=%d dt=%d min=%f max=%f }", z.mAllowZoom, z.mAllowDoubleTapZoom, z.mMinZoom.scale, z.mMaxZoom.scale).get()
+ << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Matrix& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ if (m.IsIdentity()) {
+ aStream << "[ I ]";
+ } else {
+ aStream << nsPrintfCString(
+ "[ %g %g; %g %g; %g %g; ]",
+ m._11, m._12, m._21, m._22, m._31, m._32).get();
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const Matrix5x4& m,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]",
+ m._11, m._12, m._13, m._14,
+ m._21, m._22, m._23, m._24,
+ m._31, m._32, m._33, m._34,
+ m._41, m._42, m._43, m._44,
+ m._51, m._52, m._53, m._54).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const SamplingFilter filter,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+
+ switch (filter) {
+ case SamplingFilter::GOOD: aStream << "SamplingFilter::GOOD"; break;
+ case SamplingFilter::LINEAR: aStream << "SamplingFilter::LINEAR"; break;
+ case SamplingFilter::POINT: aStream << "SamplingFilter::POINT"; break;
+ default:
+ NS_ERROR("unknown SamplingFilter type");
+ aStream << "???";
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, TextureFlags flags,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ if (flags == TextureFlags::NO_FLAGS) {
+ aStream << "NoFlags";
+ } else {
+
+#define AppendFlag(test) \
+{ \
+ if (!!(flags & test)) { \
+ if (previous) { \
+ aStream << "|"; \
+ } \
+ aStream << #test; \
+ previous = true; \
+ } \
+}
+ bool previous = false;
+ AppendFlag(TextureFlags::USE_NEAREST_FILTER);
+ AppendFlag(TextureFlags::ORIGIN_BOTTOM_LEFT);
+ AppendFlag(TextureFlags::DISALLOW_BIGIMAGE);
+
+#undef AppendFlag
+ }
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, mozilla::gfx::SurfaceFormat format,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch (format) {
+ case SurfaceFormat::B8G8R8A8: aStream << "SurfaceFormat::B8G8R8A8"; break;
+ case SurfaceFormat::B8G8R8X8: aStream << "SurfaceFormat::B8G8R8X8"; break;
+ case SurfaceFormat::R8G8B8A8: aStream << "SurfaceFormat::R8G8B8A8"; break;
+ case SurfaceFormat::R8G8B8X8: aStream << "SurfaceFormat::R8G8B8X8"; break;
+ case SurfaceFormat::R5G6B5_UINT16:
+ aStream << "SurfaceFormat::R5G6B5_UINT16"; break;
+ case SurfaceFormat::A8: aStream << "SurfaceFormat::A8"; break;
+ case SurfaceFormat::YUV: aStream << "SurfaceFormat::YUV"; break;
+ case SurfaceFormat::NV12: aStream << "SurfaceFormat::NV12"; break;
+ case SurfaceFormat::YUV422: aStream << "SurfaceFormat::YUV422"; break;
+ case SurfaceFormat::UNKNOWN: aStream << "SurfaceFormat::UNKNOWN"; break;
+ default:
+ NS_ERROR("unknown surface format");
+ aStream << "???";
+ }
+
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, gfx::SurfaceType aType,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch(aType) {
+ case SurfaceType::DATA:
+ aStream << "SurfaceType::DATA"; break;
+ case SurfaceType::D2D1_BITMAP:
+ aStream << "SurfaceType::D2D1_BITMAP"; break;
+ case SurfaceType::D2D1_DRAWTARGET:
+ aStream << "SurfaceType::D2D1_DRAWTARGET"; break;
+ case SurfaceType::CAIRO:
+ aStream << "SurfaceType::CAIRO"; break;
+ case SurfaceType::CAIRO_IMAGE:
+ aStream << "SurfaceType::CAIRO_IMAGE"; break;
+ case SurfaceType::COREGRAPHICS_IMAGE:
+ aStream << "SurfaceType::COREGRAPHICS_IMAGE"; break;
+ case SurfaceType::COREGRAPHICS_CGCONTEXT:
+ aStream << "SurfaceType::COREGRAPHICS_CGCONTEXT"; break;
+ case SurfaceType::SKIA:
+ aStream << "SurfaceType::SKIA"; break;
+ case SurfaceType::DUAL_DT:
+ aStream << "SurfaceType::DUAL_DT"; break;
+ case SurfaceType::D2D1_1_IMAGE:
+ aStream << "SurfaceType::D2D1_1_IMAGE"; break;
+ case SurfaceType::RECORDING:
+ aStream << "SurfaceType::RECORDING"; break;
+ case SurfaceType::TILED:
+ aStream << "SurfaceType::TILED"; break;
+ default:
+ NS_ERROR("unknown surface type");
+ aStream << "???";
+ }
+ aStream << sfx;
+}
+
+
+void
+AppendToString(std::stringstream& aStream, ImageFormat format,
+ const char* pfx, const char* sfx)
+{
+ aStream << pfx;
+ switch (format) {
+ case ImageFormat::PLANAR_YCBCR:
+ aStream << "ImageFormat::PLANAR_YCBCR"; break;
+ case ImageFormat::GRALLOC_PLANAR_YCBCR:
+ aStream << "ImageFormat::GRALLOC_PLANAR_YCBCR"; break;
+ case ImageFormat::SHARED_RGB:
+ aStream << "ImageFormat::SHARED_RGB"; break;
+ case ImageFormat::CAIRO_SURFACE:
+ aStream << "ImageFormat::CAIRO_SURFACE"; break;
+ case ImageFormat::MAC_IOSURFACE:
+ aStream << "ImageFormat::MAC_IOSURFACE"; break;
+ case ImageFormat::SURFACE_TEXTURE:
+ aStream << "ImageFormat::SURFACE_TEXTURE"; break;
+ case ImageFormat::EGLIMAGE:
+ aStream << "ImageFormat::EGLIMAGE"; break;
+ case ImageFormat::D3D9_RGB32_TEXTURE:
+ aStream << "ImageFormat::D3D9_RBG32_TEXTURE"; break;
+ case ImageFormat::OVERLAY_IMAGE:
+ aStream << "ImageFormat::OVERLAY_IMAGE"; break;
+ case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
+ aStream << "ImageFormat::D3D11_SHARE_HANDLE_TEXTURE"; break;
+ default:
+ NS_ERROR("unknown image format");
+ aStream << "???";
+ }
+
+ aStream << sfx;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+void
+print_stderr(std::stringstream& aStr)
+{
+#if defined(ANDROID)
+ // On Android logcat output is truncated to 1024 chars per line, and
+ // we usually use std::stringstream to build up giant multi-line gobs
+ // of output. So to avoid the truncation we find the newlines and
+ // print the lines individually.
+ std::string line;
+ while (std::getline(aStr, line)) {
+ printf_stderr("%s\n", line.c_str());
+ }
+#else
+ printf_stderr("%s", aStr.str().c_str());
+#endif
+}
+
+void
+fprint_stderr(FILE* aFile, std::stringstream& aStr)
+{
+ if (aFile == stderr) {
+ print_stderr(aStr);
+ } else {
+ fprintf_stderr(aFile, "%s", aStr.str().c_str());
+ }
+}
diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h
new file mode 100644
index 000000000..cb123a245
--- /dev/null
+++ b/gfx/layers/LayersLogging.h
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSLOGGING_H
+#define GFX_LAYERSLOGGING_H
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, etc
+#include "mozilla/gfx/TiledRegion.h" // for TiledRegion
+#include "mozilla/gfx/Types.h" // for SamplingFilter, SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsRegion.h" // for nsRegion, nsIntRegion
+#include "nscore.h" // for nsACString, etc
+
+namespace mozilla {
+namespace gfx {
+template <class units, class F> struct RectTyped;
+} // namespace gfx
+
+enum class ImageFormat;
+
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, const void* p,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const gfx::Color& c,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsPoint& p,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsRect& r,
+ const char* pfx="", const char* sfx="");
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::PointTyped<T>& p,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx << p << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntPointTyped<T>& p,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx << p << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::RectTyped<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%f, y=%f, w=%f, h=%f)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectTyped<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(x=%d, y=%d, w=%d, h=%d)",
+ r.x, r.y, r.width, r.height).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const nsRegion& r,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const nsIntRegion& r,
+ const char* pfx="", const char* sfx="");
+
+template <typename units>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRegionTyped<units>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+
+ aStream << "< ";
+ for (auto iter = r.RectIter(); !iter.Done(); iter.Next()) {
+ AppendToString(aStream, iter.Get());
+ aStream << "; ";
+ }
+ aStream << ">";
+
+ aStream << sfx;
+}
+
+template <typename T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::TiledRegion<T>& r,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ AppendToString(aStream, r.GetRegion());
+ aStream << " (bounds=";
+ AppendToString(aStream, r.GetBounds());
+ aStream << ", covers=" << r.CoversBounds() << ")" << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const EventRegions& e,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const ScrollMetadata& m,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const FrameMetrics& m,
+ const char* pfx="", const char* sfx="", bool detailed = false);
+
+void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
+ const char* pfx="", const char* sfx="");
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(l=%f, t=%f, r=%f, b=%f)",
+ m.left, m.top, m.right, m.bottom).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::SizeTyped<T>& sz,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(w=%f, h=%f)",
+ sz.width, sz.height).get();
+ aStream << sfx;
+}
+
+template<class T>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::IntSizeTyped<T>& sz,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "(w=%d, h=%d)",
+ sz.width, sz.height).get();
+ aStream << sfx;
+}
+
+template<class src, class dst>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::ScaleFactors2D<src, dst>& scale,
+ const char* pfx="", const char* sfx="")
+{
+ aStream << pfx;
+ std::streamsize oldPrecision = aStream.precision(3);
+ if (scale.AreScalesSame()) {
+ aStream << scale.xScale;
+ } else {
+ aStream << '(' << scale.xScale << ',' << scale.yScale << ')';
+ }
+ aStream.precision(oldPrecision);
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix& m,
+ const char* pfx="", const char* sfx="");
+
+template<class SourceUnits, class TargetUnits>
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& m,
+ const char* pfx="", const char* sfx="")
+{
+ if (m.Is2D()) {
+ mozilla::gfx::Matrix matrix = m.As2D();
+ AppendToString(aStream, matrix, pfx, sfx);
+ return;
+ }
+
+ aStream << pfx;
+ aStream << nsPrintfCString(
+ "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
+ m._11, m._12, m._13, m._14,
+ m._21, m._22, m._23, m._24,
+ m._31, m._32, m._33, m._34,
+ m._41, m._42, m._43, m._44).get();
+ aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream,
+ const mozilla::gfx::SamplingFilter samplingFilter,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, mozilla::layers::TextureFlags flags,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, mozilla::gfx::SurfaceFormat format,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, gfx::SurfaceType format,
+ const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, ImageFormat format,
+ const char* pfx="", const char* sfx="");
+
+// Sometimes, you just want a string from a single value.
+template <typename T>
+std::string
+Stringify(const T& obj)
+{
+ std::stringstream ss;
+ AppendToString(ss, obj);
+ return ss.str();
+}
+
+} // namespace layers
+} // namespace mozilla
+
+// versions of printf_stderr and fprintf_stderr that deal with line
+// truncation on android by printing individual lines out of the
+// stringstream as separate calls to logcat.
+void print_stderr(std::stringstream& aStr);
+void fprint_stderr(FILE* aFile, std::stringstream& aStr);
+
+#endif /* GFX_LAYERSLOGGING_H */
diff --git a/gfx/layers/LayersTypes.cpp b/gfx/layers/LayersTypes.cpp
new file mode 100644
index 000000000..a79f44554
--- /dev/null
+++ b/gfx/layers/LayersTypes.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+LayerRenderState::LayerRenderState()
+ : mFlags(LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT)
+ , mHasOwnOffset(false)
+{
+}
+
+LayerRenderState::LayerRenderState(const LayerRenderState& aOther)
+ : mFlags(aOther.mFlags)
+ , mHasOwnOffset(aOther.mHasOwnOffset)
+ , mOffset(aOther.mOffset)
+{
+}
+
+LayerRenderState::~LayerRenderState()
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/LayersTypes.h b/gfx/layers/LayersTypes.h
new file mode 100644
index 000000000..8d52ed2fd
--- /dev/null
+++ b/gfx/layers/LayersTypes.h
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERSTYPES_H
+#define GFX_LAYERSTYPES_H
+
+#include <stdint.h> // for uint32_t
+
+#include "Units.h"
+#include "mozilla/gfx/Point.h" // for IntPoint
+#include "mozilla/TypedEnumBits.h"
+#include "nsRegion.h"
+
+#include <stdio.h> // FILE
+#include "mozilla/Logging.h" // for PR_LOG
+
+#ifndef MOZ_LAYERS_HAVE_LOG
+# define MOZ_LAYERS_HAVE_LOG
+#endif
+#define MOZ_LAYERS_LOG(_args) \
+ MOZ_LOG(LayerManager::GetLog(), LogLevel::Debug, _args)
+#define MOZ_LAYERS_LOG_IF_SHADOWABLE(layer, _args) \
+ do { if (layer->AsShadowableLayer()) { MOZ_LOG(LayerManager::GetLog(), LogLevel::Debug, _args); } } while (0)
+
+#define INVALID_OVERLAY -1
+
+namespace android {
+class MOZ_EXPORT GraphicBuffer;
+} // namespace android
+
+namespace mozilla {
+namespace layers {
+
+class TextureHost;
+
+#undef NONE
+#undef OPAQUE
+
+enum class LayersBackend : int8_t {
+ LAYERS_NONE = 0,
+ LAYERS_BASIC,
+ LAYERS_OPENGL,
+ LAYERS_D3D9,
+ LAYERS_D3D11,
+ LAYERS_CLIENT,
+ LAYERS_LAST
+};
+
+enum class BufferMode : int8_t {
+ BUFFER_NONE,
+ BUFFERED
+};
+
+enum class DrawRegionClip : int8_t {
+ DRAW,
+ NONE
+};
+
+enum class SurfaceMode : int8_t {
+ SURFACE_NONE = 0,
+ SURFACE_OPAQUE,
+ SURFACE_SINGLE_CHANNEL_ALPHA,
+ SURFACE_COMPONENT_ALPHA
+};
+
+// LayerRenderState for Composer2D
+// We currently only support Composer2D using gralloc. If we want to be backed
+// by other surfaces we will need a more generic LayerRenderState.
+enum class LayerRenderStateFlags : int8_t {
+ LAYER_RENDER_STATE_DEFAULT = 0,
+ ORIGIN_BOTTOM_LEFT = 1 << 0,
+ BUFFER_ROTATION = 1 << 1,
+ // Notify Composer2D to swap the RB pixels of gralloc buffer
+ FORMAT_RB_SWAP = 1 << 2,
+ // We record opaqueness here alongside the actual surface we're going to
+ // render. This avoids confusion when a layer might return different kinds
+ // of surfaces over time (e.g. video frames).
+ OPAQUE = 1 << 3
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LayerRenderStateFlags)
+
+struct LayerRenderState {
+ // Constructors and destructor are defined in LayersTypes.cpp so we don't
+ // have to pull in a definition for GraphicBuffer.h here. In KK at least,
+ // that results in nasty pollution such as libui's hardware.h #defining
+ // 'version_major' and 'version_minor' which conflict with Theora's codec.c...
+ LayerRenderState();
+ LayerRenderState(const LayerRenderState& aOther);
+ ~LayerRenderState();
+
+ void SetOffset(const nsIntPoint& aOffset)
+ {
+ mOffset = aOffset;
+ mHasOwnOffset = true;
+ }
+
+ // see LayerRenderStateFlags
+ LayerRenderStateFlags mFlags;
+ // true if mOffset is applicable
+ bool mHasOwnOffset;
+ // the location of the layer's origin on mSurface
+ nsIntPoint mOffset;
+};
+
+enum class ScaleMode : int8_t {
+ SCALE_NONE,
+ STRETCH,
+ SENTINEL
+// Unimplemented - PRESERVE_ASPECT_RATIO_CONTAIN
+};
+
+struct EventRegions {
+ // The hit region for a layer contains all areas on the layer that are
+ // sensitive to events. This region is an over-approximation and may
+ // contain regions that are not actually sensitive, but any such regions
+ // will be included in the mDispatchToContentHitRegion.
+ nsIntRegion mHitRegion;
+ // The mDispatchToContentHitRegion for a layer contains all areas for
+ // which the main-thread must be consulted before responding to events.
+ // This region will be a subregion of mHitRegion.
+ nsIntRegion mDispatchToContentHitRegion;
+
+ // The following regions represent the touch-action areas of this layer.
+ // All of these regions are approximations to the true region, but any
+ // variance between the approximation and the true region is guaranteed
+ // to be included in the mDispatchToContentHitRegion.
+ nsIntRegion mNoActionRegion;
+ nsIntRegion mHorizontalPanRegion;
+ nsIntRegion mVerticalPanRegion;
+
+ EventRegions()
+ {
+ }
+
+ explicit EventRegions(nsIntRegion aHitRegion)
+ : mHitRegion(aHitRegion)
+ {
+ }
+
+ bool operator==(const EventRegions& aRegions) const
+ {
+ return mHitRegion == aRegions.mHitRegion &&
+ mDispatchToContentHitRegion == aRegions.mDispatchToContentHitRegion &&
+ mNoActionRegion == aRegions.mNoActionRegion &&
+ mHorizontalPanRegion == aRegions.mHorizontalPanRegion &&
+ mVerticalPanRegion == aRegions.mVerticalPanRegion;
+ }
+ bool operator!=(const EventRegions& aRegions) const
+ {
+ return !(*this == aRegions);
+ }
+
+ void ApplyTranslationAndScale(float aXTrans, float aYTrans, float aXScale, float aYScale)
+ {
+ mHitRegion.ScaleRoundOut(aXScale, aYScale);
+ mDispatchToContentHitRegion.ScaleRoundOut(aXScale, aYScale);
+ mNoActionRegion.ScaleRoundOut(aXScale, aYScale);
+ mHorizontalPanRegion.ScaleRoundOut(aXScale, aYScale);
+ mVerticalPanRegion.ScaleRoundOut(aXScale, aYScale);
+
+ mHitRegion.MoveBy(aXTrans, aYTrans);
+ mDispatchToContentHitRegion.MoveBy(aXTrans, aYTrans);
+ mNoActionRegion.MoveBy(aXTrans, aYTrans);
+ mHorizontalPanRegion.MoveBy(aXTrans, aYTrans);
+ mVerticalPanRegion.MoveBy(aXTrans, aYTrans);
+ }
+
+ void Transform(const gfx::Matrix4x4& aTransform)
+ {
+ mHitRegion.Transform(aTransform);
+ mDispatchToContentHitRegion.Transform(aTransform);
+ mNoActionRegion.Transform(aTransform);
+ mHorizontalPanRegion.Transform(aTransform);
+ mVerticalPanRegion.Transform(aTransform);
+ }
+
+ bool IsEmpty() const
+ {
+ return mHitRegion.IsEmpty()
+ && mDispatchToContentHitRegion.IsEmpty()
+ && mNoActionRegion.IsEmpty()
+ && mHorizontalPanRegion.IsEmpty()
+ && mVerticalPanRegion.IsEmpty();
+ }
+
+ nsCString ToString() const
+ {
+ nsCString result = mHitRegion.ToString();
+ result.AppendLiteral(";dispatchToContent=");
+ result.Append(mDispatchToContentHitRegion.ToString());
+ return result;
+ }
+};
+
+// Bit flags that go on a ContainerLayer (or RefLayer) and override the
+// event regions in the entire subtree below. This is needed for propagating
+// various flags across processes since the child-process layout code doesn't
+// know about parent-process listeners or CSS rules.
+enum EventRegionsOverride {
+ // The default, no flags set
+ NoOverride = 0,
+ // Treat all hit regions in the subtree as dispatch-to-content
+ ForceDispatchToContent = (1 << 0),
+ // Treat all hit regions in the subtree as empty
+ ForceEmptyHitRegion = (1 << 1),
+ // OR union of all valid bit flags, for use in BitFlagsEnumSerializer
+ ALL_BITS = (1 << 2) - 1
+};
+
+MOZ_ALWAYS_INLINE EventRegionsOverride
+operator|(EventRegionsOverride a, EventRegionsOverride b)
+{
+ return (EventRegionsOverride)((int)a | (int)b);
+}
+
+MOZ_ALWAYS_INLINE EventRegionsOverride&
+operator|=(EventRegionsOverride& a, EventRegionsOverride b)
+{
+ a = a | b;
+ return a;
+}
+
+// Flags used as an argument to functions that dump textures.
+enum TextureDumpMode {
+ Compress, // dump texture with LZ4 compression
+ DoNotCompress // dump texture uncompressed
+};
+
+// Some specialized typedefs of Matrix4x4Typed.
+typedef gfx::Matrix4x4Typed<LayerPixel, CSSTransformedLayerPixel> CSSTransformMatrix;
+// Several different async transforms can contribute to a layer's transform
+// (specifically, an async animation can contribute a transform, and each APZC
+// that scrolls a layer can contribute async scroll/zoom and overscroll
+// transforms).
+// To try to model this with typed units, we represent individual async
+// transforms as ParentLayer -> ParentLayer transforms (aliased as
+// AsyncTransformComponentMatrix), and we represent the product of all of them
+// as a CSSTransformLayer -> ParentLayer transform (aliased as
+// AsyncTransformMatrix). To create an AsyncTransformMatrix from component
+// matrices, a ViewAs operation is needed. A MultipleAsyncTransforms
+// PixelCastJustification is provided for this purpose.
+typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> AsyncTransformComponentMatrix;
+typedef gfx::Matrix4x4Typed<CSSTransformedLayerPixel, ParentLayerPixel> AsyncTransformMatrix;
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LAYERSTYPES_H */
diff --git a/gfx/layers/MacIOSurfaceHelpers.cpp b/gfx/layers/MacIOSurfaceHelpers.cpp
new file mode 100644
index 000000000..4ca32427a
--- /dev/null
+++ b/gfx/layers/MacIOSurfaceHelpers.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "libyuv.h"
+#include "MacIOSurfaceHelpers.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "YCbCrUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+#define ALIGNED_32(x) ((x+31)&~31)
+#define ALIGNEDPTR_32(x) reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(x)+31)&~31)
+
+static already_AddRefed<SourceSurface>
+CreateSourceSurfaceFromLockedMacIOSurface(MacIOSurface* aSurface)
+{
+ size_t bytesPerRow = aSurface->GetBytesPerRow();
+ size_t ioWidth = aSurface->GetDevicePixelWidth();
+ size_t ioHeight = aSurface->GetDevicePixelHeight();
+ SurfaceFormat ioFormat = aSurface->GetFormat();
+
+ if ((ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422) &&
+ (ioWidth > PlanarYCbCrImage::MAX_DIMENSION ||
+ ioHeight > PlanarYCbCrImage::MAX_DIMENSION)) {
+ return nullptr;
+ }
+
+ SurfaceFormat format =
+ (ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+
+ RefPtr<DataSourceSurface> dataSurface =
+ Factory::CreateDataSourceSurface(IntSize::Truncate(ioWidth, ioHeight), format);
+ if (NS_WARN_IF(!dataSurface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface mappedSurface;
+ if (!dataSurface->Map(DataSourceSurface::WRITE, &mappedSurface)) {
+ return nullptr;
+ }
+
+ if (ioFormat == SurfaceFormat::NV12) {
+ /* Extract and separate the CbCr planes */
+ size_t cbCrStride = aSurface->GetBytesPerRow(1);
+ size_t cbCrWidth = aSurface->GetDevicePixelWidth(1);
+ size_t cbCrHeight = aSurface->GetDevicePixelHeight(1);
+
+ auto cbPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight);
+ auto crPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight);
+
+ uint8_t* src = (uint8_t*)aSurface->GetBaseAddressOfPlane(1);
+ uint8_t* cbDest = cbPlane.get();
+ uint8_t* crDest = crPlane.get();
+
+ for (size_t i = 0; i < cbCrHeight; i++) {
+ uint8_t* rowSrc = src + cbCrStride * i;
+ for (size_t j = 0; j < cbCrWidth; j++) {
+ *cbDest = *rowSrc;
+ cbDest++;
+ rowSrc++;
+ *crDest = *rowSrc;
+ crDest++;
+ rowSrc++;
+ }
+ }
+
+ /* Convert to RGB */
+ PlanarYCbCrData data;
+ data.mYChannel = (uint8_t*)aSurface->GetBaseAddressOfPlane(0);
+ data.mYStride = aSurface->GetBytesPerRow(0);
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
+ data.mCbChannel = cbPlane.get();
+ data.mCrChannel = crPlane.get();
+ data.mCbCrStride = cbCrWidth;
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
+ data.mPicSize = data.mYSize;
+
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ } else if (ioFormat == SurfaceFormat::YUV422) {
+ if (ioWidth == ALIGNED_32(ioWidth)) {
+ // Optimization when width is aligned to 32.
+ IntSize size = IntSize::Truncate(ioWidth, ioHeight);
+ libyuv::ConvertToARGB((uint8_t*)aSurface->GetBaseAddress(), 0 /* not used */,
+ mappedSurface.mData, mappedSurface.mStride,
+ 0, 0,
+ size.width, size.height,
+ size.width, size.height,
+ libyuv::kRotate0, libyuv::FOURCC_UYVY);
+ } else {
+ /* Convert to YV16 */
+ size_t cbCrWidth = (ioWidth+1)>>1;
+ size_t cbCrHeight = ioHeight;
+ // Ensure our stride is a multiple of 32 to allow for memory aligned rows.
+ size_t cbCrStride = ALIGNED_32(cbCrWidth);
+ size_t strideDelta = cbCrStride - cbCrWidth;
+ MOZ_ASSERT(strideDelta <= 31);
+
+ auto yPlane = MakeUnique<uint8_t[]>(cbCrStride * 2 * ioHeight + 31);
+ auto cbPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31);
+ auto crPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31);
+
+ uint8_t* src = (uint8_t*)aSurface->GetBaseAddress();
+ uint8_t* yDest = ALIGNEDPTR_32(yPlane.get());
+ uint8_t* cbDest = ALIGNEDPTR_32(cbPlane.get());
+ uint8_t* crDest = ALIGNEDPTR_32(crPlane.get());
+
+ for (size_t i = 0; i < ioHeight; i++) {
+ uint8_t* rowSrc = src + bytesPerRow * i;
+ for (size_t j = 0; j < cbCrWidth; j++) {
+ *cbDest = *rowSrc;
+ cbDest++;
+ rowSrc++;
+ *yDest = *rowSrc;
+ yDest++;
+ rowSrc++;
+ *crDest = *rowSrc;
+ crDest++;
+ rowSrc++;
+ *yDest = *rowSrc;
+ yDest++;
+ rowSrc++;
+ }
+ if (strideDelta) {
+ cbDest += strideDelta;
+ crDest += strideDelta;
+ yDest += strideDelta << 1;
+ }
+ }
+
+ /* Convert to RGB */
+ PlanarYCbCrData data;
+ data.mYChannel = ALIGNEDPTR_32(yPlane.get());
+ data.mYStride = cbCrStride * 2;
+ data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
+ data.mCbChannel = ALIGNEDPTR_32(cbPlane.get());
+ data.mCrChannel = ALIGNEDPTR_32(crPlane.get());
+ data.mCbCrStride = cbCrStride;
+ data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
+ data.mPicSize = data.mYSize;
+
+ ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+ }
+ } else {
+ unsigned char* ioData = (unsigned char*)aSurface->GetBaseAddress();
+
+ for (size_t i = 0; i < ioHeight; ++i) {
+ memcpy(mappedSurface.mData + i * mappedSurface.mStride,
+ ioData + i * bytesPerRow,
+ ioWidth * 4);
+ }
+ }
+
+ dataSurface->Unmap();
+
+ return dataSurface.forget();
+}
+
+already_AddRefed<SourceSurface>
+CreateSourceSurfaceFromMacIOSurface(MacIOSurface* aSurface)
+{
+ aSurface->Lock();
+ RefPtr<SourceSurface> result = CreateSourceSurfaceFromLockedMacIOSurface(aSurface);
+ aSurface->Unlock();
+ return result.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/MacIOSurfaceHelpers.h b/gfx/layers/MacIOSurfaceHelpers.h
new file mode 100644
index 000000000..80f9cf872
--- /dev/null
+++ b/gfx/layers/MacIOSurfaceHelpers.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MACIOSURFACEHELPERS_H
+#define GFX_MACIOSURFACEHELPERS_H
+
+class MacIOSurface;
+template<class T> struct already_AddRefed;
+
+namespace mozilla {
+
+namespace gfx {
+class SourceSurface;
+}
+
+namespace layers {
+
+// Unlike MacIOSurface::GetAsSurface, this also handles IOSurface formats
+// with multiple planes and does YCbCr to RGB conversion, if necessary.
+already_AddRefed<gfx::SourceSurface>
+CreateSourceSurfaceFromMacIOSurface(MacIOSurface* aSurface);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_MACIOSURFACEHELPERS_H
diff --git a/gfx/layers/MacIOSurfaceImage.cpp b/gfx/layers/MacIOSurfaceImage.cpp
new file mode 100644
index 000000000..75fb1f2d6
--- /dev/null
+++ b/gfx/layers/MacIOSurfaceImage.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurfaceHelpers.h"
+#include "MacIOSurfaceImage.h"
+#include "gfxPlatform.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+TextureClient*
+MacIOSurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (!mTextureClient) {
+ BackendType backend = BackendType::NONE;
+ mTextureClient = TextureClient::CreateWithData(
+ MacIOSurfaceTextureData::Create(mSurface, backend),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+ }
+ return mTextureClient;
+}
+
+already_AddRefed<SourceSurface>
+MacIOSurfaceImage::GetAsSourceSurface()
+{
+ return CreateSourceSurfaceFromMacIOSurface(mSurface);
+}
diff --git a/gfx/layers/MacIOSurfaceImage.h b/gfx/layers/MacIOSurfaceImage.h
new file mode 100644
index 000000000..769d63ef4
--- /dev/null
+++ b/gfx/layers/MacIOSurfaceImage.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_MACIOSURFACEIMAGE_H
+#define GFX_MACIOSURFACEIMAGE_H
+
+#include "ImageContainer.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class MacIOSurfaceImage : public Image {
+public:
+ explicit MacIOSurfaceImage(MacIOSurface* aSurface)
+ : Image(nullptr, ImageFormat::MAC_IOSURFACE),
+ mSurface(aSurface)
+ {}
+
+ MacIOSurface* GetSurface() { return mSurface; }
+
+ gfx::IntSize GetSize() override {
+ return gfx::IntSize::Truncate(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+ }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override {
+ return this;
+ }
+
+private:
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_SHAREDTEXTUREIMAGE_H
diff --git a/gfx/layers/PersistentBufferProvider.cpp b/gfx/layers/PersistentBufferProvider.cpp
new file mode 100644
index 000000000..d6269257f
--- /dev/null
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PersistentBufferProvider.h"
+
+#include "Layers.h"
+#include "mozilla/gfx/Logging.h"
+#include "pratom.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
+: mDrawTarget(aDt)
+{
+ MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
+}
+
+PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
+{
+ MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+ MOZ_ASSERT(!mSnapshot);
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+}
+
+bool
+PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+ RefPtr<gfx::DrawTarget> dt(aDT);
+ MOZ_ASSERT(mDrawTarget == dt);
+ if (dt) {
+ // Since SkiaGL default to storing drawing command until flush
+ // we have to flush it before present.
+ dt->Flush();
+ }
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderBasic::BorrowSnapshot()
+{
+ mSnapshot = mDrawTarget->Snapshot();
+ RefPtr<SourceSurface> snapshot = mSnapshot;
+ return snapshot.forget();
+}
+
+void
+PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+ RefPtr<SourceSurface> snapshot = aSnapshot;
+ MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+ mSnapshot = nullptr;
+}
+
+//static
+already_AddRefed<PersistentBufferProviderBasic>
+PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend)
+{
+ RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+
+ if (!dt) {
+ return nullptr;
+ }
+
+ RefPtr<PersistentBufferProviderBasic> provider =
+ new PersistentBufferProviderBasic(dt);
+
+ return provider.forget();
+}
+
+
+//static
+already_AddRefed<PersistentBufferProviderShared>
+PersistentBufferProviderShared::Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd)
+{
+ if (!aFwd || !aFwd->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
+ aFwd, aFormat, aSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<PersistentBufferProviderShared> provider =
+ new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
+ return provider.forget();
+}
+
+PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd,
+ RefPtr<TextureClient>& aTexture)
+
+: mSize(aSize)
+, mFormat(aFormat)
+, mFwd(aFwd)
+, mFront(Nothing())
+{
+ if (mTextures.append(aTexture)) {
+ mBack = Some<uint32_t>(0);
+ }
+ MOZ_COUNT_CTOR(PersistentBufferProviderShared);
+}
+
+PersistentBufferProviderShared::~PersistentBufferProviderShared()
+{
+ MOZ_COUNT_DTOR(PersistentBufferProviderShared);
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().RemoveObject(this);
+ }
+
+ Destroy();
+}
+
+bool
+PersistentBufferProviderShared::SetForwarder(ShadowLayerForwarder* aFwd)
+{
+ MOZ_ASSERT(aFwd);
+ if (!aFwd) {
+ return false;
+ }
+
+ if (mFwd == aFwd) {
+ // The forwarder should not change most of the time.
+ return true;
+ }
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().RemoveObject(this);
+ }
+
+ if (mFwd->GetTextureForwarder() != aFwd->GetTextureForwarder() ||
+ mFwd->GetCompositorBackendType() != aFwd->GetCompositorBackendType()) {
+ // We are going to be used with an different and/or incompatible forwarder.
+ // This should be extremely rare. We have to copy the front buffer into a
+ // texture that is compatible with the new forwarder.
+
+ // Grab the current front buffer.
+ RefPtr<TextureClient> prevTexture = GetTexture(mFront);
+
+ // Get rid of everything else
+ Destroy();
+
+ if (prevTexture) {
+ RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+ aFwd, mFormat, mSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ MOZ_ASSERT(newTexture);
+ if (!newTexture) {
+ return false;
+ }
+
+ // If we early-return in one of the following branches, we will
+ // leave the buffer provider in an empty state, since we called
+ // Destroy. Not ideal but at least we won't try to use it with a
+ // an incompatible ipc channel.
+
+ if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
+ return false;
+ }
+
+ if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
+ newTexture->Unlock();
+ return false;
+ }
+
+ bool success = prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
+
+ prevTexture->Unlock();
+ newTexture->Unlock();
+
+ if (!success) {
+ return false;
+ }
+
+ if (!mTextures.append(newTexture)) {
+ return false;
+ }
+ mFront = Some<uint32_t>(mTextures.length() - 1);
+ mBack = mFront;
+ }
+ }
+
+ mFwd = aFwd;
+
+ return true;
+}
+
+TextureClient*
+PersistentBufferProviderShared::GetTexture(Maybe<uint32_t> aIndex)
+{
+ if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
+ return nullptr;
+ }
+ return mTextures[aIndex.value()];
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+ if (!mFwd->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!mSnapshot);
+
+ if (IsActivityTracked()) {
+ mFwd->GetActiveResourceTracker().MarkUsed(this);
+ } else {
+ mFwd->GetActiveResourceTracker().AddObject(this);
+ }
+
+ if (mDrawTarget) {
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+ }
+
+ mFront = Nothing();
+
+ auto previousBackBuffer = mBack;
+
+ TextureClient* tex = GetTexture(mBack);
+
+ // First try to reuse the current back buffer. If we can do that it means
+ // we can skip copying its content to the new back buffer.
+ if (tex && tex->IsReadLocked()) {
+ // The back buffer is currently used by the compositor, we can't draw
+ // into it.
+ tex = nullptr;
+ }
+
+ if (!tex) {
+ // Try to grab an already allocated texture if any is available.
+ for (uint32_t i = 0; i < mTextures.length(); ++i) {
+ if (!mTextures[i]->IsReadLocked()) {
+ mBack = Some(i);
+ tex = mTextures[i];
+ break;
+ }
+ }
+ }
+
+ if (!tex) {
+ // We have to allocate a new texture.
+ if (mTextures.length() >= 4) {
+ // We should never need to buffer that many textures, something's wrong.
+ MOZ_ASSERT(false);
+ // In theory we throttle the main thread when the compositor can't keep up,
+ // so we shoud never get in a situation where we sent 4 textures to the
+ // compositor and the latter as not released any of them.
+ // This seems to happen, however, in some edge cases such as just after a
+ // device reset (cf. Bug 1291163).
+ // It would be pretty bad to keep piling textures up at this point so we
+ // call NotifyInactive to remove some of our textures.
+ NotifyInactive();
+ // Give up now. The caller can fall-back to a non-shared buffer provider.
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
+ mFwd, mFormat, mSize,
+ BackendSelector::Canvas,
+ TextureFlags::DEFAULT,
+ TextureAllocationFlags::ALLOC_DEFAULT
+ );
+
+ MOZ_ASSERT(newTexture);
+ if (newTexture) {
+ if (mTextures.append(newTexture)) {
+ tex = newTexture;
+ mBack = Some<uint32_t>(mTextures.length() - 1);
+ }
+ }
+ }
+
+ if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
+ return nullptr;
+ }
+
+ if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
+ TextureClient* previous = GetTexture(previousBackBuffer);
+ if (previous && previous->Lock(OpenMode::OPEN_READ)) {
+ DebugOnly<bool> success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
+ MOZ_ASSERT(success);
+
+ previous->Unlock();
+ }
+ }
+
+ mDrawTarget = tex->BorrowDrawTarget();
+
+ RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+ return dt.forget();
+}
+
+bool
+PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+ RefPtr<gfx::DrawTarget> dt(aDT);
+ MOZ_ASSERT(mDrawTarget == dt);
+ // Can't change the current front buffer while its snapshot is borrowed!
+ MOZ_ASSERT(!mSnapshot);
+
+ mDrawTarget = nullptr;
+ dt = nullptr;
+
+ TextureClient* back = GetTexture(mBack);
+ MOZ_ASSERT(back);
+
+ if (back) {
+ back->Unlock();
+ mFront = mBack;
+ }
+
+ return !!back;
+}
+
+TextureClient*
+PersistentBufferProviderShared::GetTextureClient()
+{
+ // Can't access the front buffer while drawing.
+ MOZ_ASSERT(!mDrawTarget);
+ TextureClient* texture = GetTexture(mFront);
+ if (texture) {
+ texture->EnableReadLock();
+ } else {
+ gfxCriticalNote << "PersistentBufferProviderShared: front buffer unavailable";
+ }
+ return texture;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderShared::BorrowSnapshot()
+{
+ MOZ_ASSERT(!mDrawTarget);
+
+ auto front = GetTexture(mFront);
+ if (!front || front->IsLocked()) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ if (!front->Lock(OpenMode::OPEN_READ)) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt = front->BorrowDrawTarget();
+
+ if (!dt) {
+ front->Unlock();
+ return nullptr;
+ }
+
+ mSnapshot = dt->Snapshot();
+
+ RefPtr<SourceSurface> snapshot = mSnapshot;
+ return snapshot.forget();
+}
+
+void
+PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+ RefPtr<SourceSurface> snapshot = aSnapshot;
+ MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+
+ mSnapshot = nullptr;
+ snapshot = nullptr;
+
+ auto front = GetTexture(mFront);
+ if (front) {
+ front->Unlock();
+ }
+}
+
+void
+PersistentBufferProviderShared::NotifyInactive()
+{
+ RefPtr<TextureClient> front = GetTexture(mFront);
+ RefPtr<TextureClient> back = GetTexture(mBack);
+
+ // Clear all textures (except the front and back ones that we just kept).
+ mTextures.clear();
+
+ if (back) {
+ if (mTextures.append(back)) {
+ mBack = Some<uint32_t>(0);
+ }
+ if (front == back) {
+ mFront = mBack;
+ }
+ }
+
+ if (front && front != back) {
+ if (mTextures.append(front)) {
+ mFront = Some<uint32_t>(mTextures.length() - 1);
+ }
+ }
+}
+
+void
+PersistentBufferProviderShared::Destroy()
+{
+ mSnapshot = nullptr;
+ mDrawTarget = nullptr;
+
+ for (uint32_t i = 0; i < mTextures.length(); ++i) {
+ TextureClient* texture = mTextures[i];
+ if (texture && texture->IsLocked()) {
+ MOZ_ASSERT(false);
+ texture->Unlock();
+ }
+ }
+
+ mTextures.clear();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/PersistentBufferProvider.h b/gfx/layers/PersistentBufferProvider.h
new file mode 100644
index 000000000..1a1355ca9
--- /dev/null
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_PersistentBUFFERPROVIDER_H
+#define MOZILLA_GFX_PersistentBUFFERPROVIDER_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+
+namespace gfx {
+ class SourceSurface;
+ class DrawTarget;
+}
+
+namespace layers {
+
+class CopyableCanvasLayer;
+
+/**
+ * A PersistentBufferProvider is for users which require the temporary use of
+ * a DrawTarget to draw into. When they're done drawing they return the
+ * DrawTarget, when they later need to continue drawing they get a DrawTarget
+ * from the provider again, the provider will guarantee the contents of the
+ * previously returned DrawTarget is persisted into the one newly returned.
+ */
+class PersistentBufferProvider : public RefCounted<PersistentBufferProvider>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProvider)
+
+ virtual ~PersistentBufferProvider() { }
+
+ virtual LayersBackend GetType() { return LayersBackend::LAYERS_NONE; }
+
+ /**
+ * Get a DrawTarget from the PersistentBufferProvider.
+ *
+ * \param aPersistedRect This indicates the area of the DrawTarget that needs
+ * to have remained the same since the call to
+ * ReturnDrawTarget.
+ */
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) = 0;
+
+ /**
+ * Return a DrawTarget to the PersistentBufferProvider and indicate the
+ * contents of this DrawTarget is to be considered current by the
+ * BufferProvider. The caller should forget any references to the DrawTarget.
+ */
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
+
+ virtual TextureClient* GetTextureClient() { return nullptr; }
+
+ virtual void OnShutdown() {}
+
+ virtual bool SetForwarder(ShadowLayerForwarder* aFwd) { return true; }
+
+ /**
+ * Return true if this provider preserves the drawing state (clips, transforms,
+ * etc.) across frames. In practice this means users of the provider can skip
+ * popping all of the clips at the end of the frames and pushing them back at
+ * the beginning of the following frames, which can be costly (cf. bug 1294351).
+ */
+ virtual bool PreservesDrawingState() const = 0;
+};
+
+
+class PersistentBufferProviderBasic : public PersistentBufferProvider
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
+
+ static already_AddRefed<PersistentBufferProviderBasic>
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend);
+
+ explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget);
+
+ virtual LayersBackend GetType() override { return LayersBackend::LAYERS_BASIC; }
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+ virtual bool PreservesDrawingState() const override { return true; }
+private:
+ ~PersistentBufferProviderBasic();
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RefPtr<gfx::SourceSurface> mSnapshot;
+};
+
+
+/**
+ * Provides access to a buffer which can be sent to the compositor without
+ * requiring a copy.
+ */
+class PersistentBufferProviderShared : public PersistentBufferProvider
+ , public ActiveResource
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
+
+ static already_AddRefed<PersistentBufferProviderShared>
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd);
+
+ virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; }
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+ virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+ virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+ virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+ virtual TextureClient* GetTextureClient() override;
+
+ virtual void NotifyInactive() override;
+
+ virtual void OnShutdown() override { Destroy(); }
+
+ virtual bool SetForwarder(ShadowLayerForwarder* aFwd) override;
+
+ virtual bool PreservesDrawingState() const override { return false; }
+protected:
+ PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ ShadowLayerForwarder* aFwd,
+ RefPtr<TextureClient>& aTexture);
+
+ ~PersistentBufferProviderShared();
+
+ TextureClient* GetTexture(Maybe<uint32_t> aIndex);
+ bool CheckIndex(uint32_t aIndex) { return aIndex < mTextures.length(); }
+
+ void Destroy();
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<ShadowLayerForwarder> mFwd;
+ Vector<RefPtr<TextureClient>, 4> mTextures;
+ // Offset of the texture in mTextures that the canvas uses.
+ Maybe<uint32_t> mBack;
+ // Offset of the texture in mTextures that is presented to the compositor.
+ Maybe<uint32_t> mFront;
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RefPtr<gfx::SourceSurface > mSnapshot;
+};
+
+struct AutoReturnSnapshot
+{
+ PersistentBufferProvider* mBufferProvider;
+ RefPtr<gfx::SourceSurface>* mSnapshot;
+
+ explicit AutoReturnSnapshot(PersistentBufferProvider* aProvider = nullptr)
+ : mBufferProvider(aProvider)
+ , mSnapshot(nullptr)
+ {}
+
+ ~AutoReturnSnapshot()
+ {
+ if (mBufferProvider) {
+ mBufferProvider->ReturnSnapshot(mSnapshot ? mSnapshot->forget() : nullptr);
+ }
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ReadbackLayer.h b/gfx/layers/ReadbackLayer.h
new file mode 100644
index 000000000..4f7059110
--- /dev/null
+++ b/gfx/layers/ReadbackLayer.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKLAYER_H
+#define GFX_READBACKLAYER_H
+
+#include <stdint.h> // for uint64_t
+#include "Layers.h" // for Layer, etc
+#include "mozilla/gfx/Rect.h" // for gfxRect
+#include "mozilla/gfx/Point.h" // for IntPoint
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAutoPtr.h" // for nsAutoPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nscore.h" // for nsACString
+
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+namespace layerscope {
+class LayersPacket;
+} // namespace layerscope
+
+/**
+ * A ReadbackSink receives a stream of updates to a rectangle of pixels.
+ * These update callbacks are always called on the main thread, either during
+ * EndTransaction or from the event loop.
+ */
+class ReadbackSink {
+public:
+ ReadbackSink() {}
+ virtual ~ReadbackSink() {}
+
+ /**
+ * Sends an update to indicate that the background is currently unknown.
+ */
+ virtual void SetUnknown(uint64_t aSequenceNumber) = 0;
+ /**
+ * Called by the layer system to indicate that the contents of part of
+ * the readback area are changing.
+ * @param aRect is the rectangle of content that is being updated,
+ * in the coordinate system of the ReadbackLayer.
+ * @param aSequenceNumber updates issued out of order should be ignored.
+ * Only use updates whose sequence counter is greater than all other updates
+ * seen so far. Return null when a non-fresh sequence value is given.
+ * @return a context into which the update should be drawn. This should be
+ * set up to clip to aRect. Zero should never be passed as a sequence number.
+ * If this returns null, EndUpdate should NOT be called. If it returns
+ * non-null, EndUpdate must be called.
+ *
+ * We don't support partially unknown backgrounds. Therefore, the
+ * first BeginUpdate after a SetUnknown will have the complete background.
+ */
+ virtual already_AddRefed<gfx::DrawTarget>
+ BeginUpdate(const gfx::IntRect& aRect, uint64_t aSequenceNumber) = 0;
+ /**
+ * EndUpdate must be called immediately after BeginUpdate, without returning
+ * to the event loop.
+ * @param aContext the context returned by BeginUpdate
+ * Implicitly Restore()s the state of aContext.
+ */
+ virtual void EndUpdate(const gfx::IntRect& aRect) = 0;
+};
+
+/**
+ * A ReadbackLayer never renders anything. It enables clients to extract
+ * the rendered contents of the layer tree below the ReadbackLayer.
+ * The rendered contents are delivered asynchronously via calls to a
+ * ReadbackSink object supplied by the client.
+ *
+ * This is a "best effort" API; it is possible for the layer system to tell
+ * the ReadbackSink that the contents of the readback area are unknown.
+ *
+ * This API exists to work around the limitations of transparent windowless
+ * plugin rendering APIs. It should not be used for anything else.
+ */
+class ReadbackLayer : public Layer {
+public:
+ MOZ_LAYER_DECL_NAME("ReadbackLayer", TYPE_READBACK)
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(GetLocalTransform(), gfxRect(0, 0, mSize.width, mSize.height),
+ nullptr)*
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the callback object to which readback updates will be delivered.
+ * This also resets the "needed rectangle" so that on the next layer tree
+ * transaction we will try to deliver the full contents of the readback
+ * area to the sink.
+ * This layer takes ownership of the sink. It will be deleted when the
+ * layer is destroyed or when a new sink is set.
+ * Initially the contents of the readback area are completely unknown.
+ */
+ void SetSink(ReadbackSink* aSink)
+ {
+ SetUnknown();
+ mSink = aSink;
+ }
+ ReadbackSink* GetSink() { return mSink; }
+
+ /**
+ * CONSTRUCTION PHASE ONLY
+ * Set the size of content that should be read back. The readback area
+ * has its top-left at 0,0 and has size aSize.
+ * Can only be called while the sink is null!
+ */
+ void SetSize(const gfx::IntSize& aSize)
+ {
+ NS_ASSERTION(!mSink, "Should have no sink while changing size!");
+ mSize = aSize;
+ }
+ const gfx::IntSize& GetSize() { return mSize; }
+ gfx::IntRect GetRect() { return gfx::IntRect(gfx::IntPoint(0, 0), mSize); }
+
+ bool IsBackgroundKnown()
+ {
+ return mBackgroundLayer || mBackgroundColor.a == 1.f;
+ }
+
+ void NotifyRemoved() {
+ SetUnknown();
+ mSink = nullptr;
+ }
+
+ void NotifyPaintedLayerRemoved(PaintedLayer* aLayer)
+ {
+ if (mBackgroundLayer == aLayer) {
+ mBackgroundLayer = nullptr;
+ }
+ }
+
+ const nsIntPoint& GetBackgroundLayerOffset() { return mBackgroundLayerOffset; }
+
+ uint64_t AllocateSequenceNumber() { return ++mSequenceCounter; }
+
+ void SetUnknown()
+ {
+ if (IsBackgroundKnown()) {
+ if (mSink) {
+ mSink->SetUnknown(AllocateSequenceNumber());
+ }
+ mBackgroundLayer = nullptr;
+ mBackgroundColor = gfx::Color();
+ }
+ }
+
+protected:
+ friend class ReadbackProcessor;
+
+ ReadbackLayer(LayerManager* aManager, void* aImplData) :
+ Layer(aManager, aImplData),
+ mSequenceCounter(0),
+ mSize(0,0),
+ mBackgroundLayer(nullptr),
+ mBackgroundLayerOffset(0, 0),
+ mBackgroundColor(gfx::Color())
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+ uint64_t mSequenceCounter;
+ nsAutoPtr<ReadbackSink> mSink;
+ gfx::IntSize mSize;
+
+ // This can refer to any (earlier) sibling PaintedLayer. That PaintedLayer
+ // must have mUsedForReadback set on it. If the PaintedLayer is removed
+ // for the container, this will be set to null by NotifyPaintedLayerRemoved.
+ // This PaintedLayer contains the contents which have previously been reported
+ // to mSink. The PaintedLayer had only an integer translation transform,
+ // and it covered the entire readback area. This layer also had only an
+ // integer translation transform.
+ PaintedLayer* mBackgroundLayer;
+ // When mBackgroundLayer is non-null, this is the offset to add to
+ // convert from the coordinates of mBackgroundLayer to the coordinates
+ // of this layer.
+ nsIntPoint mBackgroundLayerOffset;
+ // When mBackgroundColor is opaque, this is the color of the ColorLayer
+ // that contained the contents we reported to mSink, which covered the
+ // entire readback area.
+ gfx::Color mBackgroundColor;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKLAYER_H */
diff --git a/gfx/layers/ReadbackProcessor.cpp b/gfx/layers/ReadbackProcessor.cpp
new file mode 100644
index 000000000..9df789635
--- /dev/null
+++ b/gfx/layers/ReadbackProcessor.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ReadbackProcessor.h"
+#include <sys/types.h> // for int32_t
+#include "Layers.h" // for Layer, PaintedLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "UnitTransforms.h" // for ViewAs
+#include "Units.h" // for ParentLayerIntRect
+#include "gfxContext.h" // for gfxContext
+#include "gfxUtils.h"
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for Intsize
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+void
+ReadbackProcessor::BuildUpdates(ContainerLayer* aContainer)
+{
+ NS_ASSERTION(mAllUpdates.IsEmpty(), "Some updates not processed?");
+
+ if (!aContainer->mMayHaveReadbackChild)
+ return;
+
+ aContainer->mMayHaveReadbackChild = false;
+ // go backwards so the updates read from earlier layers are later in the
+ // array.
+ for (Layer* l = aContainer->GetLastChild(); l; l = l->GetPrevSibling()) {
+ if (l->GetType() == Layer::TYPE_READBACK) {
+ aContainer->mMayHaveReadbackChild = true;
+ BuildUpdatesForLayer(static_cast<ReadbackLayer*>(l));
+ }
+ }
+}
+
+static Layer*
+FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset)
+{
+ gfx::Matrix transform;
+ if (!aLayer->GetTransform().Is2D(&transform) ||
+ transform.HasNonIntegerTranslation())
+ return nullptr;
+ nsIntPoint transformOffset(int32_t(transform._31), int32_t(transform._32));
+
+ for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) {
+ gfx::Matrix backgroundTransform;
+ if (!l->GetTransform().Is2D(&backgroundTransform) ||
+ gfx::ThebesMatrix(backgroundTransform).HasNonIntegerTranslation())
+ return nullptr;
+
+ nsIntPoint backgroundOffset(int32_t(backgroundTransform._31), int32_t(backgroundTransform._32));
+ IntRect rectInBackground(transformOffset - backgroundOffset, aLayer->GetSize());
+ const nsIntRegion visibleRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ if (!visibleRegion.Intersects(rectInBackground))
+ continue;
+ // Since l is present in the background, from here on we either choose l
+ // or nothing.
+ if (!visibleRegion.Contains(rectInBackground))
+ return nullptr;
+
+ if (l->GetEffectiveOpacity() != 1.0 ||
+ l->HasMaskLayers() ||
+ !(l->GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ return nullptr;
+ }
+
+ // cliprects are post-transform
+ const Maybe<ParentLayerIntRect>& clipRect = l->GetLocalClipRect();
+ if (clipRect && !clipRect->Contains(ViewAs<ParentLayerPixel>(IntRect(transformOffset, aLayer->GetSize()))))
+ return nullptr;
+
+ Layer::LayerType type = l->GetType();
+ if (type != Layer::TYPE_COLOR && type != Layer::TYPE_PAINTED)
+ return nullptr;
+
+ *aOffset = backgroundOffset - transformOffset;
+ return l;
+ }
+
+ return nullptr;
+}
+
+void
+ReadbackProcessor::BuildUpdatesForLayer(ReadbackLayer* aLayer)
+{
+ if (!aLayer->mSink)
+ return;
+
+ nsIntPoint offset;
+ Layer* newBackground = FindBackgroundLayer(aLayer, &offset);
+ if (!newBackground) {
+ aLayer->SetUnknown();
+ return;
+ }
+
+ if (newBackground->GetType() == Layer::TYPE_COLOR) {
+ ColorLayer* colorLayer = static_cast<ColorLayer*>(newBackground);
+ if (aLayer->mBackgroundColor != colorLayer->GetColor()) {
+ aLayer->mBackgroundLayer = nullptr;
+ aLayer->mBackgroundColor = colorLayer->GetColor();
+ NS_ASSERTION(aLayer->mBackgroundColor.a == 1.f,
+ "Color layer said it was opaque!");
+ RefPtr<DrawTarget> dt =
+ aLayer->mSink->BeginUpdate(aLayer->GetRect(),
+ aLayer->AllocateSequenceNumber());
+ if (dt) {
+ ColorPattern color(ToDeviceColor(aLayer->mBackgroundColor));
+ IntSize size = aLayer->GetSize();
+ dt->FillRect(Rect(0, 0, size.width, size.height), color);
+ aLayer->mSink->EndUpdate(aLayer->GetRect());
+ }
+ }
+ } else {
+ NS_ASSERTION(newBackground->AsPaintedLayer(), "Must be PaintedLayer");
+ PaintedLayer* paintedLayer = static_cast<PaintedLayer*>(newBackground);
+ // updateRect is relative to the PaintedLayer
+ IntRect updateRect = aLayer->GetRect() - offset;
+ if (paintedLayer != aLayer->mBackgroundLayer ||
+ offset != aLayer->mBackgroundLayerOffset) {
+ aLayer->mBackgroundLayer = paintedLayer;
+ aLayer->mBackgroundLayerOffset = offset;
+ aLayer->mBackgroundColor = Color();
+ paintedLayer->SetUsedForReadback(true);
+ } else {
+ nsIntRegion invalid;
+ invalid.Sub(updateRect, paintedLayer->GetValidRegion());
+ updateRect = invalid.GetBounds();
+ }
+
+ Update update = { aLayer, updateRect, aLayer->AllocateSequenceNumber() };
+ mAllUpdates.AppendElement(update);
+ }
+}
+
+void
+ReadbackProcessor::GetPaintedLayerUpdates(PaintedLayer* aLayer,
+ nsTArray<Update>* aUpdates,
+ nsIntRegion* aUpdateRegion)
+{
+ // All PaintedLayers used for readback are in mAllUpdates (some possibly
+ // with an empty update rect).
+ aLayer->SetUsedForReadback(false);
+ if (aUpdateRegion) {
+ aUpdateRegion->SetEmpty();
+ }
+ for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
+ const Update& update = mAllUpdates[i - 1];
+ if (update.mLayer->mBackgroundLayer == aLayer) {
+ aLayer->SetUsedForReadback(true);
+ // Don't bother asking for updates if we have an empty update rect.
+ if (!update.mUpdateRect.IsEmpty()) {
+ aUpdates->AppendElement(update);
+ if (aUpdateRegion) {
+ aUpdateRegion->Or(*aUpdateRegion, update.mUpdateRect);
+ }
+ }
+ mAllUpdates.RemoveElementAt(i - 1);
+ }
+ }
+}
+
+ReadbackProcessor::~ReadbackProcessor()
+{
+ for (uint32_t i = mAllUpdates.Length(); i > 0; --i) {
+ const Update& update = mAllUpdates[i - 1];
+ // Unprocessed update. Notify the readback sink that this content is
+ // unknown.
+ update.mLayer->SetUnknown();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ReadbackProcessor.h b/gfx/layers/ReadbackProcessor.h
new file mode 100644
index 000000000..b07f796c3
--- /dev/null
+++ b/gfx/layers/ReadbackProcessor.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKPROCESSOR_H
+#define GFX_READBACKPROCESSOR_H
+
+#include <stdint.h> // for uint64_t
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+
+class ContainerLayer;
+class ReadbackLayer;
+class PaintedLayer;
+
+class ReadbackProcessor {
+public:
+ /**
+ * Called by the container before processing any child layers. Call this
+ * if any child layer might have changed in any way (other than content-only
+ * changes to layers other than ColorLayers and PaintedLayers).
+ *
+ * This method recomputes the relationship between ReadbackLayers and
+ * sibling layers, and dispatches changes to ReadbackLayers. Except that
+ * if a PaintedLayer needs its contents sent to some ReadbackLayer, we'll
+ * just record that internally and later the PaintedLayer should call
+ * GetPaintedLayerUpdates when it paints, to find out which rectangle needs
+ * to be sent, and the ReadbackLayer it needs to be sent to.
+ */
+ void BuildUpdates(ContainerLayer* aContainer);
+
+ struct Update {
+ /**
+ * The layer a PaintedLayer should send its contents to.
+ */
+ ReadbackLayer* mLayer;
+ /**
+ * The rectangle of content that it should send, in the PaintedLayer's
+ * coordinate system. This rectangle is guaranteed to be in the PaintedLayer's
+ * visible region. Translate it to mLayer's coordinate system
+ * by adding mLayer->GetBackgroundLayerOffset().
+ */
+ gfx::IntRect mUpdateRect;
+ /**
+ * The sequence counter value to use when calling DoUpdate
+ */
+ uint64_t mSequenceCounter;
+ };
+ /**
+ * Appends any ReadbackLayers that need to be updated, and the rects that
+ * need to be updated, to aUpdates. Only need to call this for PaintedLayers
+ * that have been marked UsedForReadback().
+ * Each Update's mLayer's mBackgroundLayer will have been set to aLayer.
+ * If a PaintedLayer doesn't call GetPaintedLayerUpdates, then all the
+ * ReadbackLayers that needed data from that PaintedLayer will be marked
+ * as having unknown backgrounds.
+ * @param aUpdateRegion if non-null, this region is set to the union
+ * of the mUpdateRects.
+ */
+ void GetPaintedLayerUpdates(PaintedLayer* aLayer,
+ nsTArray<Update>* aUpdates,
+ nsIntRegion* aUpdateRegion = nullptr);
+
+ ~ReadbackProcessor();
+
+protected:
+ void BuildUpdatesForLayer(ReadbackLayer* aLayer);
+
+ nsTArray<Update> mAllUpdates;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKPROCESSOR_H */
diff --git a/gfx/layers/RenderTrace.cpp b/gfx/layers/RenderTrace.cpp
new file mode 100644
index 000000000..977c772a7
--- /dev/null
+++ b/gfx/layers/RenderTrace.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderTrace.h"
+
+// If rendertrace is off let's no compile this code
+#ifdef MOZ_RENDERTRACE
+#include "Layers.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+
+namespace mozilla {
+namespace layers {
+
+static gfx::Matrix4x4 GetRootTransform(Layer *aLayer) {
+ gfx::Matrix4x4 layerTrans = aLayer->GetTransform();
+ layerTrans.ProjectTo2D();
+ if (aLayer->GetParent() != nullptr) {
+ return GetRootTransform(aLayer->GetParent()) * layerTrans;
+ }
+ return layerTrans;
+}
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform) {
+ int colorId = 0;
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&colorId] (Layer *layer)
+ {
+ gfx::Matrix4x4 trans = aRootTransform * layer->GetTransform();
+ trans.ProjectTo2D();
+ gfx::IntRect clipRect = layer->GetLocalVisibleRegion().GetBounds();
+ Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ trans.TransformBounds(rect);
+
+ if (strcmp(layer->Name(), "ContainerLayer") != 0 &&
+ strcmp(layer->Name(), "ContainerLayerComposite") != 0) {
+ printf_stderr("%s RENDERTRACE %u rect #%02X%s %i %i %i %i\n",
+ layer->Name(), (int)PR_IntervalNow(),
+ colorId, aColor,
+ (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+ }
+ colorId++;
+ });
+}
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect) {
+ gfx::Matrix4x4 trans = GetRootTransform(aLayer);
+ gfx::Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ trans.TransformBounds(rect);
+
+ printf_stderr("%s RENDERTRACE %u fillrect #%s %i %i %i %i\n",
+ aLayer->Name(), (int)PR_IntervalNow(),
+ aColor,
+ (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+}
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor) {
+ // Clear with an empty rect
+ RenderTraceInvalidateStart(aLayer, aColor, gfx::IntRect());
+}
+
+void renderTraceEventStart(const char *aComment, const char *aColor) {
+ printf_stderr("%s RENDERTRACE %u fillrect #%s 0 0 10 10\n",
+ aComment, (int)PR_IntervalNow(), aColor);
+}
+
+void renderTraceEventEnd(const char *aComment, const char *aColor) {
+ printf_stderr("%s RENDERTRACE %u fillrect #%s 0 0 0 0\n",
+ aComment, (int)PR_IntervalNow(), aColor);
+}
+
+void renderTraceEventEnd(const char *aColor) {
+ renderTraceEventEnd("", aColor);
+}
+
+}
+}
+
+#endif
+
diff --git a/gfx/layers/RenderTrace.h b/gfx/layers/RenderTrace.h
new file mode 100644
index 000000000..d7c28fddf
--- /dev/null
+++ b/gfx/layers/RenderTrace.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is a general tool that will let you visualize platform operation.
+// Currently used for the layer system, the general syntax allows this
+// tools to be adapted to trace other operations.
+//
+// For the front end see: https://github.com/staktrace/rendertrace
+
+// Uncomment this line to enable RENDERTRACE
+//#define MOZ_RENDERTRACE
+
+#ifndef GFX_RENDERTRACE_H
+#define GFX_RENDERTRACE_H
+
+#include "nsRect.h"
+#include "mozilla/gfx/Matrix.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform = gfx::Matrix4x4(), bool aReset = true);
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect);
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor);
+
+void renderTraceEventStart(const char *aComment, const char *aColor);
+void renderTraceEventEnd(const char *aComment, const char *aColor);
+void renderTraceEventEnd(const char *aColor);
+
+struct RenderTraceScope {
+public:
+ RenderTraceScope(const char *aComment, const char *aColor)
+ : mComment(aComment)
+ , mColor(aColor)
+ {
+ renderTraceEventStart(mComment, mColor);
+ }
+ ~RenderTraceScope() {
+ renderTraceEventEnd(mComment, mColor);
+ }
+private:
+ const char *mComment;
+ const char *mColor;
+};
+
+#ifndef MOZ_RENDERTRACE
+inline void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx::Matrix4x4 aRootTransform, bool aReset)
+{}
+
+inline void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const gfx::IntRect aRect)
+{}
+
+inline void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor)
+{}
+
+inline void renderTraceEventStart(const char *aComment, const char *aColor)
+{}
+
+inline void renderTraceEventEnd(const char *aComment, const char *aColor)
+{}
+
+inline void renderTraceEventEnd(const char *aColor)
+{}
+
+#endif // MOZ_RENDERTRACE
+
+} // namespace layers
+} // namespace mozilla
+
+#endif //GFX_RENDERTRACE_H
diff --git a/gfx/layers/RotatedBuffer.cpp b/gfx/layers/RotatedBuffer.cpp
new file mode 100644
index 000000000..92252fb04
--- /dev/null
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -0,0 +1,798 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RotatedBuffer.h"
+#include <sys/types.h> // for int32_t
+#include <algorithm> // for max
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayersImpl.h" // for ToData
+#include "BufferUnrotate.h" // for BufferUnrotate
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "Layers.h" // for PaintedLayer, Layer, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Point.h" // for Point, IntPoint
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for ExtendMode::ExtendMode::CLAMP, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowableLayer
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "gfx2DGlue.h"
+#include "nsLayoutUtils.h" // for invalidation debugging
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+IntRect
+RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
+{
+ // quadrantTranslation is the amount we translate the top-left
+ // of the quadrant by to get coordinates relative to the layer
+ IntPoint quadrantTranslation = -mBufferRotation;
+ quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
+ quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
+ return mBufferRect + quadrantTranslation;
+}
+
+Rect
+RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
+{
+ Rect result;
+ if (aXSide == LEFT) {
+ result.x = 0;
+ result.width = mBufferRotation.x;
+ } else {
+ result.x = mBufferRotation.x;
+ result.width = mBufferRect.width - mBufferRotation.x;
+ }
+ if (aYSide == TOP) {
+ result.y = 0;
+ result.height = mBufferRotation.y;
+ } else {
+ result.y = mBufferRotation.y;
+ result.height = mBufferRect.height - mBufferRotation.y;
+ }
+ return result;
+}
+
+/**
+ * @param aXSide LEFT means we draw from the left side of the buffer (which
+ * is drawn on the right side of mBufferRect). RIGHT means we draw from
+ * the right side of the buffer (which is drawn on the left side of
+ * mBufferRect).
+ * @param aYSide TOP means we draw from the top side of the buffer (which
+ * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
+ * the bottom side of the buffer (which is drawn on the top side of
+ * mBufferRect).
+ */
+void
+RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
+ XSide aXSide, YSide aYSide,
+ ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const
+{
+ // The rectangle that we're going to fill. Basically we're going to
+ // render the buffer at mBufferRect + quadrantTranslation to get the
+ // pixels in the right place, but we're only going to paint within
+ // mBufferRect
+ IntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
+ IntRect fillRect;
+ if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
+ return;
+
+ gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
+
+ MOZ_ASSERT(aSource != BUFFER_BOTH);
+ RefPtr<SourceSurface> snapshot = GetSourceSurface(aSource);
+
+ if (!snapshot) {
+ gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
+ return;
+ }
+
+ // direct2d is much slower when using OP_SOURCE so use OP_OVER and
+ // (maybe) a clear instead. Normally we need to draw in a single operation
+ // (to avoid flickering) but direct2d is ok since it defers rendering.
+ // We should try abstract this logic in a helper when we have other use
+ // cases.
+ if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
+ aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
+ aOperator == CompositionOp::OP_SOURCE) {
+ aOperator = CompositionOp::OP_OVER;
+ if (snapshot->GetFormat() == SurfaceFormat::B8G8R8A8) {
+ aTarget->ClearRect(IntRectToRect(fillRect));
+ }
+ }
+
+ // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
+ // We also can't do a ClearRect+FillRect since we need the drawing to happen
+ // as an atomic operation (to prevent flickering).
+ // We also need this clip in the case where we have a mask, since the mask surface
+ // might cover more than fillRect, but we only want to touch the pixels inside
+ // fillRect.
+ aTarget->PushClipRect(IntRectToRect(fillRect));
+
+ if (aMask) {
+ Matrix oldTransform = aTarget->GetTransform();
+
+ // Transform from user -> buffer space.
+ Matrix transform =
+ Matrix::Translation(quadrantTranslation.x, quadrantTranslation.y);
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ transform *= oldTransform;
+ transform *= inverseMask;
+
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, SamplingFilter::POINT);
+#else
+ SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
+#endif
+
+ aTarget->SetTransform(*aMaskTransform);
+ aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
+ aTarget->SetTransform(oldTransform);
+ } else {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ DrawSurfaceOptions options(SamplingFilter::POINT);
+#else
+ DrawSurfaceOptions options;
+#endif
+ aTarget->DrawSurface(snapshot, IntRectToRect(fillRect),
+ GetSourceRectangle(aXSide, aYSide),
+ options,
+ DrawOptions(aOpacity, aOperator));
+ }
+
+ aTarget->PopClip();
+}
+
+void
+RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget, ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const
+{
+ PROFILER_LABEL("RotatedBuffer", "DrawBufferWithRotation",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ // See above, in Azure Repeat should always be a safe, even faster choice
+ // though! Particularly on D2D Repeat should be a lot faster, need to look
+ // into that. TODO[Bas]
+ DrawBufferQuadrant(aTarget, LEFT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, RIGHT, TOP, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aSource, aOpacity, aOperator, aMask, aMaskTransform);
+ DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aSource, aOpacity, aOperator,aMask, aMaskTransform);
+}
+
+already_AddRefed<SourceSurface>
+SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const
+{
+ RefPtr<SourceSurface> surf;
+ if (aSource == BUFFER_BLACK) {
+ surf = mSource;
+ } else {
+ MOZ_ASSERT(aSource == BUFFER_WHITE);
+ surf = mSourceOnWhite;
+ }
+
+ MOZ_ASSERT(surf);
+ return surf.forget();
+}
+
+/* static */ bool
+RotatedContentBuffer::IsClippingCheap(DrawTarget* aTarget, const nsIntRegion& aRegion)
+{
+ // Assume clipping is cheap if the draw target just has an integer
+ // translation, and the visible region is simple.
+ return !aTarget->GetTransform().HasNonIntegerTranslation() &&
+ aRegion.GetNumRects() <= 1;
+}
+
+void
+RotatedContentBuffer::DrawTo(PaintedLayer* aLayer,
+ DrawTarget* aTarget,
+ float aOpacity,
+ CompositionOp aOp,
+ SourceSurface* aMask,
+ const Matrix* aMaskTransform)
+{
+ if (!EnsureBuffer()) {
+ return;
+ }
+
+ bool clipped = false;
+
+ // If the entire buffer is valid, we can just draw the whole thing,
+ // no need to clip. But we'll still clip if clipping is cheap ---
+ // that might let us copy a smaller region of the buffer.
+ // Also clip to the visible region if we're told to.
+ if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
+ (ToData(aLayer)->GetClipToVisibleRegion() &&
+ !aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect())) ||
+ IsClippingCheap(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion())) {
+ // We don't want to draw invalid stuff, so we need to clip. Might as
+ // well clip to the smallest area possible --- the visible region.
+ // Bug 599189 if there is a non-integer-translation transform in aTarget,
+ // we might sample pixels outside GetLocalVisibleRegion(), which is wrong
+ // and may cause gray lines.
+ gfxUtils::ClipToRegion(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ clipped = true;
+ }
+
+ DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
+ if (clipped) {
+ aTarget->PopClip();
+ }
+}
+
+DrawTarget*
+RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
+ ContextSource aSource,
+ DrawIterator* aIter)
+{
+ IntRect bounds = aBounds;
+ if (aIter) {
+ // If an iterator was provided, then BeginPaint must have been run with
+ // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
+ // Iterate over each of them, and return an appropriate buffer each time we find
+ // one that intersects the draw region. The iterator mCount value tracks which
+ // quadrants we have considered across multiple calls to this function.
+ aIter->mDrawRegion.SetEmpty();
+ while (aIter->mCount < 4) {
+ IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
+ (aIter->mCount & 2) ? TOP : BOTTOM);
+ aIter->mDrawRegion.And(aBounds, quadrant);
+ aIter->mCount++;
+ if (!aIter->mDrawRegion.IsEmpty()) {
+ break;
+ }
+ }
+ if (aIter->mDrawRegion.IsEmpty()) {
+ return nullptr;
+ }
+ bounds = aIter->mDrawRegion.GetBounds();
+ }
+
+ if (!EnsureBuffer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
+ if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
+ if (!EnsureBufferOnWhite()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid() && mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
+ } else if (aSource == BUFFER_WHITE) {
+ if (!EnsureBufferOnWhite()) {
+ return nullptr;
+ }
+ mLoanedDrawTarget = mDTBufferOnWhite;
+ } else {
+ // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
+ mLoanedDrawTarget = mDTBuffer;
+ }
+
+ // Figure out which quadrant to draw in
+ int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
+ int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
+ XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
+ YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
+ IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
+ NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
+
+ mLoanedTransform = mLoanedDrawTarget->GetTransform();
+ mLoanedDrawTarget->SetTransform(Matrix(mLoanedTransform).
+ PreTranslate(-quadrantRect.x,
+ -quadrantRect.y));
+
+ return mLoanedDrawTarget;
+}
+
+void
+BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
+{
+ MOZ_ASSERT(mLoanedDrawTarget);
+ MOZ_ASSERT(aReturned == mLoanedDrawTarget);
+ if (mLoanedDrawTarget) {
+ mLoanedDrawTarget->SetTransform(mLoanedTransform);
+ mLoanedDrawTarget = nullptr;
+ }
+ aReturned = nullptr;
+}
+
+gfxContentType
+RotatedContentBuffer::BufferContentType()
+{
+ if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
+ SurfaceFormat format = SurfaceFormat::B8G8R8A8;
+
+ if (mBufferProvider) {
+ format = mBufferProvider->GetFormat();
+ } else if (mDTBuffer && mDTBuffer->IsValid()) {
+ format = mDTBuffer->GetFormat();
+ }
+
+ return ContentForFormat(format);
+ }
+ return gfxContentType::SENTINEL;
+}
+
+bool
+RotatedContentBuffer::BufferSizeOkFor(const IntSize& aSize)
+{
+ return (aSize == mBufferRect.Size() ||
+ (SizedToVisibleBounds != mBufferSizePolicy &&
+ aSize < mBufferRect.Size()));
+}
+
+bool
+RotatedContentBuffer::EnsureBuffer()
+{
+ NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
+ if (!mDTBuffer || !mDTBuffer->IsValid()) {
+ if (mBufferProvider) {
+ mDTBuffer = mBufferProvider->BorrowDrawTarget();
+ }
+ }
+
+ NS_WARNING_ASSERTION(mDTBuffer && mDTBuffer->IsValid(), "no buffer");
+ return !!mDTBuffer;
+}
+
+bool
+RotatedContentBuffer::EnsureBufferOnWhite()
+{
+ NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
+ if (!mDTBufferOnWhite) {
+ if (mBufferProviderOnWhite) {
+ mDTBufferOnWhite =
+ mBufferProviderOnWhite->BorrowDrawTarget();
+ }
+ }
+
+ NS_WARNING_ASSERTION(mDTBufferOnWhite, "no buffer");
+ return !!mDTBufferOnWhite;
+}
+
+bool
+RotatedContentBuffer::HaveBuffer() const
+{
+ return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid());
+}
+
+bool
+RotatedContentBuffer::HaveBufferOnWhite() const
+{
+ return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+}
+
+static void
+WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
+{
+ if (*aRotationPoint < 0) {
+ *aRotationPoint += aSize;
+ } else if (*aRotationPoint >= aSize) {
+ *aRotationPoint -= aSize;
+ }
+}
+
+static IntRect
+ComputeBufferRect(const IntRect& aRequestedRect)
+{
+ IntRect rect(aRequestedRect);
+ // Set a minimum width to guarantee a minimum size of buffers we
+ // allocate (and work around problems on some platforms with smaller
+ // dimensions). 64 is the magic number needed to work around the
+ // rendering glitch, and guarantees image rows can be SIMD'd for
+ // even r5g6b5 surfaces pretty much everywhere.
+ rect.width = std::max(aRequestedRect.width, 64);
+ return rect;
+}
+
+void
+RotatedContentBuffer::FlushBuffers()
+{
+ if (mDTBuffer) {
+ mDTBuffer->Flush();
+ }
+ if (mDTBufferOnWhite) {
+ mDTBufferOnWhite->Flush();
+ }
+}
+
+RotatedContentBuffer::PaintState
+RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
+ uint32_t aFlags)
+{
+ PaintState result;
+ // We need to disable rotation if we're going to be resampled when
+ // drawing, because we might sample across the rotation boundary.
+ bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
+ !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION));
+
+ nsIntRegion validRegion = aLayer->GetValidRegion();
+
+ bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
+ ContentType layerContentType =
+ canUseOpaqueSurface ? gfxContentType::COLOR :
+ gfxContentType::COLOR_ALPHA;
+
+ SurfaceMode mode;
+ nsIntRegion neededRegion;
+ IntRect destBufferRect;
+
+ bool canReuseBuffer = HaveBuffer();
+
+ while (true) {
+ mode = aLayer->GetSurfaceMode();
+ neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
+ canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
+ result.mContentType = layerContentType;
+
+ if (canReuseBuffer) {
+ if (mBufferRect.Contains(neededRegion.GetBounds())) {
+ // We don't need to adjust mBufferRect.
+ destBufferRect = mBufferRect;
+ } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
+ // The buffer's big enough but doesn't contain everything that's
+ // going to be visible. We'll move it.
+ destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
+ } else {
+ destBufferRect = neededRegion.GetBounds();
+ }
+ } else {
+ // We won't be reusing the buffer. Compute a new rect.
+ destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+ }
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+ if (!aLayer->GetParent() ||
+ !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
+ !aLayer->AsShadowableLayer() ||
+ !aLayer->AsShadowableLayer()->HasShadow()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ result.mContentType = gfxContentType::COLOR;
+ }
+#endif
+ }
+
+ if ((aFlags & PAINT_WILL_RESAMPLE) &&
+ (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
+ neededRegion.GetNumRects() > 1))
+ {
+ // The area we add to neededRegion might not be painted opaquely.
+ if (mode == SurfaceMode::SURFACE_OPAQUE) {
+ result.mContentType = gfxContentType::COLOR_ALPHA;
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
+
+ // We need to validate the entire buffer, to make sure that only valid
+ // pixels are sampled.
+ neededRegion = destBufferRect;
+ }
+
+ // If we have an existing buffer, but the content type has changed or we
+ // have transitioned into/out of component alpha, then we need to recreate it.
+ if (canReuseBuffer &&
+ (result.mContentType != BufferContentType() ||
+ (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
+ {
+ // Restart the decision process; we won't re-enter since we guard on
+ // being able to re-use the buffer.
+ canReuseBuffer = false;
+ continue;
+ }
+
+ break;
+ }
+
+ if (HaveBuffer() &&
+ (result.mContentType != BufferContentType() ||
+ (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()))
+ {
+ // We're effectively clearing the valid region, so we need to draw
+ // the entire needed region now.
+ canReuseBuffer = false;
+ result.mRegionToInvalidate = aLayer->GetValidRegion();
+ validRegion.SetEmpty();
+ Clear();
+
+#if defined(MOZ_DUMP_PAINTING)
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ if (result.mContentType != BufferContentType()) {
+ printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
+ } else if ((mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != HaveBufferOnWhite()) {
+ printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
+ }
+ }
+#endif
+ }
+
+ NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
+ "Destination rect doesn't contain what we need to paint");
+
+ result.mRegionToDraw.Sub(neededRegion, validRegion);
+
+ if (result.mRegionToDraw.IsEmpty())
+ return result;
+
+ if (HaveBuffer()) {
+ // Do not modify result.mRegionToDraw or result.mContentType after this call.
+ // Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,
+ // or call CreateBuffer before this call.
+ FinalizeFrame(result.mRegionToDraw);
+ }
+
+ IntRect drawBounds = result.mRegionToDraw.GetBounds();
+ RefPtr<DrawTarget> destDTBuffer;
+ RefPtr<DrawTarget> destDTBufferOnWhite;
+ uint32_t bufferFlags = 0;
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ bufferFlags |= BUFFER_COMPONENT_ALPHA;
+ }
+ if (canReuseBuffer) {
+ if (!EnsureBuffer()) {
+ return result;
+ }
+ IntRect keepArea;
+ if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
+ // Set mBufferRotation so that the pixels currently in mDTBuffer
+ // will still be rendered in the right place when mBufferRect
+ // changes to destBufferRect.
+ IntPoint newRotation = mBufferRotation +
+ (destBufferRect.TopLeft() - mBufferRect.TopLeft());
+ WrapRotationAxis(&newRotation.x, mBufferRect.width);
+ WrapRotationAxis(&newRotation.y, mBufferRect.height);
+ NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
+ "newRotation out of bounds");
+ int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
+ int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
+ bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
+ (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
+ if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
+ (newRotation != IntPoint(0,0) && !canHaveRotation)) {
+ // The stuff we need to redraw will wrap around an edge of the
+ // buffer (and the caller doesn't know how to support that), so
+ // move the pixels we can keep into a position that lets us
+ // redraw in just one quadrant.
+ if (mBufferRotation == IntPoint(0,0)) {
+ IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
+ IntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid());
+ mDTBuffer->CopyRect(srcRect, dest);
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mDTBufferOnWhite->CopyRect(srcRect, dest);
+ }
+ result.mDidSelfCopy = true;
+ mDidSelfCopy = true;
+ // Don't set destBuffer; we special-case self-copies, and
+ // just did the necessary work above.
+ mBufferRect = destBufferRect;
+ } else {
+ // With azure and a data surface perform an buffer unrotate
+ // (SelfCopy).
+ unsigned char* data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+
+ if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
+ uint8_t bytesPerPixel = BytesPerPixel(format);
+ BufferUnrotate(data,
+ size.width * bytesPerPixel,
+ size.height, stride,
+ newRotation.x * bytesPerPixel, newRotation.y);
+ mDTBuffer->ReleaseBits(data);
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
+ mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
+ uint8_t bytesPerPixel = BytesPerPixel(format);
+ BufferUnrotate(data,
+ size.width * bytesPerPixel,
+ size.height, stride,
+ newRotation.x * bytesPerPixel, newRotation.y);
+ mDTBufferOnWhite->ReleaseBits(data);
+ }
+
+ // Buffer unrotate moves all the pixels, note that
+ // we self copied for SyncBackToFrontBuffer
+ result.mDidSelfCopy = true;
+ mDidSelfCopy = true;
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0, 0);
+ }
+
+ if (!result.mDidSelfCopy) {
+ destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+ CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
+ &destDTBuffer, &destDTBufferOnWhite);
+ if (!destDTBuffer ||
+ (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
+ if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+ gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+ }
+ return result;
+ }
+ }
+ }
+ } else {
+ mBufferRect = destBufferRect;
+ mBufferRotation = newRotation;
+ }
+ } else {
+ // No pixels are going to be kept. The whole visible region
+ // will be redrawn, so we don't need to copy anything, so we don't
+ // set destBuffer.
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0,0);
+ }
+ } else {
+ // The buffer's not big enough, so allocate a new one
+ CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
+ &destDTBuffer, &destDTBufferOnWhite);
+ if (!destDTBuffer ||
+ (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
+ if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+ gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+ }
+ return result;
+ }
+ }
+
+ NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
+ "If we're resampling, we need to validate the entire buffer");
+
+ // If we have no buffered data already, then destBuffer will be a fresh buffer
+ // and we do not need to clear it below.
+ bool isClear = !HaveBuffer();
+
+ if (destDTBuffer) {
+ if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
+ // Copy the bits
+ IntPoint offset = -destBufferRect.TopLeft();
+ Matrix mat = Matrix::Translation(offset.x, offset.y);
+ destDTBuffer->SetTransform(mat);
+ if (!EnsureBuffer()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid(), "Have we got a Thebes buffer for some reason?");
+ DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+ destDTBuffer->SetTransform(Matrix());
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!destDTBufferOnWhite || !EnsureBufferOnWhite()) {
+ return result;
+ }
+ MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid(), "Have we got a Thebes buffer for some reason?");
+ destDTBufferOnWhite->SetTransform(mat);
+ DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+ destDTBufferOnWhite->SetTransform(Matrix());
+ }
+ }
+
+ mDTBuffer = destDTBuffer.forget();
+ mDTBufferOnWhite = destDTBufferOnWhite.forget();
+ mBufferRect = destBufferRect;
+ mBufferRotation = IntPoint(0,0);
+ }
+ NS_ASSERTION(canHaveRotation || mBufferRotation == IntPoint(0,0),
+ "Rotation disabled, but we have nonzero rotation?");
+
+ nsIntRegion invalidate;
+ invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
+ result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
+ result.mClip = DrawRegionClip::DRAW;
+ result.mMode = mode;
+
+ return result;
+}
+
+DrawTarget*
+RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
+ DrawIterator* aIter /* = nullptr */)
+{
+ if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
+ return nullptr;
+ }
+
+ DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
+ BUFFER_BOTH, aIter);
+ if (!result) {
+ return nullptr;
+ }
+
+ nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
+ if (aIter) {
+ // The iterators draw region currently only contains the bounds of the region,
+ // this makes it the precise region.
+ aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
+ drawPtr = &aIter->mDrawRegion;
+ }
+ if (result->GetBackendType() == BackendType::DIRECT2D ||
+ result->GetBackendType() == BackendType::DIRECT2D1_1) {
+ // Simplify the draw region to avoid hitting expensive drawing paths
+ // for complex regions.
+ drawPtr->SimplifyOutwardByArea(100 * 100);
+ }
+
+ if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ if (!mDTBuffer || !mDTBuffer->IsValid() ||
+ !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
+ // This can happen in release builds if allocating one of the two buffers
+ // failed. This in turn can happen if unreasonably large textures are
+ // requested.
+ return nullptr;
+ }
+ for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+ ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+ mDTBufferOnWhite->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+ ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ }
+ } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
+ // HaveBuffer() => we have an existing buffer that we must clear
+ for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ result->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
+ }
+ }
+
+ return result;
+}
+
+already_AddRefed<SourceSurface>
+RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
+{
+ if (!mDTBuffer || !mDTBuffer->IsValid()) {
+ gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer);
+ return nullptr;
+ }
+
+ if (aSource == BUFFER_BLACK) {
+ return mDTBuffer->Snapshot();
+ } else {
+ if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
+ gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite);
+ return nullptr;
+ }
+ MOZ_ASSERT(aSource == BUFFER_WHITE);
+ return mDTBufferOnWhite->Snapshot();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/gfx/layers/RotatedBuffer.h b/gfx/layers/RotatedBuffer.h
new file mode 100644
index 000000000..3b7e18123
--- /dev/null
+++ b/gfx/layers/RotatedBuffer.h
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ROTATEDBUFFER_H_
+#define ROTATEDBUFFER_H_
+
+#include "gfxTypes.h"
+#include <stdint.h> // for uint32_t
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h" // for DrawTarget, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace gfx {
+class Matrix;
+} // namespace gfx
+
+namespace layers {
+
+class TextureClient;
+class PaintedLayer;
+
+/**
+ * This is a cairo/Thebes surface, but with a literal twist. Scrolling
+ * causes the layer's visible region to move. We want to keep
+ * reusing the same surface if the region size hasn't changed, but we don't
+ * want to keep moving the contents of the surface around in memory. So
+ * we use a trick.
+ * Consider just the vertical case, and suppose the buffer is H pixels
+ * high and we're scrolling down by N pixels. Instead of copying the
+ * buffer contents up by N pixels, we leave the buffer contents in place,
+ * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
+ * Then we can refresh the screen by painting rows N to H-1 of the buffer
+ * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
+ * at row H-N on the screen.
+ * mBufferRotation.y would be N in this example.
+ */
+class RotatedBuffer {
+public:
+ typedef gfxContentType ContentType;
+
+ RotatedBuffer(const gfx::IntRect& aBufferRect,
+ const gfx::IntPoint& aBufferRotation)
+ : mBufferRect(aBufferRect)
+ , mBufferRotation(aBufferRotation)
+ , mDidSelfCopy(false)
+ { }
+ RotatedBuffer()
+ : mDidSelfCopy(false)
+ { }
+
+ /*
+ * Which buffer should be drawn to/read from.
+ */
+ enum ContextSource {
+ BUFFER_BLACK, // The normal buffer, or buffer with black background when using component alpha.
+ BUFFER_WHITE, // The buffer with white background, only valid with component alpha.
+ BUFFER_BOTH // The combined black/white buffers, only valid for writing operations, not reading.
+ };
+ // It is the callers repsonsibility to ensure aTarget is flushed after calling
+ // this method.
+ void DrawBufferWithRotation(gfx::DrawTarget* aTarget, ContextSource aSource,
+ float aOpacity = 1.0,
+ gfx::CompositionOp aOperator = gfx::CompositionOp::OP_OVER,
+ gfx::SourceSurface* aMask = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr) const;
+
+ /**
+ * |BufferRect()| is the rect of device pixels that this
+ * RotatedBuffer covers. That is what DrawBufferWithRotation()
+ * will paint when it's called.
+ */
+ const gfx::IntRect& BufferRect() const { return mBufferRect; }
+ const gfx::IntPoint& BufferRotation() const { return mBufferRotation; }
+
+ virtual bool HaveBuffer() const = 0;
+ virtual bool HaveBufferOnWhite() const = 0;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const = 0;
+
+protected:
+
+ enum XSide {
+ LEFT, RIGHT
+ };
+ enum YSide {
+ TOP, BOTTOM
+ };
+ gfx::IntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
+
+ gfx::Rect GetSourceRectangle(XSide aXSide, YSide aYSide) const;
+
+ /*
+ * If aMask is non-null, then it is used as an alpha mask for rendering this
+ * buffer. aMaskTransform must be non-null if aMask is non-null, and is used
+ * to adjust the coordinate space of the mask.
+ */
+ void DrawBufferQuadrant(gfx::DrawTarget* aTarget, XSide aXSide, YSide aYSide,
+ ContextSource aSource,
+ float aOpacity,
+ gfx::CompositionOp aOperator,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) const;
+
+ /** The area of the PaintedLayer that is covered by the buffer as a whole */
+ gfx::IntRect mBufferRect;
+ /**
+ * The x and y rotation of the buffer. Conceptually the buffer
+ * has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
+ * is tiled to fill the plane, and the result is clipped to mBufferRect.
+ * So the pixel at mBufferRotation within the buffer is what gets painted at
+ * mBufferRect.TopLeft().
+ * This is "rotation" in the sense of rotating items in a linear buffer,
+ * where items falling off the end of the buffer are returned to the
+ * buffer at the other end, not 2D rotation!
+ */
+ gfx::IntPoint mBufferRotation;
+ // When this is true it means that all pixels have moved inside the buffer.
+ // It's not possible to sync with another buffer without a full copy.
+ bool mDidSelfCopy;
+};
+
+class SourceRotatedBuffer : public RotatedBuffer
+{
+public:
+ SourceRotatedBuffer(gfx::SourceSurface* aSource, gfx::SourceSurface* aSourceOnWhite,
+ const gfx::IntRect& aBufferRect,
+ const gfx::IntPoint& aBufferRotation)
+ : RotatedBuffer(aBufferRect, aBufferRotation)
+ , mSource(aSource)
+ , mSourceOnWhite(aSourceOnWhite)
+ { }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
+
+ virtual bool HaveBuffer() const { return !!mSource; }
+ virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; }
+
+private:
+ RefPtr<gfx::SourceSurface> mSource;
+ RefPtr<gfx::SourceSurface> mSourceOnWhite;
+};
+
+// Mixin class for classes which need logic for loaning out a draw target.
+// See comments on BorrowDrawTargetForQuadrantUpdate.
+class BorrowDrawTarget
+{
+protected:
+ void ReturnDrawTarget(gfx::DrawTarget*& aReturned);
+
+ // The draw target loaned by BorrowDrawTargetForQuadrantUpdate. It should not
+ // be used, we just keep a reference to ensure it is kept alive and so we can
+ // correctly restore state when it is returned.
+ RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
+ gfx::Matrix mLoanedTransform;
+};
+
+/**
+ * This class encapsulates the buffer used to retain PaintedLayer contents,
+ * i.e., the contents of the layer's GetVisibleRegion().
+ */
+class RotatedContentBuffer : public RotatedBuffer
+ , public BorrowDrawTarget
+{
+public:
+ typedef gfxContentType ContentType;
+
+ /**
+ * Controls the size of the backing buffer of this.
+ * - SizedToVisibleBounds: the backing buffer is exactly the same
+ * size as the bounds of PaintedLayer's visible region
+ * - ContainsVisibleBounds: the backing buffer is large enough to
+ * fit visible bounds. May be larger.
+ */
+ enum BufferSizePolicy {
+ SizedToVisibleBounds,
+ ContainsVisibleBounds
+ };
+
+ explicit RotatedContentBuffer(BufferSizePolicy aBufferSizePolicy)
+ : mBufferProvider(nullptr)
+ , mBufferProviderOnWhite(nullptr)
+ , mBufferSizePolicy(aBufferSizePolicy)
+ {
+ MOZ_COUNT_CTOR(RotatedContentBuffer);
+ }
+ virtual ~RotatedContentBuffer()
+ {
+ MOZ_COUNT_DTOR(RotatedContentBuffer);
+ }
+
+ /**
+ * Wipe out all retained contents. Call this when the entire
+ * buffer becomes invalid.
+ */
+ void Clear()
+ {
+ mDTBuffer = nullptr;
+ mDTBufferOnWhite = nullptr;
+ mBufferProvider = nullptr;
+ mBufferProviderOnWhite = nullptr;
+ mBufferRect.SetEmpty();
+ }
+
+ /**
+ * This is returned by BeginPaint. The caller should draw into mTarget.
+ * mRegionToDraw must be drawn. mRegionToInvalidate has been invalidated
+ * by RotatedContentBuffer and must be redrawn on the screen.
+ * mRegionToInvalidate is set when the buffer has changed from
+ * opaque to transparent or vice versa, since the details of rendering can
+ * depend on the buffer type. mDidSelfCopy is true if we kept our buffer
+ * but used MovePixels() to shift its content.
+ */
+ struct PaintState {
+ PaintState()
+ : mRegionToDraw()
+ , mRegionToInvalidate()
+ , mMode(SurfaceMode::SURFACE_NONE)
+ , mClip(DrawRegionClip::NONE)
+ , mContentType(gfxContentType::SENTINEL)
+ , mDidSelfCopy(false)
+ {}
+
+ nsIntRegion mRegionToDraw;
+ nsIntRegion mRegionToInvalidate;
+ SurfaceMode mMode;
+ DrawRegionClip mClip;
+ ContentType mContentType;
+ bool mDidSelfCopy;
+ };
+
+ enum {
+ PAINT_WILL_RESAMPLE = 0x01,
+ PAINT_NO_ROTATION = 0x02,
+ PAINT_CAN_DRAW_ROTATED = 0x04
+ };
+ /**
+ * Start a drawing operation. This returns a PaintState describing what
+ * needs to be drawn to bring the buffer up to date in the visible region.
+ * This queries aLayer to get the currently valid and visible regions.
+ * The returned mTarget may be null if mRegionToDraw is empty.
+ * Otherwise it must not be null.
+ * mRegionToInvalidate will contain mRegionToDraw.
+ * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
+ * buffer will be resampled when rendering (i.e the effective transform
+ * combined with the scale for the resolution is not just an integer
+ * translation). This will disable buffer rotation (since we don't want
+ * to resample across the rotation boundary) and will ensure that we
+ * make the entire buffer contents valid (since we don't want to sample
+ * invalid pixels outside the visible region, if the visible region doesn't
+ * fill the buffer bounds).
+ * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
+ * rotated content that crosses the physical buffer boundary. The caller
+ * will need to call BorrowDrawTargetForPainting multiple times to achieve
+ * this.
+ */
+ PaintState BeginPaint(PaintedLayer* aLayer,
+ uint32_t aFlags);
+
+ struct DrawIterator {
+ friend class RotatedContentBuffer;
+ DrawIterator()
+ : mCount(0)
+ {}
+
+ nsIntRegion mDrawRegion;
+
+ private:
+ uint32_t mCount;
+ };
+
+ /**
+ * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
+ * this. See notes on BorrowDrawTargetForQuadrantUpdate.
+ * May return null. If the return value is non-null, it must be
+ * 'un-borrowed' using ReturnDrawTarget.
+ *
+ * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
+ * must call this function repeatedly (with an iterator) until it returns
+ * nullptr. The caller should draw the mDrawRegion of the iterator instead
+ * of mRegionToDraw in the PaintState.
+ *
+ * @param aPaintState Paint state data returned by a call to BeginPaint
+ * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
+ * was specified to BeginPaint.
+ */
+ gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ DrawIterator* aIter = nullptr);
+
+ enum {
+ BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
+ // component alpha.
+ };
+ /**
+ * Return a new surface of |aSize| and |aType|.
+ *
+ * If the created buffer supports azure content, then the result(s) will
+ * be returned in aBlackDT/aWhiteDT, otherwise aBlackSurface/aWhiteSurface
+ * will be used.
+ */
+ virtual void
+ CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) = 0;
+
+ /**
+ * Get the underlying buffer, if any. This is useful because we can pass
+ * in the buffer as the default "reference surface" if there is one.
+ * Don't use it for anything else!
+ */
+ gfx::DrawTarget* GetDTBuffer() { return mDTBuffer; }
+ gfx::DrawTarget* GetDTBufferOnWhite() { return mDTBufferOnWhite; }
+
+ virtual already_AddRefed<gfx::SourceSurface> GetSourceSurface(ContextSource aSource) const;
+
+ /**
+ * Complete the drawing operation. The region to draw must have been
+ * drawn before this is called. The contents of the buffer are drawn
+ * to aTarget.
+ */
+ void DrawTo(PaintedLayer* aLayer,
+ gfx::DrawTarget* aTarget,
+ float aOpacity,
+ gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform);
+
+protected:
+ // new texture client versions
+ void SetBufferProvider(TextureClient* aClient)
+ {
+ // Only this buffer provider can give us a buffer. If we
+ // already have one, something has gone wrong.
+ MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid());
+
+ mBufferProvider = aClient;
+ if (!mBufferProvider) {
+ mDTBuffer = nullptr;
+ }
+ }
+
+ void SetBufferProviderOnWhite(TextureClient* aClient)
+ {
+ // Only this buffer provider can give us a buffer. If we
+ // already have one, something has gone wrong.
+ MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
+
+ mBufferProviderOnWhite = aClient;
+ if (!mBufferProviderOnWhite) {
+ mDTBufferOnWhite = nullptr;
+ }
+ }
+
+ /**
+ * Get a draw target at the specified resolution for updating |aBounds|,
+ * which must be contained within a single quadrant.
+ *
+ * The result should only be held temporarily by the caller (it will be kept
+ * alive by this). Once used it should be returned using ReturnDrawTarget.
+ * BorrowDrawTargetForQuadrantUpdate may not be called more than once without
+ * first calling ReturnDrawTarget.
+ *
+ * ReturnDrawTarget will restore the transform on the draw target. But it is
+ * the callers responsibility to restore the clip. The caller should flush the
+ * draw target, if necessary.
+ */
+ gfx::DrawTarget*
+ BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
+ ContextSource aSource,
+ DrawIterator* aIter);
+
+ static bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
+
+protected:
+ /**
+ * Return the buffer's content type. Requires a valid buffer or
+ * buffer provider.
+ */
+ gfxContentType BufferContentType();
+ bool BufferSizeOkFor(const gfx::IntSize& aSize);
+ /**
+ * If the buffer hasn't been mapped, map it.
+ */
+ bool EnsureBuffer();
+ bool EnsureBufferOnWhite();
+
+ // Flush our buffers if they are mapped.
+ void FlushBuffers();
+
+ /**
+ * True if we have a buffer where we can get it (but not necessarily
+ * mapped currently).
+ */
+ virtual bool HaveBuffer() const;
+ virtual bool HaveBufferOnWhite() const;
+
+ /**
+ * Any actions that should be performed at the last moment before we begin
+ * rendering the next frame. I.e., after we calculate what we will draw,
+ * but before we rotate the buffer and possibly create new buffers.
+ * aRegionToDraw is the region which is guaranteed to be overwritten when
+ * drawing the next frame.
+ */
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
+
+ RefPtr<gfx::DrawTarget> mDTBuffer;
+ RefPtr<gfx::DrawTarget> mDTBufferOnWhite;
+
+ /**
+ * These members are only set transiently. They're used to map mDTBuffer
+ * when we're using surfaces that require explicit map/unmap. Only one
+ * may be used at a time.
+ */
+ TextureClient* mBufferProvider;
+ TextureClient* mBufferProviderOnWhite;
+
+ BufferSizePolicy mBufferSizePolicy;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* ROTATEDBUFFER_H_ */
diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp
new file mode 100644
index 000000000..79a9469bf
--- /dev/null
+++ b/gfx/layers/TextureDIB.cpp
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureDIB.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class MemoryDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ mSurface = nullptr;
+ }
+
+ MemoryDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ {
+ MOZ_COUNT_CTOR(MemoryDIBTextureData);
+ }
+
+ virtual ~MemoryDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(MemoryDIBTextureData);
+ }
+};
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class ShmemDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+ void DeallocateData()
+ {
+ if (mSurface) {
+ ::DeleteObject(mBitmap);
+ ::DeleteDC(mDC);
+ ::CloseHandle(mFileMapping);
+ mBitmap = NULL;
+ mDC = NULL;
+ mFileMapping = NULL;
+ mSurface = nullptr;
+ }
+ }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ DeallocateData();
+ }
+
+ ShmemDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface,
+ HANDLE aFileMapping, HANDLE aHostHandle,
+ HDC aDC, HBITMAP aBitmap)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ , mFileMapping(aFileMapping)
+ , mHostHandle(aHostHandle)
+ , mDC(aDC)
+ , mBitmap(aBitmap)
+ {
+ MOZ_COUNT_CTOR(ShmemDIBTextureData);
+ }
+
+ virtual ~ShmemDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(ShmemDIBTextureData);
+
+ // The host side has its own references and handles to this data, we can
+ // safely clear ours.
+ DeallocateData();
+ }
+
+ HANDLE mFileMapping;
+ HANDLE mHostHandle;
+ HDC mDC;
+ HBITMAP mBitmap;
+};
+
+void
+DIBTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasIntermediateBuffer = true;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<gfx::DrawTarget>
+DIBTextureData::BorrowDrawTarget()
+{
+ return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
+}
+
+DIBTextureData*
+DIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return nullptr;
+ }
+ if (aAllocator->IsSameProcess()) {
+ return MemoryDIBTextureData::Create(aSize, aFormat);
+ } else {
+ return ShmemDIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+}
+
+TextureData*
+MemoryDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return MemoryDIBTextureData::Create(mSize, mFormat);
+}
+
+bool
+MemoryDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ // The host will release this ref when it receives the surface descriptor.
+ // We AddRef in case we die before the host receives the pointer.
+ aOutDescriptor = SurfaceDescriptorDIB(reinterpret_cast<uintptr_t>(mSurface.get()));
+ mSurface->AddRef();
+ return true;
+}
+
+DIBTextureData*
+MemoryDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+{
+ RefPtr<gfxWindowsSurface> surface
+ = new gfxWindowsSurface(aSize, SurfaceFormatToImageFormat(aFormat));
+ if (!surface || surface->CairoStatus()) {
+ NS_WARNING("Could not create DIB surface");
+ return nullptr;
+ }
+
+ return new MemoryDIBTextureData(aSize, aFormat, surface);
+}
+
+bool
+MemoryDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DIB).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(imgSurf->Data() + imgSurf->Stride() * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ return true;
+}
+
+TextureData*
+ShmemDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return ShmemDIBTextureData::Create(mSize, mFormat, aAllocator);
+}
+
+bool
+ShmemDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DTD).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ GdiFlush();
+
+ uint32_t stride = mSize.width * BytesPerPixel(mFormat);
+ uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_WRITE, 0, 0, stride * mSize.height);
+
+ if (!data) {
+ gfxCriticalError() << "Failed to map view of file for UpdateFromSurface.";
+ srcSurf->Unmap();
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(data + stride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ ::UnmapViewOfFile(data);
+
+ srcSurf->Unmap();
+ return true;
+}
+
+bool
+ShmemDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ ::GdiFlush();
+ aOutDescriptor = SurfaceDescriptorFileMapping((WindowsHandle)mHostHandle, mFormat, mSize);
+ return true;
+}
+
+DIBTextureData*
+ShmemDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator->GetParentPid() != base::ProcessId());
+
+ DWORD mapSize = aSize.width * aSize.height * BytesPerPixel(aFormat);
+ HANDLE fileMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, mapSize, NULL);
+
+ if (!fileMapping) {
+ gfxCriticalError() << "Failed to create memory file mapping for " << mapSize << " bytes.";
+ return nullptr;
+ }
+
+ BITMAPV4HEADER header;
+ memset(&header, 0, sizeof(BITMAPV4HEADER));
+ header.bV4Size = sizeof(BITMAPV4HEADER);
+ header.bV4Width = aSize.width;
+ header.bV4Height = -LONG(aSize.height); // top-to-buttom DIB
+ header.bV4Planes = 1;
+ header.bV4BitCount = 32;
+ header.bV4V4Compression = BI_BITFIELDS;
+ header.bV4RedMask = 0x00FF0000;
+ header.bV4GreenMask = 0x0000FF00;
+ header.bV4BlueMask = 0x000000FF;
+
+ HDC nulldc = ::GetDC(NULL);
+
+ HDC dc = ::CreateCompatibleDC(nulldc);
+
+ ::ReleaseDC(nullptr, nulldc);
+
+ if (!dc) {
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Failed to create DC for bitmap.";
+ return nullptr;
+ }
+
+ void* bits;
+ HBITMAP bitmap = ::CreateDIBSection(dc, (BITMAPINFO*)&header,
+ DIB_RGB_COLORS, &bits,
+ fileMapping, 0);
+
+ if (!bitmap) {
+ gfxCriticalError() << "Failed to create DIB section for a bitmap of size "
+ << aSize << " and mapSize " << mapSize;
+ ::CloseHandle(fileMapping);
+ ::DeleteDC(dc);
+ return nullptr;
+ }
+
+ ::SelectObject(dc, bitmap);
+
+ RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(dc, 0);
+ if (surface->CairoStatus())
+ {
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Could not create surface, status: "
+ << surface->CairoStatus();
+ return nullptr;
+ }
+
+ HANDLE hostHandle = NULL;
+
+ if (!ipc::DuplicateHandle(fileMapping, aAllocator->GetParentPid(),
+ &hostHandle, 0, DUPLICATE_SAME_ACCESS)) {
+ gfxCriticalError() << "Failed to duplicate handle to parent process for surface.";
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ return nullptr;
+ }
+
+ return new ShmemDIBTextureData(aSize, aFormat, surface,
+ fileMapping, hostHandle,
+ dc, bitmap);
+}
+
+
+bool
+TextureHostDirectUpload::Lock()
+{
+ MOZ_ASSERT(!mIsLocked);
+ mIsLocked = true;
+ return true;
+}
+
+void
+TextureHostDirectUpload::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+void
+TextureHostDirectUpload::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+}
+
+void
+TextureHostDirectUpload::DeallocateDeviceData()
+{
+ if (mTextureSource) {
+ mTextureSource->DeallocateDeviceData();
+ }
+}
+
+bool
+TextureHostDirectUpload::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mTextureSource) {
+ Updated();
+ }
+
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+DIBTextureHost::DIBTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorDIB& aDescriptor)
+ : TextureHostDirectUpload(aFlags, SurfaceFormat::B8G8R8X8, IntSize())
+{
+ // We added an extra ref for transport, so we shouldn't AddRef now.
+ mSurface =
+ dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
+ MOZ_ASSERT(mSurface);
+
+ mSize = mSurface->GetSize();
+ mFormat = mSurface->GetSurfaceFormat();
+}
+
+void
+DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ if (mSurface->CairoStatus()) {
+ gfxWarning() << "Bad Cairo surface internal update " << mSurface->CairoStatus();
+ mTextureSource = nullptr;
+ return;
+ }
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat);
+
+ if (!surf || !mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+TextureHostFileMapping::TextureHostFileMapping(TextureFlags aFlags,
+ const SurfaceDescriptorFileMapping& aDescriptor)
+ : TextureHostDirectUpload(aFlags, aDescriptor.format(), aDescriptor.size())
+ , mFileMapping((HANDLE)aDescriptor.handle())
+{
+}
+
+TextureHostFileMapping::~TextureHostFileMapping()
+{
+ ::CloseHandle(mFileMapping);
+}
+
+UserDataKey kFileMappingKey;
+
+static void UnmapFileData(void* aData)
+{
+ MOZ_ASSERT(aData);
+ ::UnmapViewOfFile(aData);
+}
+
+void
+TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ uint8_t* data = nullptr;
+ int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height, BytesPerPixel(mFormat));
+ if (totalBytes > 0) {
+ data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, totalBytes);
+ }
+
+ if (data) {
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat);
+ if (surf) {
+ surf->AddUserData(&kFileMappingKey, data, UnmapFileData);
+ if (!mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+}
+}
diff --git a/gfx/layers/TextureDIB.h b/gfx/layers/TextureDIB.h
new file mode 100644
index 000000000..7eed7e58c
--- /dev/null
+++ b/gfx/layers/TextureDIB.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREDIB_H
+#define MOZILLA_GFX_TEXTUREDIB_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "gfxWindowsPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+class DIBTextureData : public TextureData
+{
+public:
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+protected:
+ DIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface)
+ : mSurface(aSurface)
+ , mSize(aSize)
+ , mFormat(aFormat)
+ {
+ MOZ_ASSERT(aSurface);
+ }
+
+ RefPtr<gfxWindowsSurface> mSurface;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+};
+
+/**
+ * This is meant for a texture host which does a direct upload from
+ * Updated to a Compositor specific DataTextureSource and therefor doesn't
+ * need any specific Lock/Unlock magic.
+ */
+class TextureHostDirectUpload : public TextureHost
+{
+public:
+ TextureHostDirectUpload(TextureFlags aFlags,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize)
+ : TextureHost(aFlags)
+ , mFormat(aFormat)
+ , mSize(aSize)
+ , mIsLocked(false)
+ { }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual bool HasIntermediateBuffer() const { return true; }
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+protected:
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<Compositor> mCompositor;
+ gfx::SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ bool mIsLocked;
+};
+
+class DIBTextureHost : public TextureHostDirectUpload
+{
+public:
+ DIBTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorDIB& aDescriptor);
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // TODO: cf bug 872568
+ }
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ RefPtr<gfxWindowsSurface> mSurface;
+};
+
+class TextureHostFileMapping : public TextureHostDirectUpload
+{
+public:
+ TextureHostFileMapping(TextureFlags aFlags,
+ const SurfaceDescriptorFileMapping& aDescriptor);
+ ~TextureHostFileMapping();
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ MOZ_CRASH("GFX: TextureHostFileMapping::GetAsSurface not implemented");
+ // Not implemented! It would be tricky to keep track of the
+ // scope of the file mapping. We could do this through UserData
+ // on the DataSourceSurface but we don't need this right now.
+ }
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ HANDLE mFileMapping;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_TEXTUREDIB_H */
diff --git a/gfx/layers/TextureWrapperImage.cpp b/gfx/layers/TextureWrapperImage.cpp
new file mode 100644
index 000000000..e1ead5d68
--- /dev/null
+++ b/gfx/layers/TextureWrapperImage.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "TextureWrapperImage.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+TextureWrapperImage::TextureWrapperImage(TextureClient* aClient, const IntRect& aPictureRect)
+ : Image(nullptr, ImageFormat::TEXTURE_WRAPPER),
+ mPictureRect(aPictureRect),
+ mTextureClient(aClient)
+{
+}
+
+TextureWrapperImage::~TextureWrapperImage()
+{
+}
+
+gfx::IntSize
+TextureWrapperImage::GetSize()
+{
+ return mTextureClient->GetSize();
+}
+
+gfx::IntRect
+TextureWrapperImage::GetPictureRect()
+{
+ return mPictureRect;
+}
+
+already_AddRefed<gfx::SourceSurface>
+TextureWrapperImage::GetAsSourceSurface()
+{
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_READ);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt = mTextureClient->BorrowDrawTarget();
+ if (!dt) {
+ return nullptr;
+ }
+
+ return dt->Snapshot();
+}
+
+TextureClient*
+TextureWrapperImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/TextureWrapperImage.h b/gfx/layers/TextureWrapperImage.h
new file mode 100644
index 000000000..ef67c3dd4
--- /dev/null
+++ b/gfx/layers/TextureWrapperImage.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
+#define GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
+
+#include "mozilla/RefPtr.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+// Wraps a TextureClient into an Image. This may only be used on the main
+// thread, and only with TextureClients that support BorrowDrawTarget().
+class TextureWrapperImage final : public Image
+{
+public:
+ TextureWrapperImage(TextureClient* aClient, const gfx::IntRect& aPictureRect);
+ ~TextureWrapperImage() override;
+
+ gfx::IntSize GetSize() override;
+ gfx::IntRect GetPictureRect() override;
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+private:
+ gfx::IntRect mPictureRect;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_LAYERS_TEXTUREWRAPPINGIMAGE_H_
diff --git a/gfx/layers/TiledLayerBuffer.h b/gfx/layers/TiledLayerBuffer.h
new file mode 100644
index 000000000..c1efcc39e
--- /dev/null
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -0,0 +1,220 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TILEDLAYERBUFFER_H
+#define GFX_TILEDLAYERBUFFER_H
+
+// Debug defines
+//#define GFX_TILEDLAYER_DEBUG_OVERLAY
+//#define GFX_TILEDLAYER_PREF_WARNINGS
+//#define GFX_TILEDLAYER_RETAINING_LOG
+
+#include <stdint.h> // for uint16_t, uint32_t
+#include <sys/types.h> // for int32_t
+#include "LayersLogging.h" // for print_stderr
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h" // for gfxCriticalError
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+
+struct TileUnit {};
+template<> struct IsPixel<TileUnit> : mozilla::TrueType {};
+
+namespace layers {
+
+// You can enable all the TILING_LOG print statements by
+// changing the 0 to a 1 in the following #define.
+#define ENABLE_TILING_LOG 0
+
+#if ENABLE_TILING_LOG
+# define TILING_LOG(...) printf_stderr(__VA_ARGS__);
+#else
+# define TILING_LOG(...)
+#endif
+
+// Normal integer division truncates towards zero,
+// we instead want to floor to hangle negative numbers.
+static inline int floor_div(int a, int b)
+{
+ int rem = a % b;
+ int div = a/b;
+ if (rem == 0) {
+ return div;
+ } else {
+ // If the signs are different substract 1.
+ int sub;
+ sub = a ^ b;
+ // The results of this shift is either 0 or -1.
+ sub >>= 8*sizeof(int)-1;
+ return div+sub;
+ }
+}
+
+// Tiles are aligned to a grid with one of the grid points at (0,0) and other
+// grid points spaced evenly in the x- and y-directions by GetTileSize()
+// multiplied by mResolution. GetScaledTileSize() provides convenience for
+// accessing these values.
+//
+// This tile buffer stores a valid region, which defines the areas that have
+// up-to-date content. The contents of tiles within this region will be reused
+// from paint to paint. It also stores the region that was modified in the last
+// paint operation; this is useful when one tiled layer buffer shadows another
+// (as in an off-main-thread-compositing scenario), so that the shadow tiled
+// layer buffer can correctly reflect the updates of the master layer buffer.
+//
+// The associated Tile may be of any type as long as the derived class can
+// validate and return tiles of that type. Tiles will be frequently copied, so
+// the tile type should be a reference or some other type with an efficient
+// copy constructor.
+//
+// The contents of the tile buffer will be rendered at the resolution specified
+// in mResolution, which can be altered with SetResolution. The resolution
+// should always be a factor of the tile length, to avoid tiles covering
+// non-integer amounts of pixels.
+
+// Size and Point in number of tiles rather than in pixels
+typedef gfx::IntSizeTyped<TileUnit> TileIntSize;
+typedef gfx::IntPointTyped<TileUnit> TileIntPoint;
+
+/**
+ * Stores the origin and size of a tile buffer and handles switching between
+ * tile indices and tile positions.
+ *
+ * Tile positions in TileIntPoint take the first tile offset into account which
+ * means that two TilesPlacement of the same layer and resolution give tile
+ * positions in the same coordinate space (useful when changing the offset and/or
+ * size of a tile buffer).
+ */
+struct TilesPlacement {
+ // in tiles
+ TileIntPoint mFirst;
+ TileIntSize mSize;
+
+ TilesPlacement(int aFirstX, int aFirstY,
+ int aRetainedWidth, int aRetainedHeight)
+ : mFirst(aFirstX, aFirstY)
+ , mSize(aRetainedWidth, aRetainedHeight)
+ {}
+
+ int TileIndex(TileIntPoint aPosition) const {
+ return (aPosition.x - mFirst.x) * mSize.height + aPosition.y - mFirst.y;
+ }
+
+ TileIntPoint TilePosition(size_t aIndex) const {
+ return TileIntPoint(
+ mFirst.x + aIndex / mSize.height,
+ mFirst.y + aIndex % mSize.height
+ );
+ }
+
+ bool HasTile(TileIntPoint aPosition) const {
+ return aPosition.x >= mFirst.x && aPosition.x < mFirst.x + mSize.width &&
+ aPosition.y >= mFirst.y && aPosition.y < mFirst.y + mSize.height;
+ }
+};
+
+
+// Given a position i, this function returns the position inside the current tile.
+inline int GetTileStart(int i, int aTileLength) {
+ return (i >= 0) ? (i % aTileLength)
+ : ((aTileLength - (-i % aTileLength)) %
+ aTileLength);
+}
+
+// Rounds the given coordinate down to the nearest tile boundary.
+inline int RoundDownToTileEdge(int aX, int aTileLength) { return aX - GetTileStart(aX, aTileLength); }
+
+template<typename Derived, typename Tile>
+class TiledLayerBuffer
+{
+public:
+ TiledLayerBuffer()
+ : mTiles(0, 0, 0, 0)
+ , mResolution(1)
+ , mTileSize(mozilla::gfx::gfxVars::TileSize())
+ {}
+
+ ~TiledLayerBuffer() {}
+
+ gfx::IntPoint GetTileOffset(TileIntPoint aPosition) const {
+ gfx::IntSize scaledTileSize = GetScaledTileSize();
+ return gfx::IntPoint(aPosition.x * scaledTileSize.width,
+ aPosition.y * scaledTileSize.height) + mTileOrigin;
+ }
+
+ const TilesPlacement& GetPlacement() const { return mTiles; }
+
+ const gfx::IntSize& GetTileSize() const { return mTileSize; }
+
+ gfx::IntSize GetScaledTileSize() const { return gfx::IntSize::Round(gfx::Size(mTileSize) / mResolution); }
+
+ unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
+
+ Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
+
+ const nsIntRegion& GetValidRegion() const { return mValidRegion; }
+ const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
+ void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
+
+ // Get and set draw scaling. mResolution affects the resolution at which the
+ // contents of the buffer are drawn. mResolution has no effect on the
+ // coordinate space of the valid region, but does affect the size of an
+ // individual tile's rect in relation to the valid region.
+ // Setting the resolution will invalidate the buffer.
+ float GetResolution() const { return mResolution; }
+ bool IsLowPrecision() const { return mResolution < 1; }
+
+ void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml,
+ TextureDumpMode aCompress);
+
+protected:
+
+ nsIntRegion mValidRegion;
+ nsIntRegion mPaintedRegion;
+
+ /**
+ * mRetainedTiles is a rectangular buffer of mTiles.mSize.width x mTiles.mSize.height
+ * stored as column major with the same origin as mValidRegion.GetBounds().
+ * Any tile that does not intersect mValidRegion is a PlaceholderTile.
+ * Only the region intersecting with mValidRegion should be read from a tile,
+ * another other region is assumed to be uninitialized. The contents of the
+ * tiles is scaled by mResolution.
+ */
+ nsTArray<Tile> mRetainedTiles;
+ TilesPlacement mTiles;
+ float mResolution;
+ gfx::IntSize mTileSize;
+ gfx::IntPoint mTileOrigin;
+};
+
+template<typename Derived, typename Tile> void
+TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress)
+{
+ for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
+ const TileIntPoint tilePosition = mTiles.TilePosition(i);
+ gfx::IntPoint tileOffset = GetTileOffset(tilePosition);
+
+ aStream << "\n" << aPrefix << "Tile (x=" <<
+ tileOffset.x << ", y=" << tileOffset.y << "): ";
+ if (!mRetainedTiles[i].IsPlaceholderTile()) {
+ mRetainedTiles[i].DumpTexture(aStream, aCompress);
+ } else {
+ aStream << "empty tile";
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_TILEDLAYERBUFFER_H
diff --git a/gfx/layers/TransactionIdAllocator.h b/gfx/layers/TransactionIdAllocator.h
new file mode 100644
index 000000000..f6940a35e
--- /dev/null
+++ b/gfx/layers/TransactionIdAllocator.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TRANSACTION_ID_ALLOCATOR_H
+#define GFX_TRANSACTION_ID_ALLOCATOR_H
+
+#include "nsISupportsImpl.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace layers {
+
+class TransactionIdAllocator {
+protected:
+ virtual ~TransactionIdAllocator() {}
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(TransactionIdAllocator)
+
+ /**
+ * Allocate a unique id number for the current refresh tick, can
+ * only be called while IsInRefresh().
+ *
+ * If too many id's are allocated without being returned then
+ * the refresh driver will suspend until they catch up.
+ */
+ virtual uint64_t GetTransactionId() = 0;
+
+ /**
+ * Return the transaction id that for the last non-revoked transaction.
+ * This allows the caller to tell whether a composite was triggered by
+ * a paint that occurred after a call to TransactionId().
+ */
+ virtual uint64_t LastTransactionId() const = 0;
+
+ /**
+ * Notify that all work (including asynchronous composites)
+ * for a given transaction id has been completed.
+ *
+ * If the refresh driver has been suspended because
+ * of having too many outstanding id's, then this may
+ * resume it.
+ */
+ virtual void NotifyTransactionCompleted(uint64_t aTransactionId) = 0;
+
+ /**
+ * Revoke a transaction id that isn't needed to track
+ * completion of asynchronous work. This is similar
+ * to NotifyTransactionCompleted except avoids
+ * return ordering issues.
+ */
+ virtual void RevokeTransactionId(uint64_t aTransactionId) = 0;
+
+ /**
+ * Get the start time of the current refresh tick.
+ */
+ virtual mozilla::TimeStamp GetTransactionStart() = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+
+#endif /* GFX_TRANSACTION_ID_ALLOCATOR_H */
diff --git a/gfx/layers/TreeTraversal.h b/gfx/layers/TreeTraversal.h
new file mode 100644
index 000000000..80c1f7fb7
--- /dev/null
+++ b/gfx/layers/TreeTraversal.h
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_TreeTraversal_h
+#define mozilla_layers_TreeTraversal_h
+
+#include <queue>
+
+namespace mozilla {
+namespace layers {
+
+
+/*
+ * Returned by |aPostAction| and |aPreAction| in ForEachNode, indicates
+ * the behavior to follow either action:
+ *
+ * TraversalFlag::Skip - the node's children are not traversed. If this
+ * flag is returned by |aPreAction|, |aPostAction| is skipped for the
+ * current node, as well.
+ * TraversalFlag::Continue - traversal continues normally.
+ * TraversalFlag::Abort - traversal stops immediately.
+ */
+enum class TraversalFlag { Skip, Continue, Abort };
+
+/*
+ * Iterator types to be specified in traversal function calls:
+ *
+ * ForwardIterator - for nodes using GetFirstChild() and GetNextSibling()
+ * ReverseIterator - for nodes using GetLastChild() and GetPrevSibling()
+ */
+class ForwardIterator
+{
+ public:
+ template <typename Node>
+ static Node* FirstChild(Node* n) {
+ return n->GetFirstChild();
+ }
+ template <typename Node>
+ static Node* NextSibling(Node* n) {
+ return n->GetNextSibling();
+ }
+ template <typename Node>
+ static Node FirstChild(Node n) {
+ return n.GetFirstChild();
+ }
+ template <typename Node>
+ static Node NextSibling(Node n) {
+ return n.GetNextSibling();
+ }
+};
+class ReverseIterator
+{
+ public:
+ template <typename Node>
+ static Node* FirstChild(Node* n) {
+ return n->GetLastChild();
+ }
+ template <typename Node>
+ static Node* NextSibling(Node* n) {
+ return n->GetPrevSibling();
+ }
+ template <typename Node>
+ static Node FirstChild(Node n) {
+ return n.GetLastChild();
+ }
+ template <typename Node>
+ static Node NextSibling(Node n) {
+ return n.GetPrevSibling();
+ }
+};
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
+ *
+ * Returns true if traversal aborted, false if continued normally. If
+ * TraversalFlag::Skip is returned in |aPreAction|, then |aPostAction|
+ * is not performed.
+ *
+ * |Iterator| should have static methods named NextSibling() and FirstChild()
+ * that accept an argument of type Node. For convenience, classes
+ * |ForwardIterator| and |ReverseIterator| are provided which implement these
+ * methods as GetNextSibling()/GetFirstChild() and GetPrevSibling()/GetLastChild(),
+ * respectively.
+ */
+template <typename Iterator, typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value &&
+ IsSame<decltype(aPostAction(aRoot)),TraversalFlag>::value, bool>::Type
+{
+ if (!aRoot) {
+ return false;
+ }
+
+ TraversalFlag result = aPreAction(aRoot);
+
+ if (result == TraversalFlag::Abort) {
+ return true;
+ }
+
+ if (result == TraversalFlag::Continue) {
+ for (Node child = Iterator::FirstChild(aRoot);
+ child;
+ child = Iterator::NextSibling(child)) {
+ bool abort = ForEachNode<Iterator>(child, aPreAction, aPostAction);
+ if (abort) {
+ return true;
+ }
+ }
+
+ result = aPostAction(aRoot);
+
+ if (result == TraversalFlag::Abort) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
+ */
+template <typename Iterator, typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value &&
+ IsSame<decltype(aPostAction(aRoot)),void>::value, void>::Type
+{
+ if (!aRoot) {
+ return;
+ }
+
+ aPreAction(aRoot);
+
+ for (Node child = Iterator::FirstChild(aRoot);
+ child;
+ child = Iterator::NextSibling(child)) {
+ ForEachNode<Iterator>(child, aPreAction, aPostAction);
+ }
+
+ aPostAction(aRoot);
+}
+
+/*
+ * ForEachNode pre-order traversal, using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PreAction>
+auto ForEachNode(Node aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+ return ForEachNode<Iterator>(aRoot, aPreAction, [](Node aNode){ return TraversalFlag::Continue; });
+}
+
+/*
+ * ForEachNode pre-order, not using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PreAction>
+auto ForEachNode(Node aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value, void>::Type
+{
+ ForEachNode<Iterator>(aRoot, aPreAction, [](Node aNode){});
+}
+
+/*
+ * ForEachNode post-order traversal, using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+ return ForEachNode<Iterator>(aRoot, [](Node aNode){ return TraversalFlag::Continue; }, aPostAction);
+}
+
+/*
+ * ForEachNode post-order, not using TraversalFlag.
+ */
+template <typename Iterator, typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), void>::value, void>::Type
+{
+ ForEachNode<Iterator>(aRoot, [](Node aNode){}, aPostAction);
+}
+
+/*
+ * Do a breadth-first search of the tree rooted at |aRoot|, and return the
+ * first visited node that satisfies |aCondition|, or nullptr if no such node
+ * was found.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node()
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node BreadthFirstSearch(Node aRoot, const Condition& aCondition)
+{
+ if (!aRoot) {
+ return Node();
+ }
+
+ std::queue<Node> queue;
+ queue.push(aRoot);
+ while (!queue.empty()) {
+ Node node = queue.front();
+ queue.pop();
+
+ if (aCondition(node)) {
+ return node;
+ }
+
+ for (Node child = Iterator::FirstChild(node);
+ child;
+ child = Iterator::NextSibling(child)) {
+ queue.push(child);
+ }
+ }
+
+ return Node();
+}
+
+/*
+ * Do a pre-order, depth-first search of the tree rooted at |aRoot|, and
+ * return the first visited node that satisfies |aCondition|, or nullptr
+ * if no such node was found.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node().
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node DepthFirstSearch(Node aRoot, const Condition& aCondition)
+{
+ Node result = Node();
+
+ ForEachNode<Iterator>(aRoot,
+ [&aCondition, &result](Node aNode)
+ {
+ if (aCondition(aNode)) {
+ result = aNode;
+ return TraversalFlag::Abort;
+ }
+
+ return TraversalFlag::Continue;
+ });
+
+ return result;
+}
+
+/*
+ * Perform a post-order, depth-first search starting at aRoot.
+ *
+ * |Iterator| and |Node| have all the same requirements seen in ForEachNode()'s
+ * definition, but in addition to those, |Node| must be able to express a null
+ * value, returned from Node().
+ */
+template <typename Iterator, typename Node, typename Condition>
+Node DepthFirstSearchPostOrder(Node aRoot, const Condition& aCondition)
+{
+ Node result = Node();
+
+ ForEachNodePostOrder<Iterator>(aRoot,
+ [&aCondition, &result](Node aNode)
+ {
+ if (aCondition(aNode)) {
+ result = aNode;
+ return TraversalFlag::Abort;
+ }
+
+ return TraversalFlag::Continue;
+ });
+
+ return result;
+}
+
+}
+}
+
+#endif // mozilla_layers_TreeTraversal_h
diff --git a/gfx/layers/apz/public/CompositorController.h b/gfx/layers/apz/public/CompositorController.h
new file mode 100644
index 000000000..2560240e2
--- /dev/null
+++ b/gfx/layers/apz/public/CompositorController.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorController_h
+#define mozilla_layers_CompositorController_h
+
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class CompositorController
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+ virtual void ScheduleRenderOnCompositorThread() = 0;
+ virtual void ScheduleHideAllPluginWindows() = 0;
+ virtual void ScheduleShowAllPluginWindows() = 0;
+
+protected:
+ virtual ~CompositorController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorController_h
diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h
new file mode 100644
index 000000000..d572a410b
--- /dev/null
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GeckoContentController_h
+#define mozilla_layers_GeckoContentController_h
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "InputData.h" // for PinchGestureInput
+#include "Units.h" // for CSSPoint, CSSRect, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/EventForwards.h" // for Modifiers
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class Runnable;
+
+namespace layers {
+
+class GeckoContentController
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController)
+
+ /**
+ * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
+ * Implementations per-platform are responsible for actually handling this.
+ *
+ * This method must always be called on the repaint thread, which depends
+ * on the GeckoContentController. For ChromeProcessController it is the
+ * Gecko main thread, while for RemoteContentController it is the compositor
+ * thread where it can send IPDL messages.
+ */
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
+
+ /**
+ * Different types of tap-related events that can be sent in
+ * the HandleTap function. The names should be relatively self-explanatory.
+ * Note that the eLongTapUp will always be preceded by an eLongTap, but not
+ * all eLongTap notifications will be followed by an eLongTapUp (for instance,
+ * if the user moves their finger after triggering the long-tap but before
+ * lifting it).
+ * The difference between eDoubleTap and eSecondTap is subtle - the eDoubleTap
+ * is for an actual double-tap "gesture" while eSecondTap is for the same user
+ * input but where a double-tap gesture is not allowed. This is used to fire
+ * a click event with detail=2 to web content (similar to what a mouse double-
+ * click would do).
+ */
+ enum class TapType {
+ eSingleTap,
+ eDoubleTap,
+ eSecondTap,
+ eLongTap,
+ eLongTapUp,
+
+ eSentinel,
+ };
+
+ /**
+ * Requests handling of a tap event. |aPoint| is in LD pixels, relative to the
+ * current scroll offset.
+ */
+ virtual void HandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) = 0;
+
+ /**
+ * When the apz.allow_zooming pref is set to false, the APZ will not
+ * translate pinch gestures to actual zooming. Instead, it will call this
+ * method to notify gecko of the pinch gesture, and allow it to deal with it
+ * however it wishes. Note that this function is not called if the pinch is
+ * prevented by content calling preventDefault() on the touch events, or via
+ * use of the touch-action property.
+ * @param aType One of PINCHGESTURE_START, PINCHGESTURE_SCALE, or
+ * PINCHGESTURE_END, indicating the phase of the pinch.
+ * @param aGuid The guid of the APZ that is detecting the pinch. This is
+ * generally the root APZC for the layers id.
+ * @param aSpanChange For the START or END event, this is always 0.
+ * For a SCALE event, this is the difference in span between the
+ * previous state and the new state.
+ * @param aModifiers The keyboard modifiers depressed during the pinch.
+ */
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) = 0;
+
+ /**
+ * Schedules a runnable to run on the controller/UI thread at some time
+ * in the future.
+ * This method must always be called on the controller thread.
+ */
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) = 0;
+
+ /**
+ * Returns true if we are currently on the thread that can send repaint requests.
+ */
+ virtual bool IsRepaintThread() = 0;
+
+ /**
+ * Runs the given task on the "repaint" thread.
+ */
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) = 0;
+
+ enum class APZStateChange {
+ /**
+ * APZ started modifying the view (including panning, zooming, and fling).
+ */
+ eTransformBegin,
+ /**
+ * APZ finished modifying the view.
+ */
+ eTransformEnd,
+ /**
+ * APZ started a touch.
+ * |aArg| is 1 if touch can be a pan, 0 otherwise.
+ */
+ eStartTouch,
+ /**
+ * APZ started a pan.
+ */
+ eStartPanning,
+ /**
+ * APZ finished processing a touch.
+ * |aArg| is 1 if touch was a click, 0 otherwise.
+ */
+ eEndTouch,
+
+ // Sentinel value for IPC, this must be the last item in the enum and
+ // should not be used as an actual message value.
+ eSentinel
+ };
+ /**
+ * General notices of APZ state changes for consumers.
+ * |aGuid| identifies the APZC originating the state change.
+ * |aChange| identifies the type of state change
+ * |aArg| is used by some state changes to pass extra information (see
+ * the documentation for each state change above)
+ */
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg = 0) {}
+
+ /**
+ * Notify content of a MozMouseScrollFailed event.
+ */
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+ {}
+
+ /**
+ * Notify content that the repaint requests have been flushed.
+ */
+ virtual void NotifyFlushComplete() = 0;
+
+ virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
+ virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
+ virtual void SetScrollingRootContent(bool isRootContent) {}
+
+ GeckoContentController() {}
+
+ /**
+ * Needs to be called on the main thread.
+ */
+ virtual void Destroy() {}
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~GeckoContentController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_GeckoContentController_h
diff --git a/gfx/layers/apz/public/IAPZCTreeManager.cpp b/gfx/layers/apz/public/IAPZCTreeManager.cpp
new file mode 100644
index 000000000..372257ae4
--- /dev/null
+++ b/gfx/layers/apz/public/IAPZCTreeManager.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/IAPZCTreeManager.h"
+
+#include "gfxPrefs.h" // for gfxPrefs
+#include "InputData.h" // for InputData, etc
+#include "mozilla/EventStateManager.h" // for WheelPrefs
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
+#include "mozilla/MouseEvents.h" // for WidgetMouseEvent
+#include "mozilla/TouchEvents.h" // for WidgetTouchEvent
+
+namespace mozilla {
+namespace layers {
+
+static bool
+WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
+{
+ return aEvent.mMessage == eMouseMove ||
+ aEvent.mMessage == eMouseDown ||
+ aEvent.mMessage == eMouseUp ||
+ aEvent.mMessage == eDragEnd;
+}
+
+// Returns whether or not a wheel event action will be (or was) performed by
+// APZ. If this returns true, the event must not perform a synchronous
+// scroll.
+//
+// Even if this returns false, all wheel events in APZ-aware widgets must
+// be sent through APZ so they are transformed correctly for TabParent.
+static bool
+WillHandleWheelEvent(WidgetWheelEvent* aEvent)
+{
+ return EventStateManager::WheelEventIsScrollAction(aEvent) &&
+ (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
+ aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE);
+}
+
+nsEventStatus
+IAPZCTreeManager::ReceiveInputEvent(
+ WidgetInputEvent& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+ // it if the input event goes into a block.
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = 0;
+ }
+
+ switch (aEvent.mClass) {
+ case eMouseEventClass:
+ case eDragEventClass: {
+
+ WidgetMouseEvent& mouseEvent = *aEvent.AsMouseEvent();
+
+ // Note, we call this before having transformed the reference point.
+ if (mouseEvent.IsReal()) {
+ UpdateWheelTransaction(mouseEvent.mRefPoint, mouseEvent.mMessage);
+ }
+
+ if (WillHandleMouseEvent(mouseEvent)) {
+
+ MouseInput input(mouseEvent);
+ input.mOrigin = ScreenPoint(mouseEvent.mRefPoint.x, mouseEvent.mRefPoint.y);
+
+ nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+
+ mouseEvent.mRefPoint.x = input.mOrigin.x;
+ mouseEvent.mRefPoint.y = input.mOrigin.y;
+ mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+ return status;
+
+ }
+
+ TransformEventRefPoint(&mouseEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+ }
+ case eTouchEventClass: {
+
+ WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
+ MultiTouchInput touchInput(touchEvent);
+ nsEventStatus result = ReceiveInputEvent(touchInput, aOutTargetGuid, aOutInputBlockId);
+ // touchInput was modified in-place to possibly remove some
+ // touch points (if we are overscrolled), and the coordinates were
+ // modified using the APZ untransform. We need to copy these changes
+ // back into the WidgetInputEvent.
+ touchEvent.mTouches.Clear();
+ touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
+ for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
+ *touchEvent.mTouches.AppendElement() =
+ touchInput.mTouches[i].ToNewDOMTouch();
+ }
+ touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
+ return result;
+
+ }
+ case eWheelEventClass: {
+ WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
+
+ if (WillHandleWheelEvent(&wheelEvent)) {
+
+ ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
+ if (gfxPrefs::SmoothScrollEnabled() &&
+ ((wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE &&
+ gfxPrefs::WheelSmoothScrollEnabled()) ||
+ (wheelEvent.mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PAGE &&
+ gfxPrefs::PageSmoothScrollEnabled())))
+ {
+ scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
+ }
+
+ ScreenPoint origin(wheelEvent.mRefPoint.x, wheelEvent.mRefPoint.y);
+ ScrollWheelInput input(wheelEvent.mTime, wheelEvent.mTimeStamp, 0,
+ scrollMode,
+ ScrollWheelInput::DeltaTypeForDeltaMode(
+ wheelEvent.mDeltaMode),
+ origin,
+ wheelEvent.mDeltaX, wheelEvent.mDeltaY,
+ wheelEvent.mAllowToOverrideSystemScrollSpeed);
+
+ // We add the user multiplier as a separate field, rather than premultiplying
+ // it, because if the input is converted back to a WidgetWheelEvent, then
+ // EventStateManager would apply the delta a second time. We could in theory
+ // work around this by asking ESM to customize the event much sooner, and
+ // then save the "mCustomizedByUserPrefs" bit on ScrollWheelInput - but for
+ // now, this seems easier.
+ EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
+ &input.mUserDeltaMultiplierX,
+ &input.mUserDeltaMultiplierY);
+
+ nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
+ wheelEvent.mRefPoint.x = input.mOrigin.x;
+ wheelEvent.mRefPoint.y = input.mOrigin.y;
+ wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
+ return status;
+ }
+
+ UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+ TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+
+ }
+ default: {
+
+ UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
+ TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
+ return nsEventStatus_eIgnore;
+
+ }
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Invalid WidgetInputEvent type.");
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/public/IAPZCTreeManager.h b/gfx/layers/apz/public/IAPZCTreeManager.h
new file mode 100644
index 000000000..383181e8f
--- /dev/null
+++ b/gfx/layers/apz/public/IAPZCTreeManager.h
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_IAPZCTreeManager_h
+#define mozilla_layers_IAPZCTreeManager_h
+
+#include <stdint.h> // for uint64_t, uint32_t
+
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus
+#include "mozilla/layers/APZUtils.h" // for HitTestResult
+#include "nsTArrayForwardDeclare.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "Units.h" // for CSSPoint, CSSRect, etc
+
+namespace mozilla {
+class InputData;
+
+namespace layers {
+
+enum AllowedTouchBehavior {
+ NONE = 0,
+ VERTICAL_PAN = 1 << 0,
+ HORIZONTAL_PAN = 1 << 1,
+ PINCH_ZOOM = 1 << 2,
+ DOUBLE_TAP_ZOOM = 1 << 3,
+ UNKNOWN = 1 << 4
+};
+
+enum ZoomToRectBehavior : uint32_t {
+ DEFAULT_BEHAVIOR = 0,
+ DISABLE_ZOOM_OUT = 1 << 0,
+ PAN_INTO_VIEW_ONLY = 1 << 1,
+ ONLY_ZOOM_TO_DEFAULT_SCALE = 1 << 2
+};
+
+class AsyncDragMetrics;
+
+class IAPZCTreeManager {
+ NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
+
+public:
+
+ /**
+ * General handler for incoming input events. Manipulates the frame metrics
+ * based on what type of input it is. For example, a PinchGestureEvent will
+ * cause scaling. This should only be called externally to this class, and
+ * must be called on the controller thread.
+ *
+ * This function transforms |aEvent| to have its coordinates in DOM space.
+ * This is so that the event can be passed through the DOM and content can
+ * handle them. The event may need to be converted to a WidgetInputEvent
+ * by the caller if it wants to do this.
+ *
+ * The following values may be returned by this function:
+ * nsEventStatus_eConsumeNoDefault is returned to indicate the
+ * APZ is consuming this event and the caller should discard the event with
+ * extreme prejudice. The exact scenarios under which this is returned is
+ * implementation-dependent and may vary.
+ * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
+ * use this event. This might be because it was directed at a point on
+ * the screen where there was no APZ, or because the thing the user was
+ * trying to do was not allowed. (For example, attempting to pan a
+ * non-pannable document).
+ * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
+ * code may have used this event to do some user-visible thing. Note that
+ * in some cases CONSUMED is returned even if the event was NOT used. This
+ * is because we cannot always know at the time of event delivery whether
+ * the event will be used or not. So we err on the side of sending
+ * CONSUMED when we are uncertain.
+ *
+ * @param aEvent input event object; is modified in-place
+ * @param aOutTargetGuid returns the guid of the apzc this event was
+ * delivered to. May be null.
+ * @param aOutInputBlockId returns the id of the input block that this event
+ * was added to, if that was the case. May be null.
+ */
+ virtual nsEventStatus ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) = 0;
+
+ /**
+ * WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an
+ * already-existing instance of an WidgetInputEvent which may be an
+ * WidgetTouchEvent) to have its coordinates in DOM space. This is so that the
+ * event can be passed through the DOM and content can handle them.
+ *
+ * NOTE: Be careful of invoking the WidgetInputEvent variant. This can only be
+ * called on the main thread. See widget/InputData.h for more information on
+ * why we have InputData and WidgetInputEvent separated. If this function is
+ * used, the controller thread must be the main thread, or undefined behaviour
+ * may occur.
+ * NOTE: On unix, mouse events are treated as touch and are forwarded
+ * to the appropriate apz as such.
+ *
+ * See documentation for other ReceiveInputEvent above.
+ */
+ nsEventStatus ReceiveInputEvent(
+ WidgetInputEvent& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId);
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up. |aRect| must be given in CSS pixels, relative to the document.
+ * |aFlags| is a combination of the ZoomToRectBehavior enum values.
+ */
+ virtual void ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) = 0;
+
+ /**
+ * If we have touch listeners, this should always be called when we know
+ * definitively whether or not content has preventDefaulted any touch events
+ * that have come in. If |aPreventDefault| is true, any touch events in the
+ * queue will be discarded. This function must be called on the controller
+ * thread.
+ */
+ virtual void ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) = 0;
+
+ /**
+ * When the event regions code is enabled, this function should be invoked to
+ * to confirm the target of the input block. This is only needed in cases
+ * where the initial input event of the block hit a dispatch-to-content region
+ * but is safe to call for all input blocks. This function should always be
+ * invoked on the controller thread.
+ * The different elements in the array of targets correspond to the targets
+ * for the different touch points. In the case where the touch point has no
+ * target, or the target is not a scrollable frame, the target's |mScrollId|
+ * should be set to FrameMetrics::NULL_SCROLL_ID.
+ */
+ virtual void SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ * If the |aConstraints| is Nothing() then previously-provided constraints for
+ * the given |aGuid| are cleared.
+ */
+ virtual void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) = 0;
+
+ /**
+ * Cancels any currently running animation. Note that all this does is set the
+ * state of the AsyncPanZoomController back to NOTHING, but it is the
+ * animation's responsibility to check this before advancing.
+ */
+ virtual void CancelAnimation(const ScrollableLayerGuid &aGuid) = 0;
+
+ /**
+ * Adjusts the root APZC to compensate for a shift in the surface. See the
+ * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
+ * some more details. This is only currently needed due to surface shifts
+ * caused by the dynamic toolbar on Android.
+ */
+ virtual void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) = 0;
+
+ virtual void SetDPI(float aDpiValue) = 0;
+
+ /**
+ * Sets allowed touch behavior values for current touch-session for specific
+ * input block (determined by aInputBlock).
+ * Should be invoked by the widget. Each value of the aValues arrays
+ * corresponds to the different touch point that is currently active.
+ * Must be called after receiving the TOUCH_START event that starts the
+ * touch-session.
+ * This must be called on the controller thread.
+ */
+ virtual void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) = 0;
+
+ virtual void StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) = 0;
+
+ /**
+ * Function used to disable LongTap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ virtual void SetLongTapEnabled(bool aTapGestureEnabled) = 0;
+
+ /**
+ * Process touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ virtual void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) = 0;
+
+protected:
+
+ // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
+
+ virtual void TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) = 0;
+
+ virtual void UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) = 0;
+
+ // Discourage destruction outside of decref
+
+ virtual ~IAPZCTreeManager() { }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_IAPZCTreeManager_h
diff --git a/gfx/layers/apz/public/MetricsSharingController.h b/gfx/layers/apz/public/MetricsSharingController.h
new file mode 100644
index 000000000..090878573
--- /dev/null
+++ b/gfx/layers/apz/public/MetricsSharingController.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_MetricsSharingController_h
+#define mozilla_layers_MetricsSharingController_h
+
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutexHandle
+#include "mozilla/ipc/SharedMemoryBasic.h" // for SharedMemoryBasic
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class MetricsSharingController
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+ virtual base::ProcessId RemotePid() = 0;
+ virtual bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId) = 0;
+ virtual bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId) = 0;
+
+protected:
+ virtual ~MetricsSharingController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_MetricsSharingController_h
diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp
new file mode 100644
index 000000000..857ae5958
--- /dev/null
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -0,0 +1,2099 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stack>
+#include "APZCTreeManager.h"
+#include "AsyncPanZoomController.h"
+#include "Compositor.h" // for Compositor
+#include "DragTracker.h" // for DragTracker
+#include "gfxPrefs.h" // for gfxPrefs
+#include "HitTestingTreeNode.h" // for HitTestingTreeNode
+#include "InputBlockState.h" // for InputBlockState
+#include "InputData.h" // for InputData, etc
+#include "Layers.h" // for Layer, etc
+#include "mozilla/dom/Touch.h" // for Touch
+#include "mozilla/gfx/GPUParent.h" // for GPUParent
+#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "mozilla/TouchEvents.h"
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/EventStateManager.h" // for WheelPrefs
+#include "nsDebug.h" // for NS_WARNING
+#include "nsPoint.h" // for nsIntPoint
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "OverscrollHandoffState.h" // for OverscrollHandoffState
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
+#include "LayersLogging.h" // for Stringify
+#include "Units.h" // for ParentlayerPixel
+#include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
+#include "UnitTransforms.h" // for ViewAs
+
+#define ENABLE_APZCTM_LOGGING 0
+// #define ENABLE_APZCTM_LOGGING 1
+
+#if ENABLE_APZCTM_LOGGING
+# define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
+#else
+# define APZCTM_LOG(...)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+typedef mozilla::gfx::Point Point;
+typedef mozilla::gfx::Point4D Point4D;
+typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+
+float APZCTreeManager::sDPI = 160.0;
+
+struct APZCTreeManager::TreeBuildingState {
+ TreeBuildingState(const CompositorBridgeParent::LayerTreeState* const aLayerTreeState,
+ bool aIsFirstPaint, uint64_t aOriginatingLayersId,
+ APZTestData* aTestData, uint32_t aPaintSequence)
+ : mLayerTreeState(aLayerTreeState)
+ , mIsFirstPaint(aIsFirstPaint)
+ , mOriginatingLayersId(aOriginatingLayersId)
+ , mPaintLogger(aTestData, aPaintSequence)
+ {
+ }
+
+ // State that doesn't change as we recurse in the tree building
+ const CompositorBridgeParent::LayerTreeState* const mLayerTreeState;
+ const bool mIsFirstPaint;
+ const uint64_t mOriginatingLayersId;
+ const APZPaintLogHelper mPaintLogger;
+
+ // State that is updated as we perform the tree build
+
+ // A list of nodes that need to be destroyed at the end of the tree building.
+ // This is initialized with all nodes in the old tree, and nodes are removed
+ // from it as we reuse them in the new tree.
+ nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
+
+ // This map is populated as we place APZCs into the new tree. Its purpose is
+ // to facilitate re-using the same APZC for different layers that scroll
+ // together (and thus have the same ScrollableLayerGuid).
+ std::map<ScrollableLayerGuid, AsyncPanZoomController*> mApzcMap;
+};
+
+class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
+ : mTreeManager(aTreeManager)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
+ }
+ }
+
+ void Unregister()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
+ }
+ mTreeManager = nullptr;
+ }
+
+protected:
+ virtual ~CheckerboardFlushObserver() {}
+
+private:
+ RefPtr<APZCTreeManager> mTreeManager;
+};
+
+NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
+
+NS_IMETHODIMP
+APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t*)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTreeManager.get());
+
+ MutexAutoLock lock(mTreeManager->mTreeLock);
+ if (mTreeManager->mRootNode) {
+ ForEachNode<ReverseIterator>(mTreeManager->mRootNode.get(),
+ [](HitTestingTreeNode* aNode)
+ {
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->FlushActiveCheckerboardReport();
+ }
+ });
+ }
+ if (XRE_IsGPUProcess()) {
+ if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
+ nsCString topic("APZ:FlushActiveCheckerboard:Done");
+ Unused << gpu->SendNotifyUiObservers(topic);
+ }
+ } else {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
+ }
+ }
+ return NS_OK;
+}
+
+
+/*static*/ const ScreenMargin
+APZCTreeManager::CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ return AsyncPanZoomController::CalculatePendingDisplayPort(
+ aFrameMetrics, aVelocity);
+}
+
+APZCTreeManager::APZCTreeManager()
+ : mInputQueue(new InputQueue()),
+ mTreeLock("APZCTreeLock"),
+ mHitResultForInputBlock(HitNothing),
+ mRetainedTouchIdentifier(-1),
+ mApzcTreeLog("apzctree")
+{
+ RefPtr<APZCTreeManager> self(this);
+ NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
+ self->mFlushObserver = new CheckerboardFlushObserver(self);
+ }));
+ AsyncPanZoomController::InitializeGlobalState();
+ mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
+}
+
+APZCTreeManager::~APZCTreeManager()
+{
+}
+
+/*static*/ void
+APZCTreeManager::InitializeGlobalState()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ AsyncPanZoomController::InitializeGlobalState();
+}
+
+AsyncPanZoomController*
+APZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ return new AsyncPanZoomController(aLayersId, this, mInputQueue,
+ aController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
+}
+
+TimeStamp
+APZCTreeManager::GetFrameTime()
+{
+ return TimeStamp::Now();
+}
+
+void
+APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags> &aValues)
+{
+ mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void
+APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+ Layer* aRoot,
+ bool aIsFirstPaint,
+ uint64_t aOriginatingLayersId,
+ uint32_t aPaintSequenceNumber)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ MutexAutoLock lock(mTreeLock);
+
+ // For testing purposes, we log some data to the APZTestData associated with
+ // the layers id that originated this update.
+ APZTestData* testData = nullptr;
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
+ testData = &state->mApzTestData;
+ testData->StartNewPaint(aPaintSequenceNumber);
+ }
+ }
+
+ const CompositorBridgeParent::LayerTreeState* treeState =
+ CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
+ MOZ_ASSERT(treeState);
+ TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
+ testData, aPaintSequenceNumber);
+
+ // We do this business with collecting the entire tree into an array because otherwise
+ // it's very hard to determine which APZC instances need to be destroyed. In the worst
+ // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
+ // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
+ // completely different place. In scenario (a) we would want to destroy the APZC while
+ // walking the layer tree and noticing that the layer/APZC is no longer there. But if
+ // we do that then we run into a problem in scenario (b) because we might encounter that
+ // layer later during the walk. To handle both of these we have to 'remember' that the
+ // layer was not found, and then do the destroy only at the end of the tree walk after
+ // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
+ // as part of a recursive tree walk is hard and so maintaining a list and removing
+ // APZCs that are still alive is much simpler.
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [&state] (HitTestingTreeNode* aNode)
+ {
+ state.mNodesToDestroy.AppendElement(aNode);
+ });
+ mRootNode = nullptr;
+
+ if (aRoot) {
+ std::stack<gfx::TreeAutoIndent> indents;
+ std::stack<gfx::Matrix4x4> ancestorTransforms;
+ HitTestingTreeNode* parent = nullptr;
+ HitTestingTreeNode* next = nullptr;
+ uint64_t layersId = aRootLayerTreeId;
+ ancestorTransforms.push(Matrix4x4());
+
+ mApzcTreeLog << "[start]\n";
+ LayerMetricsWrapper root(aRoot);
+ mTreeLock.AssertCurrentThreadOwns();
+
+ ForEachNode<ReverseIterator>(root,
+ [&](LayerMetricsWrapper aLayerMetrics)
+ {
+ mApzcTreeLog << aLayerMetrics.Name() << '\t';
+
+ HitTestingTreeNode* node = PrepareNodeForLayer(aLayerMetrics,
+ aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
+ parent, next, state);
+ MOZ_ASSERT(node);
+ AsyncPanZoomController* apzc = node->GetApzc();
+ aLayerMetrics.SetApzc(apzc);
+
+ mApzcTreeLog << '\n';
+
+ // Accumulate the CSS transform between layers that have an APZC.
+ // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
+ // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
+ // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
+ // transform to layer L when we recurse into the children below. If we are at a layer
+ // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
+ // the new accumulation as we go down.
+ // If a transform is a perspective transform, it's ignored for this purpose
+ // (see bug 1168263).
+ Matrix4x4 currentTransform = aLayerMetrics.TransformIsPerspective() ? Matrix4x4() : aLayerMetrics.GetTransform();
+ if (!apzc) {
+ currentTransform = currentTransform * ancestorTransforms.top();
+ }
+ ancestorTransforms.push(currentTransform);
+
+ // Note that |node| at this point will not have any children, otherwise we
+ // we would have to set next to node->GetFirstChild().
+ MOZ_ASSERT(!node->GetFirstChild());
+ parent = node;
+ next = nullptr;
+ layersId = (aLayerMetrics.AsRefLayer() ? aLayerMetrics.AsRefLayer()->GetReferentId() : layersId);
+ indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
+ },
+ [&](LayerMetricsWrapper aLayerMetrics)
+ {
+ next = parent;
+ parent = parent->GetParent();
+ layersId = next->GetLayersId();
+ ancestorTransforms.pop();
+ indents.pop();
+ });
+
+ mApzcTreeLog << "[end]\n";
+ }
+
+ // We do not support tree structures where the root node has siblings.
+ MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
+
+ for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
+ APZCTM_LOG("Destroying node at %p with APZC %p\n",
+ state.mNodesToDestroy[i].get(),
+ state.mNodesToDestroy[i]->GetApzc());
+ state.mNodesToDestroy[i]->Destroy();
+ }
+
+#if ENABLE_APZCTM_LOGGING
+ // Make the hit-test tree line up with the layer dump
+ printf_stderr("APZCTreeManager (%p)\n", this);
+ mRootNode->Dump(" ");
+#endif
+}
+
+// Compute the clip region to be used for a layer with an APZC. This function
+// is only called for layers which actually have scrollable metrics and an APZC.
+static ParentLayerIntRegion
+ComputeClipRegion(GeckoContentController* aController,
+ const LayerMetricsWrapper& aLayer)
+{
+ ParentLayerIntRegion clipRegion;
+ if (aLayer.GetClipRect()) {
+ clipRegion = *aLayer.GetClipRect();
+ } else {
+ // if there is no clip on this layer (which should only happen for the
+ // root scrollable layer in a process, or for some of the LayerMetrics
+ // expansions of a multi-metrics layer), fall back to using the comp
+ // bounds which should be equivalent.
+ clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
+ }
+
+ return clipRegion;
+}
+
+void
+APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+ const AsyncPanZoomController* apzc)
+{
+ const FrameMetrics& metrics = aLayer.Metrics();
+ mApzcTreeLog << "APZC " << apzc->GetGuid()
+ << "\tcb=" << metrics.GetCompositionBounds()
+ << "\tsr=" << metrics.GetScrollableRect()
+ << (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
+ << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
+ << aLayer.Metadata().GetContentDescription().get();
+}
+
+void
+APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling)
+{
+ if (aNextSibling) {
+ aNextSibling->SetPrevSibling(aNode);
+ } else if (aParent) {
+ aParent->SetLastChild(aNode);
+ } else {
+ MOZ_ASSERT(!mRootNode);
+ mRootNode = aNode;
+ aNode->MakeRoot();
+ }
+}
+
+static EventRegions
+GetEventRegions(const LayerMetricsWrapper& aLayer)
+{
+ if (aLayer.IsScrollInfoLayer()) {
+ ParentLayerIntRect compositionBounds(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
+ nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
+ EventRegions eventRegions(hitRegion);
+ eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
+ return eventRegions;
+ }
+ return aLayer.GetEventRegions();
+}
+
+already_AddRefed<HitTestingTreeNode>
+APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
+ AsyncPanZoomController* aApzc,
+ uint64_t aLayersId)
+{
+ // Find a node without an APZC and return it. Note that unless the layer tree
+ // actually changes, this loop should generally do an early-return on the
+ // first iteration, so it should be cheap in the common case.
+ for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
+ RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
+ if (!node->IsPrimaryHolder()) {
+ aState.mNodesToDestroy.RemoveElement(node);
+ node->RecycleWith(aApzc, aLayersId);
+ return node.forget();
+ }
+ }
+ RefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false, aLayersId);
+ return node.forget();
+}
+
+static EventRegionsOverride
+GetEventRegionsOverride(HitTestingTreeNode* aParent,
+ const LayerMetricsWrapper& aLayer)
+{
+ // Make it so that if the flag is set on the layer tree, it automatically
+ // propagates to all the nodes in the corresponding subtree rooted at that
+ // layer in the hit-test tree. This saves having to walk up the tree every
+ // we want to see if a hit-test node is affected by this flag.
+ EventRegionsOverride result = aLayer.GetEventRegionsOverride();
+ if (aParent) {
+ result |= aParent->GetEventRegionsOverride();
+ }
+ return result;
+}
+
+void
+APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (!apzc) {
+ return;
+ }
+
+ uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
+ mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
+}
+
+HitTestingTreeNode*
+APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+ const FrameMetrics& aMetrics,
+ uint64_t aLayersId,
+ const gfx::Matrix4x4& aAncestorTransform,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling,
+ TreeBuildingState& aState)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ bool needsApzc = true;
+ if (!aMetrics.IsScrollable()) {
+ needsApzc = false;
+ }
+
+ const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!(state && state->mController.get())) {
+ needsApzc = false;
+ }
+
+ RefPtr<HitTestingTreeNode> node = nullptr;
+ if (!needsApzc) {
+ node = RecycleOrCreateNode(aState, nullptr, aLayersId);
+ AttachNodeToTree(node, aParent, aNextSibling);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
+ GetEventRegionsOverride(aParent, aLayer));
+ node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
+ aLayer.GetScrollbarDirection(),
+ aLayer.GetScrollbarSize(),
+ aLayer.IsScrollbarContainer());
+ node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
+ return node;
+ }
+
+ AsyncPanZoomController* apzc = nullptr;
+ // If we get here, aLayer is a scrollable layer and somebody
+ // has registered a GeckoContentController for it, so we need to ensure
+ // it has an APZC instance to manage its scrolling.
+
+ // aState.mApzcMap allows reusing the exact same APZC instance for different layers
+ // with the same FrameMetrics data. This is needed because in some cases content
+ // that is supposed to scroll together is split into multiple layers because of
+ // e.g. non-scrolling content interleaved in z-index order.
+ ScrollableLayerGuid guid(aLayersId, aMetrics);
+ auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
+ if (!insertResult.second) {
+ apzc = insertResult.first->second;
+ PrintAPZCInfo(aLayer, apzc);
+ }
+ APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
+
+ // If we haven't encountered a layer already with the same metrics, then we need to
+ // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
+ // below.
+ if (apzc == nullptr) {
+ apzc = aLayer.GetApzc();
+
+ // If the content represented by the scrollable layer has changed (which may
+ // be possible because of DLBI heuristics) then we don't want to keep using
+ // the same old APZC for the new content. Also, when reparenting a tab into a
+ // new window a layer might get moved to a different layer tree with a
+ // different APZCTreeManager. In these cases we don't want to reuse the same
+ // APZC, so null it out so we run through the code to find another one or
+ // create one.
+ if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
+ apzc = nullptr;
+ }
+
+ // See if we can find an APZC from the previous tree that matches the
+ // ScrollableLayerGuid from this layer. If there is one, then we know that
+ // the layout of the page changed causing the layer tree to be rebuilt, but
+ // the underlying content for the APZC is still there somewhere. Therefore,
+ // we want to find the APZC instance and continue using it here.
+ //
+ // We particularly want to find the primary-holder node from the previous
+ // tree that matches, because we don't want that node to get destroyed. If
+ // it does get destroyed, then the APZC will get destroyed along with it by
+ // definition, but we want to keep that APZC around in the new tree.
+ // We leave non-primary-holder nodes in the destroy list because we don't
+ // care about those nodes getting destroyed.
+ for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
+ RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
+ if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
+ node = n;
+ if (apzc != nullptr) {
+ // If there is an APZC already then it should match the one from the
+ // old primary-holder node
+ MOZ_ASSERT(apzc == node->GetApzc());
+ }
+ apzc = node->GetApzc();
+ break;
+ }
+ }
+
+ // The APZC we get off the layer may have been destroyed previously if the
+ // layer was inactive or omitted from the layer tree for whatever reason
+ // from a layers update. If it later comes back it will have a reference to
+ // a destroyed APZC and so we need to throw that out and make a new one.
+ bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
+ if (newApzc) {
+ MOZ_ASSERT(aState.mLayerTreeState);
+ apzc = NewAPZCInstance(aLayersId, state->mController);
+ apzc->SetCompositorController(aState.mLayerTreeState->GetCompositorController());
+ if (state->mCrossProcessParent) {
+ apzc->SetMetricsSharingController(state->CrossProcessSharingController());
+ } else {
+ apzc->SetMetricsSharingController(aState.mLayerTreeState->InProcessSharingController());
+ }
+ MOZ_ASSERT(node == nullptr);
+ node = new HitTestingTreeNode(apzc, true, aLayersId);
+ } else {
+ // If we are re-using a node for this layer clear the tree pointers
+ // so that it doesn't continue pointing to nodes that might no longer
+ // be in the tree. These pointers will get reset properly as we continue
+ // building the tree. Also remove it from the set of nodes that are going
+ // to be destroyed, because it's going to remain active.
+ aState.mNodesToDestroy.RemoveElement(node);
+ node->SetPrevSibling(nullptr);
+ node->SetLastChild(nullptr);
+ }
+
+ APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
+
+ apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
+ aLayersId == aState.mOriginatingLayersId);
+
+ // Since this is the first time we are encountering an APZC with this guid,
+ // the node holding it must be the primary holder. It may be newly-created
+ // or not, depending on whether it went through the newApzc branch above.
+ MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
+
+ ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ Some(clipRegion),
+ GetEventRegionsOverride(aParent, aLayer));
+ apzc->SetAncestorTransform(aAncestorTransform);
+
+ PrintAPZCInfo(aLayer, apzc);
+
+ // Bind the APZC instance into the tree of APZCs
+ AttachNodeToTree(node, aParent, aNextSibling);
+
+ // For testing, log the parent scroll id of every APZC that has a
+ // parent. This allows test code to reconstruct the APZC tree.
+ // Note that we currently only do this for APZCs in the layer tree
+ // that originated the update, because the only identifying information
+ // we are logging about APZCs is the scroll id, and otherwise we could
+ // confuse APZCs from different layer trees with the same scroll id.
+ if (aLayersId == aState.mOriginatingLayersId) {
+ if (apzc->HasNoParentWithSameLayersId()) {
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "hasNoParentWithSameLayersId", true);
+ } else {
+ MOZ_ASSERT(apzc->GetParent());
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "parentScrollId", apzc->GetParent()->GetGuid().mScrollId);
+ }
+ if (aMetrics.IsRootContent()) {
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
+ "isRootContent", true);
+ }
+ // Note that the async scroll offset is in ParentLayer pixels
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "asyncScrollOffset",
+ apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL));
+ }
+
+ if (newApzc) {
+ auto it = mZoomConstraints.find(guid);
+ if (it != mZoomConstraints.end()) {
+ // We have a zoomconstraints for this guid, apply it.
+ apzc->UpdateZoomConstraints(it->second);
+ } else if (!apzc->HasNoParentWithSameLayersId()) {
+ // This is a sub-APZC, so inherit the zoom constraints from its parent.
+ // This ensures that if e.g. user-scalable=no was specified, none of the
+ // APZCs for that subtree allow double-tap to zoom.
+ apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
+ }
+ // Otherwise, this is the root of a layers id, but we didn't have a saved
+ // zoom constraints. Leave it empty for now.
+ }
+
+ // Add a guid -> APZC mapping for the newly created APZC.
+ insertResult.first->second = apzc;
+ } else {
+ // We already built an APZC earlier in this tree walk, but we have another layer
+ // now that will also be using that APZC. The hit-test region on the APZC needs
+ // to be updated to deal with the new layer's hit region.
+
+ node = RecycleOrCreateNode(aState, apzc, aLayersId);
+ AttachNodeToTree(node, aParent, aNextSibling);
+
+ // Even though different layers associated with a given APZC may be at
+ // different levels in the layer tree (e.g. one being an uncle of another),
+ // we require from Layout that the CSS transforms up to their common
+ // ancestor be roughly the same. There are cases in which the transforms
+ // are not exactly the same, for example if the parent is container layer
+ // for an opacity, and this container layer has a resolution-induced scale
+ // as its base transform and a prescale that is supposed to undo that scale.
+ // Due to floating point inaccuracies those transforms can end up not quite
+ // canceling each other. That's why we're using a fuzzy comparison here
+ // instead of an exact one.
+ MOZ_ASSERT(aAncestorTransform.FuzzyEqualsMultiplicative(apzc->GetAncestorTransform()));
+
+ ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
+ node->SetHitTestData(
+ GetEventRegions(aLayer),
+ aLayer.GetTransformTyped(),
+ Some(clipRegion),
+ GetEventRegionsOverride(aParent, aLayer));
+ }
+
+ node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
+ aLayer.GetScrollbarDirection(),
+ aLayer.GetScrollbarSize(),
+ aLayer.IsScrollbarContainer());
+ node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
+ return node;
+}
+
+template<typename PanGestureOrScrollWheelInput>
+static bool
+WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
+{
+ if (!NS_IsMainThread()) {
+ return true;
+ }
+
+ WidgetWheelEvent wheelEvent = aPanInput.ToWidgetWheelEvent(nullptr);
+ return WillHandleWheelEvent(&wheelEvent);
+}
+
+void
+APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
+{
+ // Previously, paints were throttled and therefore this method was used to
+ // ensure any pending paints were flushed. Now, paints are flushed
+ // immediately, so it is safe to simply send a notification now.
+ APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ MOZ_ASSERT(state && state->mController);
+ state->mController->DispatchToRepaintThread(NewRunnableMethod(
+ state->mController, &GeckoContentController::NotifyFlushComplete));
+}
+
+nsEventStatus
+APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // Initialize aOutInputBlockId to a sane value, and then later we overwrite
+ // it if the input event goes into a block.
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
+ }
+ nsEventStatus result = nsEventStatus_eIgnore;
+ HitTestResult hitResult = HitNothing;
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
+ result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
+ break;
+ } case MOUSE_INPUT: {
+ MouseInput& mouseInput = aEvent.AsMouseInput();
+ mouseInput.mHandledByAPZ = true;
+
+ if (DragTracker::StartsDrag(mouseInput)) {
+ // If this is the start of a drag we need to unambiguously know if it's
+ // going to land on a scrollbar or not. We can't apply an untransform
+ // here without knowing that, so we need to ensure the untransform is
+ // a no-op.
+ FlushRepaintsToClearScreenToGeckoTransform();
+ }
+
+ bool hitScrollbar = false;
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
+ &hitResult, &hitScrollbar);
+
+ // When the mouse is outside the window we still want to handle dragging
+ // but we won't find an APZC. Fallback to root APZC then.
+ { // scope lock
+ MutexAutoLock lock(mTreeLock);
+ if (!apzc && mRootNode) {
+ apzc = mRootNode->GetApzc();
+ }
+ }
+
+ if (apzc) {
+ bool targetConfirmed = (hitResult != HitNothing && hitResult != HitDispatchToContentRegion);
+ if (gfxPrefs::APZDragEnabled() && hitScrollbar) {
+ // If scrollbar dragging is enabled and we hit a scrollbar, wait
+ // for the main-thread confirmation because it contains drag metrics
+ // that we need.
+ targetConfirmed = false;
+ }
+ result = mInputQueue->ReceiveInputEvent(
+ apzc, targetConfirmed,
+ mouseInput, aOutInputBlockId);
+
+ if (result == nsEventStatus_eConsumeDoDefault) {
+ // This input event is part of a drag block, so whether or not it is
+ // directed at a scrollbar depends on whether the drag block started
+ // on a scrollbar.
+ hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
+ }
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+
+ if (!hitScrollbar) {
+ // The input was not targeted at a scrollbar, so we untransform it
+ // like we do for other content. Scrollbars are "special" because they
+ // have special handling in AsyncCompositionManager when resolution is
+ // applied. TODO: we should find a better way to deal with this.
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+ Maybe<ScreenPoint> untransformedRefPoint = UntransformBy(
+ outTransform, mouseInput.mOrigin);
+ if (untransformedRefPoint) {
+ mouseInput.mOrigin = *untransformedRefPoint;
+ }
+ } else {
+ // Likewise, if the input was targeted at a scrollbar, we don't want to
+ // apply the callback transform in the main thread, so we remove the
+ // scrollid from the guid. We need to keep the layersId intact so
+ // that the response from the child process doesn't get discarded.
+ aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
+ }
+ }
+ break;
+ } case SCROLLWHEEL_INPUT: {
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
+
+ wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
+ if (!wheelInput.mHandledByAPZ) {
+ return result;
+ }
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ // For wheel events, the call to ReceiveInputEvent below may result in
+ // scrolling, which changes the async transform. However, the event we
+ // want to pass to gecko should be the pre-scroll event coordinates,
+ // transformed into the gecko space. (pre-scroll because the mouse
+ // cursor is stationary during wheel scrolling, unlike touchmove
+ // events). Since we just flushed the pending repaints the transform to
+ // gecko space should only consist of overscroll-cancelling transforms.
+ ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedOrigin = UntransformBy(
+ transformToGecko, wheelInput.mOrigin);
+
+ if (!untransformedOrigin) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ wheelInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ wheelInput.mOrigin = *untransformedOrigin;
+ }
+ break;
+ } case PANGESTURE_INPUT: {
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ PanGestureInput& panInput = aEvent.AsPanGestureInput();
+ panInput.mHandledByAPZ = WillHandleInput(panInput);
+ if (!panInput.mHandledByAPZ) {
+ return result;
+ }
+
+ // If/when we enable support for pan inputs off-main-thread, we'll need
+ // to duplicate this EventStateManager code or something. See the other
+ // call to GetUserPrefsForWheelEvent in this file for why these fields
+ // are stored separately.
+ MOZ_ASSERT(NS_IsMainThread());
+ WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
+ EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
+ &panInput.mUserDeltaMultiplierX,
+ &panInput.mUserDeltaMultiplierY);
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ // For pan gesture events, the call to ReceiveInputEvent below may result in
+ // scrolling, which changes the async transform. However, the event we
+ // want to pass to gecko should be the pre-scroll event coordinates,
+ // transformed into the gecko space. (pre-scroll because the mouse
+ // cursor is stationary during pan gesture scrolling, unlike touchmove
+ // events). Since we just flushed the pending repaints the transform to
+ // gecko space should only consist of overscroll-cancelling transforms.
+ ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedStartPoint = UntransformBy(
+ transformToGecko, panInput.mPanStartPoint);
+ Maybe<ScreenPoint> untransformedDisplacement = UntransformVector(
+ transformToGecko, panInput.mPanDisplacement, panInput.mPanStartPoint);
+
+ if (!untransformedStartPoint || !untransformedDisplacement) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ panInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ panInput.mPanStartPoint = *untransformedStartPoint;
+ panInput.mPanDisplacement = *untransformedDisplacement;
+ }
+ break;
+ } case PINCHGESTURE_INPUT: { // note: no one currently sends these
+ PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenPoint> untransformedFocusPoint = UntransformBy(
+ outTransform, pinchInput.mFocusPoint);
+
+ if (!untransformedFocusPoint) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ pinchInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ pinchInput.mFocusPoint = *untransformedFocusPoint;
+ }
+ break;
+ } case TAPGESTURE_INPUT: { // note: no one currently sends these
+ TapGestureInput& tapInput = aEvent.AsTapGestureInput();
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(tapInput.mPoint,
+ &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+
+ ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
+ * GetApzcToGeckoTransform(apzc);
+ Maybe<ScreenIntPoint> untransformedPoint =
+ UntransformBy(outTransform, tapInput.mPoint);
+
+ if (!untransformedPoint) {
+ return result;
+ }
+
+ result = mInputQueue->ReceiveInputEvent(
+ apzc,
+ /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
+ tapInput, aOutInputBlockId);
+
+ // Update the out-parameters so they are what the caller expects.
+ apzc->GetGuid(aOutTargetGuid);
+ tapInput.mPoint = *untransformedPoint;
+ }
+ break;
+ } case SENTINEL_INPUT: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputType.");
+ break;
+ }
+ }
+ return result;
+}
+
+static TouchBehaviorFlags
+ConvertToTouchBehavior(HitTestResult result)
+{
+ switch (result) {
+ case HitNothing:
+ return AllowedTouchBehavior::NONE;
+ case HitLayer:
+ return AllowedTouchBehavior::VERTICAL_PAN
+ | AllowedTouchBehavior::HORIZONTAL_PAN
+ | AllowedTouchBehavior::PINCH_ZOOM
+ | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+ case HitLayerTouchActionNone:
+ return AllowedTouchBehavior::NONE;
+ case HitLayerTouchActionPanX:
+ return AllowedTouchBehavior::HORIZONTAL_PAN;
+ case HitLayerTouchActionPanY:
+ return AllowedTouchBehavior::VERTICAL_PAN;
+ case HitLayerTouchActionPanXY:
+ return AllowedTouchBehavior::HORIZONTAL_PAN
+ | AllowedTouchBehavior::VERTICAL_PAN;
+ case HitDispatchToContentRegion:
+ default:
+ return AllowedTouchBehavior::UNKNOWN;
+ }
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
+ nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
+ HitTestResult* aOutHitResult)
+{
+ RefPtr<AsyncPanZoomController> apzc;
+ if (aEvent.mTouches.Length() == 0) {
+ return apzc.forget();
+ }
+
+ FlushRepaintsToClearScreenToGeckoTransform();
+
+ HitTestResult hitResult;
+ apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, &hitResult);
+ if (aOutTouchBehaviors) {
+ aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
+ }
+ for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
+ RefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, &hitResult);
+ if (aOutTouchBehaviors) {
+ aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
+ }
+ apzc = GetMultitouchTarget(apzc, apzc2);
+ APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
+ }
+
+ if (aOutHitResult) {
+ // XXX we should probably be combining the hit results from the different
+ // touch points somehow, instead of just using the last one.
+ *aOutHitResult = hitResult;
+ }
+ return apzc.forget();
+}
+
+nsEventStatus
+APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ aInput.mHandledByAPZ = true;
+ nsTArray<TouchBehaviorFlags> touchBehaviors;
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
+ // If we are panned into overscroll and a second finger goes down,
+ // ignore that second touch point completely. The touch-start for it is
+ // dropped completely; subsequent touch events until the touch-end for it
+ // will have this touch point filtered out.
+ // (By contrast, if we're in overscroll but not panning, such as after
+ // putting two fingers down during an overscroll animation, we process the
+ // second touch and proceed to pinch.)
+ if (mApzcForInputBlock &&
+ mApzcForInputBlock->IsInPanningState() &&
+ BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
+ if (mRetainedTouchIdentifier == -1) {
+ mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ mHitResultForInputBlock = HitNothing;
+ mApzcForInputBlock = GetTouchInputBlockAPZC(aInput, &touchBehaviors, &mHitResultForInputBlock);
+ MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
+ for (size_t i = 0; i < touchBehaviors.Length(); i++) {
+ APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", touchBehaviors[i]);
+ if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
+ // If there's any unknown items in the list, throw it out and we'll
+ // wait for the main thread to send us a notification.
+ touchBehaviors.Clear();
+ break;
+ }
+ }
+ } else if (mApzcForInputBlock) {
+ APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
+ }
+
+ // If we receive a touch-cancel, it means all touches are finished, so we
+ // can stop ignoring any that we were ignoring.
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
+ mRetainedTouchIdentifier = -1;
+ }
+
+ // If we are currently ignoring any touch points, filter them out from the
+ // set of touch points included in this event. Note that we modify aInput
+ // itself, so that the touch points are also filtered out when the caller
+ // passes the event on to content.
+ if (mRetainedTouchIdentifier != -1) {
+ for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
+ if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
+ aInput.mTouches.RemoveElementAt(j);
+ if (!touchBehaviors.IsEmpty()) {
+ MOZ_ASSERT(touchBehaviors.Length() > j);
+ touchBehaviors.RemoveElementAt(j);
+ }
+ --j;
+ }
+ }
+ if (aInput.mTouches.IsEmpty()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+
+ nsEventStatus result = nsEventStatus_eIgnore;
+ if (mApzcForInputBlock) {
+ MOZ_ASSERT(mHitResultForInputBlock != HitNothing);
+
+ mApzcForInputBlock->GetGuid(aOutTargetGuid);
+ uint64_t inputBlockId = 0;
+ result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
+ /* aTargetConfirmed = */ mHitResultForInputBlock != HitDispatchToContentRegion,
+ aInput, &inputBlockId);
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = inputBlockId;
+ }
+ if (!touchBehaviors.IsEmpty()) {
+ mInputQueue->SetAllowedTouchBehavior(inputBlockId, touchBehaviors);
+ }
+
+ // For computing the event to pass back to Gecko, use up-to-date transforms
+ // (i.e. not anything cached in an input block).
+ // This ensures that transformToApzc and transformToGecko are in sync.
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+
+ for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
+ SingleTouchData& touchData = aInput.mTouches[i];
+ Maybe<ScreenIntPoint> untransformedScreenPoint = UntransformBy(
+ outTransform, touchData.mScreenPoint);
+ if (!untransformedScreenPoint) {
+ return nsEventStatus_eIgnore;
+ }
+ touchData.mScreenPoint = *untransformedScreenPoint;
+ }
+ }
+
+ mTouchCounter.Update(aInput);
+
+ // If it's the end of the touch sequence then clear out variables so we
+ // don't keep dangling references and leak things.
+ if (mTouchCounter.GetActiveTouchCount() == 0) {
+ mApzcForInputBlock = nullptr;
+ mHitResultForInputBlock = HitNothing;
+ mRetainedTouchIdentifier = -1;
+ }
+
+ return result;
+}
+
+void
+APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage)
+{
+ WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
+ if (!txn) {
+ return;
+ }
+
+ // If the transaction has simply timed out, we don't need to do anything
+ // else.
+ if (txn->MaybeTimeout(TimeStamp::Now())) {
+ return;
+ }
+
+ switch (aEventMessage) {
+ case eMouseMove:
+ case eDragOver: {
+
+ ScreenIntPoint point =
+ ViewAs<ScreenPixel>(aRefPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+
+ txn->OnMouseMove(point);
+
+ return;
+ }
+ case eKeyPress:
+ case eKeyUp:
+ case eKeyDown:
+ case eMouseUp:
+ case eMouseDown:
+ case eMouseDoubleClick:
+ case eMouseClick:
+ case eContextMenu:
+ case eDrop:
+ txn->EndTransaction();
+ return;
+ default:
+ break;
+ }
+}
+
+void
+APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ // Transform the aRefPoint.
+ // If the event hits an overscrolled APZC, instruct the caller to ignore it.
+ HitTestResult hitResult = HitNothing;
+ PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
+ ScreenIntPoint refPointAsScreen =
+ ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
+ if (apzc) {
+ MOZ_ASSERT(hitResult != HitNothing);
+ apzc->GetGuid(aOutTargetGuid);
+ ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
+ ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
+ ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
+ Maybe<ScreenIntPoint> untransformedRefPoint =
+ UntransformBy(outTransform, refPointAsScreen);
+ if (untransformedRefPoint) {
+ *aRefPoint =
+ ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
+ }
+ }
+}
+
+void
+APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+ if (mApzcForInputBlock) {
+ mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
+ }
+}
+
+void
+APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags)
+{
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (apzc) {
+ apzc->ZoomToRect(aRect, aFlags);
+ }
+}
+
+void
+APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void
+APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ RefPtr<AsyncPanZoomController> target = nullptr;
+ if (aTargets.Length() > 0) {
+ target = GetTargetAPZC(aTargets[0]);
+ }
+ for (size_t i = 1; i < aTargets.Length(); i++) {
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
+ target = GetMultitouchTarget(target, apzc);
+ }
+ mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
+}
+
+void
+APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
+ mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
+}
+
+void
+APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+
+ // Propagate the zoom constraints down to the subtree, stopping at APZCs
+ // which have their own zoom constraints or are in a different layers id.
+ if (aConstraints) {
+ APZCTM_LOG("Recording constraints %s for guid %s\n",
+ Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
+ mZoomConstraints[aGuid] = aConstraints.ref();
+ } else {
+ APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
+ mZoomConstraints.erase(aGuid);
+ }
+ if (node && aConstraints) {
+ ForEachNode<ReverseIterator>(node.get(),
+ [&aConstraints, &node, this](HitTestingTreeNode* aNode)
+ {
+ if (aNode != node) {
+ if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
+ // We can have subtrees with their own zoom constraints or separate layers
+ // id - leave these alone.
+ if (childApzc->HasNoParentWithSameLayersId() ||
+ this->mZoomConstraints.find(childApzc->GetGuid()) != this->mZoomConstraints.end()) {
+ return TraversalFlag::Skip;
+ }
+ }
+ }
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref());
+ }
+ return TraversalFlag::Continue;
+ });
+ }
+}
+
+void
+APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform()
+{
+ // As the name implies, we flush repaint requests for the entire APZ tree in
+ // order to clear the screen-to-gecko transform (aka the "untransform" applied
+ // to incoming input events before they can be passed on to Gecko).
+ //
+ // The primary reason we do this is to avoid the problem where input events,
+ // after being untransformed, end up hit-testing differently in Gecko. This
+ // might happen in cases where the input event lands on content that is async-
+ // scrolled into view, but Gecko still thinks it is out of view given the
+ // visible area of a scrollframe.
+ //
+ // Another reason we want to clear the untransform is that if our APZ hit-test
+ // hits a dispatch-to-content region then that's an ambiguous result and we
+ // need to ask Gecko what actually got hit. In order to do this we need to
+ // untransform the input event into Gecko space - but to do that we need to
+ // know which APZC got hit! This leads to a circular dependency; the only way
+ // to get out of it is to make sure that the untransform for all the possible
+ // matched APZCs is the same. It is simplest to ensure that by flushing the
+ // pending repaint requests, which makes all of the untransforms empty (and
+ // therefore equal).
+ MutexAutoLock lock(mTreeLock);
+ mTreeLock.AssertCurrentThreadOwns();
+
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode)
+ {
+ if (aNode->IsPrimaryHolder()) {
+ MOZ_ASSERT(aNode->GetApzc());
+ aNode->GetApzc()->FlushRepaintForNewInputBlock();
+ }
+ });
+}
+
+void
+APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
+{
+ RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+ if (apzc) {
+ apzc->CancelAnimation();
+ }
+}
+
+void
+APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
+ if (apzc) {
+ apzc->AdjustScrollForSurfaceShift(aShift);
+ }
+}
+
+void
+APZCTreeManager::ClearTree()
+{
+ // Ensure that no references to APZCs are alive in any lingering input
+ // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
+ // the InputQueue.
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod(mInputQueue, &InputQueue::Clear));
+
+ MutexAutoLock lock(mTreeLock);
+
+ // Collect the nodes into a list, and then destroy each one.
+ // We can't destroy them as we collect them, because ForEachNode()
+ // does a pre-order traversal of the tree, and Destroy() nulls out
+ // the fields needed to reach the children of the node.
+ nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
+ ForEachNode<ReverseIterator>(mRootNode.get(),
+ [&nodesToDestroy](HitTestingTreeNode* aNode)
+ {
+ nodesToDestroy.AppendElement(aNode);
+ });
+
+ for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
+ nodesToDestroy[i]->Destroy();
+ }
+ mRootNode = nullptr;
+
+ RefPtr<APZCTreeManager> self(this);
+ NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
+ self->mFlushObserver->Unregister();
+ self->mFlushObserver = nullptr;
+ }));
+}
+
+RefPtr<HitTestingTreeNode>
+APZCTreeManager::GetRootNode() const
+{
+ MutexAutoLock lock(mTreeLock);
+ return mRootNode;
+}
+
+/**
+ * Transform a displacement from the ParentLayer coordinates of a source APZC
+ * to the ParentLayer coordinates of a target APZC.
+ * @param aTreeManager the tree manager for the APZC tree containing |aSource|
+ * and |aTarget|
+ * @param aSource the source APZC
+ * @param aTarget the target APZC
+ * @param aStartPoint the start point of the displacement
+ * @param aEndPoint the end point of the displacement
+ * @return true on success, false if aStartPoint or aEndPoint cannot be transformed into target's coordinate space
+ */
+static bool
+TransformDisplacement(APZCTreeManager* aTreeManager,
+ AsyncPanZoomController* aSource,
+ AsyncPanZoomController* aTarget,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint) {
+ if (aSource == aTarget) {
+ return true;
+ }
+
+ // Convert start and end points to Screen coordinates.
+ ParentLayerToScreenMatrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
+ ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint);
+ ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint);
+
+ // Convert start and end points to aTarget's ParentLayer coordinates.
+ ScreenToParentLayerMatrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
+ Maybe<ParentLayerPoint> startPoint = UntransformBy(transformToApzc, screenStart);
+ Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd);
+ if (!startPoint || !endPoint) {
+ return false;
+ }
+ aEndPoint = *endPoint;
+ aStartPoint = *startPoint;
+
+ return true;
+}
+
+void
+APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState)
+{
+ const OverscrollHandoffChain& overscrollHandoffChain = aOverscrollHandoffState.mChain;
+ uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
+ RefPtr<AsyncPanZoomController> next;
+ // If we have reached the end of the overscroll handoff chain, there is
+ // nothing more to scroll, so we ignore the rest of the pan gesture.
+ if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
+ // Nothing more to scroll - ignore the rest of the pan gesture.
+ return;
+ }
+
+ next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
+
+ if (next == nullptr || next->IsDestroyed()) {
+ return;
+ }
+
+ // Convert the start and end points from |aPrev|'s coordinate space to
+ // |next|'s coordinate space.
+ if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
+ return;
+ }
+
+ // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
+ // again with an incremented index.
+ if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) {
+ // Transform |aStartPoint| and |aEndPoint| (which now represent the
+ // portion of the displacement that wasn't consumed by APZCs later
+ // in the handoff chain) back into |aPrev|'s coordinate space. This
+ // allows the caller (which is |aPrev|) to interpret the unconsumed
+ // displacement in its own coordinate space, and make use of it
+ // (e.g. by going into overscroll).
+ if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) {
+ NS_WARNING("Failed to untransform scroll points during dispatch");
+ }
+ }
+}
+
+void
+APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev,
+ FlingHandoffState& aHandoffState)
+{
+ // If immediate handoff is disallowed, do not allow handoff beyond the
+ // single APZC that's scrolled by the input block that triggered this fling.
+ if (aHandoffState.mIsHandoff &&
+ !gfxPrefs::APZAllowImmediateHandoff() &&
+ aHandoffState.mScrolledApzc == aPrev) {
+ return;
+ }
+
+ const OverscrollHandoffChain* chain = aHandoffState.mChain;
+ RefPtr<AsyncPanZoomController> current;
+ uint32_t overscrollHandoffChainLength = chain->Length();
+ uint32_t startIndex;
+
+ // This will store any velocity left over after the entire handoff.
+ ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity;
+
+ // The fling's velocity needs to be transformed from the screen coordinates
+ // of |aPrev| to the screen coordinates of |next|. To transform a velocity
+ // correctly, we need to convert it to a displacement. For now, we do this
+ // by anchoring it to a start point of (0, 0).
+ // TODO: For this to be correct in the presence of 3D transforms, we should
+ // use the end point of the touch that started the fling as the start point
+ // rather than (0, 0).
+ ParentLayerPoint startPoint; // (0, 0)
+ ParentLayerPoint endPoint;
+
+ if (aHandoffState.mIsHandoff) {
+ startIndex = chain->IndexOf(aPrev) + 1;
+
+ // IndexOf will return aOverscrollHandoffChain->Length() if
+ // |aPrev| is not found.
+ if (startIndex >= overscrollHandoffChainLength) {
+ return;
+ }
+ } else {
+ startIndex = 0;
+ }
+
+ for (; startIndex < overscrollHandoffChainLength; startIndex++) {
+ current = chain->GetApzcAtIndex(startIndex);
+
+ // Make sure the apcz about to be handled can be handled
+ if (current == nullptr || current->IsDestroyed()) {
+ return;
+ }
+
+ endPoint = startPoint + aHandoffState.mVelocity;
+
+ // Only transform when current apcz can be transformed with previous
+ if (startIndex > 0) {
+ if (!TransformDisplacement(this,
+ chain->GetApzcAtIndex(startIndex - 1),
+ current,
+ startPoint,
+ endPoint)) {
+ return;
+ }
+ }
+
+ ParentLayerPoint transformedVelocity = endPoint - startPoint;
+ aHandoffState.mVelocity = transformedVelocity;
+
+ if (current->AttemptFling(aHandoffState)) {
+ // Coming out of AttemptFling(), the handoff state's velocity is the
+ // residual velocity after attempting to fling |current|.
+ ParentLayerPoint residualVelocity = aHandoffState.mVelocity;
+
+ // If there's no residual velocity, there's nothing more to hand off.
+ if (IsZero(residualVelocity)) {
+ finalResidualVelocity = ParentLayerPoint();
+ break;
+ }
+
+ // If there is residual velocity, subtract the proportion of used
+ // velocity from finalResidualVelocity and continue handoff along the
+ // chain.
+ if (!FuzzyEqualsAdditive(transformedVelocity.x,
+ residualVelocity.x, COORDINATE_EPSILON)) {
+ finalResidualVelocity.x *= (residualVelocity.x / transformedVelocity.x);
+ }
+ if (!FuzzyEqualsAdditive(transformedVelocity.y,
+ residualVelocity.y, COORDINATE_EPSILON)) {
+ finalResidualVelocity.y *= (residualVelocity.y / transformedVelocity.y);
+ }
+ }
+ }
+
+ // Set the handoff state's velocity to any residual velocity left over
+ // after the entire handoff process.
+ aHandoffState.mVelocity = finalResidualVelocity;
+}
+
+bool
+APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
+{
+ RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
+ return target != nullptr;
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+ RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
+ return apzc.forget();
+}
+
+already_AddRefed<HitTestingTreeNode>
+APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+ RefPtr<HitTestingTreeNode> target = DepthFirstSearchPostOrder<ReverseIterator>(mRootNode.get(),
+ [&aGuid, &aComparator](HitTestingTreeNode* node)
+ {
+ bool matches = false;
+ if (node->GetApzc()) {
+ if (aComparator) {
+ matches = aComparator(aGuid, node->GetApzc()->GetGuid());
+ } else {
+ matches = node->GetApzc()->Matches(aGuid);
+ }
+ }
+ return matches;
+ }
+ );
+ return target.forget();
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar)
+{
+ MutexAutoLock lock(mTreeLock);
+ HitTestResult hitResult = HitNothing;
+ ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
+ PixelCastJustification::ScreenIsParentLayerForRoot);
+ RefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point,
+ &hitResult, aOutHitScrollbar);
+
+ if (aOutHitResult) {
+ *aOutHitResult = hitResult;
+ }
+ return target.forget();
+}
+
+static bool
+GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne, const ScrollableLayerGuid& aTwo)
+{
+ return aOne.mLayersId == aTwo.mLayersId
+ && aOne.mScrollId == aTwo.mScrollId;
+}
+
+RefPtr<const OverscrollHandoffChain>
+APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget)
+{
+ // Scroll grabbing is a mechanism that allows content to specify that
+ // the initial target of a pan should be not the innermost scrollable
+ // frame at the touch point (which is what GetTargetAPZC finds), but
+ // something higher up in the tree.
+ // It's not sufficient to just find the initial target, however, as
+ // overscroll can be handed off to another APZC. Without scroll grabbing,
+ // handoff just occurs from child to parent. With scroll grabbing, the
+ // handoff order can be different, so we build a chain of APZCs in the
+ // order in which scroll will be handed off to them.
+
+ // Grab tree lock since we'll be walking the APZC tree.
+ MutexAutoLock lock(mTreeLock);
+
+ // Build the chain. If there is a scroll parent link, we use that. This is
+ // needed to deal with scroll info layers, because they participate in handoff
+ // but do not follow the expected layer tree structure. If there are no
+ // scroll parent links we just walk up the tree to find the scroll parent.
+ OverscrollHandoffChain* result = new OverscrollHandoffChain;
+ AsyncPanZoomController* apzc = aInitialTarget;
+ while (apzc != nullptr) {
+ result->Add(apzc);
+
+ if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
+ if (!apzc->IsRootForLayersId()) {
+ // This probably indicates a bug or missed case in layout code
+ NS_WARNING("Found a non-root APZ with no handoff parent");
+ }
+ apzc = apzc->GetParent();
+ continue;
+ }
+
+ // Guard against a possible infinite-loop condition. If we hit this, the
+ // layout code that generates the handoff parents did something wrong.
+ MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
+
+ // Find the AsyncPanZoomController instance with a matching layersId and
+ // the scroll id that matches apzc->GetScrollHandoffParentId().
+ // As an optimization, we start by walking up the APZC tree from 'apzc'
+ // until we reach the top of the layer subtree for this layers id.
+ AsyncPanZoomController* scrollParent = nullptr;
+ AsyncPanZoomController* parent = apzc;
+ while (!parent->HasNoParentWithSameLayersId()) {
+ parent = parent->GetParent();
+ // While walking up to find the root of the subtree, if we encounter the
+ // handoff parent, we don't actually need to do the search so we can
+ // just abort here.
+ if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
+ scrollParent = parent;
+ break;
+ }
+ }
+ // If that heuristic didn't turn up the scroll parent, do a full tree search.
+ if (!scrollParent) {
+ ScrollableLayerGuid guid(parent->GetGuid().mLayersId, 0, apzc->GetScrollHandoffParentId());
+ RefPtr<HitTestingTreeNode> node = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
+ MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
+ scrollParent = node ? node->GetApzc() : nullptr;
+ }
+ apzc = scrollParent;
+ }
+
+ // Now adjust the chain to account for scroll grabbing. Sorting is a bit
+ // of an overkill here, but scroll grabbing will likely be generalized
+ // to scroll priorities, so we might as well do it this way.
+ result->SortByScrollPriority();
+
+ // Print the overscroll chain for debugging.
+ for (uint32_t i = 0; i < result->Length(); ++i) {
+ APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
+ }
+
+ return result;
+}
+
+void
+APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
+{
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
+}
+
+RefPtr<HitTestingTreeNode>
+APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics)
+{
+ MutexAutoLock lock(mTreeLock);
+
+ return DepthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [&aDragMetrics](HitTestingTreeNode* aNode) {
+ return aNode->MatchesScrollDragMetrics(aDragMetrics);
+ });
+}
+
+AsyncPanZoomController*
+APZCTreeManager::GetTargetApzcForNode(HitTestingTreeNode* aNode)
+{
+ for (const HitTestingTreeNode* n = aNode;
+ n && n->GetLayersId() == aNode->GetLayersId();
+ n = n->GetParent()) {
+ if (n->GetApzc()) {
+ APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
+ return n->GetApzc();
+ }
+ if (n->GetFixedPosTarget() != FrameMetrics::NULL_SCROLL_ID) {
+ ScrollableLayerGuid guid(n->GetLayersId(), 0, n->GetFixedPosTarget());
+ RefPtr<HitTestingTreeNode> fpNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
+ APZCTM_LOG("Found target node %p using fixed-pos lookup on %" PRIu64 "\n", fpNode.get(), n->GetFixedPosTarget());
+ return fpNode ? fpNode->GetApzc() : nullptr;
+ }
+ }
+ return nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
+ const ParentLayerPoint& aHitTestPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar)
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ // This walks the tree in depth-first, reverse order, so that it encounters
+ // APZCs front-to-back on the screen.
+ HitTestingTreeNode* resultNode;
+ HitTestingTreeNode* root = aNode;
+ std::stack<ParentLayerPoint> hitTestPoints;
+ hitTestPoints.push(aHitTestPoint);
+
+ ForEachNode<ReverseIterator>(root,
+ [&hitTestPoints](HitTestingTreeNode* aNode) {
+ if (aNode->IsOutsideClip(hitTestPoints.top())) {
+ // If the point being tested is outside the clip region for this node
+ // then we don't need to test against this node or any of its children.
+ // Just skip it and move on.
+ APZCTM_LOG("Point %f %f outside clip for node %p\n",
+ hitTestPoints.top().x, hitTestPoints.top().y, aNode);
+ return TraversalFlag::Skip;
+ }
+ // First check the subtree rooted at this node, because deeper nodes
+ // are more "in front".
+ Maybe<LayerPoint> hitTestPointForChildLayers = aNode->Untransform(hitTestPoints.top());
+ APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
+ Stringify(hitTestPoints.top()).c_str(),
+ hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil");
+ if (!hitTestPointForChildLayers) {
+ return TraversalFlag::Skip;
+ }
+ hitTestPoints.push(ViewAs<ParentLayerPixel>(hitTestPointForChildLayers.ref(),
+ PixelCastJustification::MovingDownToChildren));
+ return TraversalFlag::Continue;
+ },
+ [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) {
+ hitTestPoints.pop();
+ HitTestResult hitResult = aNode->HitTest(hitTestPoints.top());
+ APZCTM_LOG("Testing ParentLayer point %s against node %p\n",
+ Stringify(hitTestPoints.top()).c_str(), aNode);
+ if (hitResult != HitTestResult::HitNothing) {
+ resultNode = aNode;
+ // If event regions are disabled, *aOutHitResult will be HitLayer
+ *aOutHitResult = hitResult;
+ return TraversalFlag::Abort;
+ }
+ return TraversalFlag::Continue;
+ }
+ );
+
+ if (*aOutHitResult != HitNothing) {
+ MOZ_ASSERT(resultNode);
+ if (aOutHitScrollbar) {
+ for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
+ if (n->IsScrollbarNode()) {
+ *aOutHitScrollbar = true;
+ }
+ }
+ }
+
+ AsyncPanZoomController* result = GetTargetApzcForNode(resultNode);
+ if (!result) {
+ result = FindRootApzcForLayersId(resultNode->GetLayersId());
+ MOZ_ASSERT(result);
+ APZCTM_LOG("Found target %p using root lookup\n", result);
+ }
+ APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
+ result, resultNode, *aOutHitResult);
+ return result;
+ }
+
+ return nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [aLayersId](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc
+ && apzc->GetLayersId() == aLayersId
+ && apzc->IsRootForLayersId();
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootContentApzcForLayersId(uint64_t aLayersId) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [aLayersId](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc
+ && apzc->GetLayersId() == aLayersId
+ && apzc->IsRootContent();
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindRootContentOrRootApzc() const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+
+ // Note: this is intended to find the same "root" that would be found
+ // by AsyncCompositionManager::ApplyAsyncContentTransformToTree inside
+ // the MOZ_WIDGET_ANDROID block. That is, it should find the RCD node if there
+ // is one, or the root APZC if there is not.
+ // Since BreadthFirstSearch is a pre-order search, we first do a search for
+ // the RCD, and then if we don't find one, we do a search for the root APZC.
+ HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return apzc && apzc->IsRootContent();
+ });
+ if (resultNode) {
+ return resultNode->GetApzc();
+ }
+ resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
+ [](HitTestingTreeNode* aNode) {
+ AsyncPanZoomController* apzc = aNode->GetApzc();
+ return (apzc != nullptr);
+ });
+ return resultNode ? resultNode->GetApzc() : nullptr;
+}
+
+/* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
+ some useful transformations that input events may need applied. This is best
+ illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
+ is the layer that corresponds to the argument |aApzc|, and layer R is the root
+ of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
+ When layer L is displayed to the screen by the compositor, the set of transforms that
+ are applied to L are (in order from top to bottom):
+
+ L's CSS transform (hereafter referred to as transform matrix LC)
+ L's nontransient async transform (hereafter referred to as transform matrix LN)
+ L's transient async transform (hereafter referred to as transform matrix LT)
+ M's CSS transform (hereafter referred to as transform matrix MC)
+ M's nontransient async transform (hereafter referred to as transform matrix MN)
+ M's transient async transform (hereafter referred to as transform matrix MT)
+ ...
+ R's CSS transform (hereafter referred to as transform matrix RC)
+ R's nontransient async transform (hereafter referred to as transform matrix RN)
+ R's transient async transform (hereafter referred to as transform matrix RT)
+
+ Also, for any layer, the async transform is the combination of its transient and non-transient
+ parts. That is, for any layer L:
+ LA === LN * LT
+ LA.Inverse() === LT.Inverse() * LN.Inverse()
+
+ If we want user input to modify L's transient async transform, we have to first convert
+ user input from screen space to the coordinate space of L's transient async transform. Doing
+ this involves applying the following transforms (in order from top to bottom):
+ RT.Inverse()
+ RN.Inverse()
+ RC.Inverse()
+ ...
+ MT.Inverse()
+ MN.Inverse()
+ MC.Inverse()
+ This combined transformation is returned by GetScreenToApzcTransform().
+
+ Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
+ out all of the async transforms that are involved in this chain. This is because async
+ transforms are stored only in the compositor and gecko does not account for them when
+ doing display-list-based hit-testing for event dispatching.
+ Furthermore, because these input events are processed by Gecko in a FIFO queue that
+ includes other things (specifically paint requests), it is possible that by time the
+ input event reaches gecko, it will have painted something else. Therefore, we need to
+ apply another transform to the input events to account for the possible disparity between
+ what we know gecko last painted and the last paint request we sent to gecko. Let this
+ transform be represented by LD, MD, ... RD.
+ Therefore, given a user input in screen space, the following transforms need to be applied
+ (in order from top to bottom):
+ RT.Inverse()
+ RN.Inverse()
+ RC.Inverse()
+ ...
+ MT.Inverse()
+ MN.Inverse()
+ MC.Inverse()
+ LT.Inverse()
+ LN.Inverse()
+ LC.Inverse()
+ LC
+ LD
+ MC
+ MD
+ ...
+ RC
+ RD
+ This sequence can be simplified and refactored to the following:
+ GetScreenToApzcTransform()
+ LA.Inverse()
+ LD
+ MC
+ MD
+ ...
+ RC
+ RD
+ Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform()
+ returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can
+ combine it with GetScreenToApzcTransform() to get the final transform required in this case.
+
+ Note that for many of these layers, there will be no AsyncPanZoomController attached, and
+ so the async transform will be the identity transform. So, in the example above, if layers
+ L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
+ RN and RD will be identity transforms.
+ Additionally, for space-saving purposes, each APZC instance stores its layer's individual
+ CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
+ layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
+ The APZC instances track the last dispatched paint request and so are able to calculate LD and
+ PD using those internally stored values.
+ The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
+ required can be generated.
+ */
+
+/*
+ * See the long comment above for a detailed explanation of this function.
+ */
+ScreenToParentLayerMatrix4x4
+APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const
+{
+ Matrix4x4 result;
+ MutexAutoLock lock(mTreeLock);
+
+ // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
+ // explained in the comment above. This function is called with aApzc at L, and the loop
+ // below performs one iteration, where parent is at P. The comments explain what values are stored
+ // in the variables at these two levels. All the comments use standard matrix notation where the
+ // leftmost matrix in a multiplication is applied first.
+
+ // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
+
+ // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ result = ancestorUntransform;
+
+ for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
+ // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
+ ancestorUntransform = parent->GetAncestorTransform().Inverse();
+ // asyncUntransform is updated to PA.Inverse() when parent == P
+ Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
+ // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
+ Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
+
+ // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+ result = untransformSinceLastApzc * result;
+
+ // The above value for result when parent == P matches the required output
+ // as explained in the comment above this method. Note that any missing
+ // terms are guaranteed to be identity transforms.
+ }
+
+ return ViewAs<ScreenToParentLayerMatrix4x4>(result);
+}
+
+/*
+ * See the long comment above GetScreenToApzcTransform() for a detailed
+ * explanation of this function.
+ */
+ParentLayerToScreenMatrix4x4
+APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const
+{
+ Matrix4x4 result;
+ MutexAutoLock lock(mTreeLock);
+
+ // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
+ // explained in the comment above. This function is called with aApzc at L, and the loop
+ // below performs one iteration, where parent is at P. The comments explain what values are stored
+ // in the variables at these two levels. All the comments use standard matrix notation where the
+ // leftmost matrix in a multiplication is applied first.
+
+ // asyncUntransform is LA.Inverse()
+ Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
+
+ // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
+ result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
+
+ for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
+ // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
+ result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
+
+ // The above value for result when parent == P matches the required output
+ // as explained in the comment above this method. Note that any missing
+ // terms are guaranteed to be identity transforms.
+ }
+
+ return ViewAs<ParentLayerToScreenMatrix4x4>(result);
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
+{
+ MutexAutoLock lock(mTreeLock);
+ RefPtr<AsyncPanZoomController> apzc;
+ // For now, we only ever want to do pinching on the root-content APZC for
+ // a given layers id.
+ if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
+ // If the two APZCs have the same layers id, find the root-content APZC
+ // for that layers id. Don't call CommonAncestor() because there may not
+ // be a common ancestor for the layers id (e.g. if one APZCs is inside a
+ // fixed-position element).
+ apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
+ } else {
+ // Otherwise, find the common ancestor (to reach a common layers id), and
+ // get the root-content APZC for that layers id.
+ apzc = CommonAncestor(aApzc1, aApzc2);
+ if (apzc) {
+ apzc = FindRootContentApzcForLayersId(apzc->GetLayersId());
+ }
+ }
+ return apzc.forget();
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
+{
+ mTreeLock.AssertCurrentThreadOwns();
+ RefPtr<AsyncPanZoomController> ancestor;
+
+ // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
+ // will return null.
+
+ // Calculate depth of the APZCs in the tree
+ int depth1 = 0, depth2 = 0;
+ for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
+ depth1++;
+ }
+ for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
+ depth2++;
+ }
+
+ // At most one of the following two loops will be executed; the deeper APZC pointer
+ // will get walked up to the depth of the shallower one.
+ int minDepth = depth1 < depth2 ? depth1 : depth2;
+ while (depth1 > minDepth) {
+ depth1--;
+ aApzc1 = aApzc1->GetParent();
+ }
+ while (depth2 > minDepth) {
+ depth2--;
+ aApzc2 = aApzc2->GetParent();
+ }
+
+ // Walk up the ancestor chains of both APZCs, always staying at the same depth for
+ // either APZC, and return the the first common ancestor encountered.
+ while (true) {
+ if (aApzc1 == aApzc2) {
+ ancestor = aApzc1;
+ break;
+ }
+ if (depth1 <= 0) {
+ break;
+ }
+ aApzc1 = aApzc1->GetParent();
+ aApzc2 = aApzc2->GetParent();
+ }
+ return ancestor.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h
new file mode 100644
index 000000000..c98e292ef
--- /dev/null
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManager_h
+#define mozilla_layers_APZCTreeManager_h
+
+#include <map> // for std::map
+
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/TouchCounter.h"// for TouchCounter
+#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
+#include "mozilla/Mutex.h" // for Mutex
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp
+#include "nsCOMPtr.h" // for already_AddRefed
+
+
+namespace mozilla {
+class MultiTouchInput;
+
+namespace layers {
+
+class Layer;
+class AsyncPanZoomController;
+class APZCTreeManagerParent;
+class CompositorBridgeParent;
+class OverscrollHandoffChain;
+struct OverscrollHandoffState;
+struct FlingHandoffState;
+class LayerMetricsWrapper;
+class InputQueue;
+class GeckoContentController;
+class HitTestingTreeNode;
+
+/**
+ * ****************** NOTE ON LOCK ORDERING IN APZ **************************
+ *
+ * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
+ * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
+ *
+ * To avoid deadlock, we impose a lock ordering between these locks, which is:
+ *
+ * tree lock -> APZC locks
+ *
+ * The interpretation of the lock ordering is that if lock A precedes lock B
+ * in the ordering sequence, then you must NOT wait on A while holding B.
+ *
+ * **************************************************************************
+ */
+
+/**
+ * This class manages the tree of AsyncPanZoomController instances. There is one
+ * instance of this class owned by each CompositorBridgeParent, and it contains as
+ * many AsyncPanZoomController instances as there are scrollable container layers.
+ * This class generally lives on the compositor thread, although some functions
+ * may be called from other threads as noted; thread safety is ensured internally.
+ *
+ * The bulk of the work of this class happens as part of the UpdateHitTestingTree
+ * function, which is when a layer tree update is received by the compositor.
+ * This function walks through the layer tree and creates a tree of
+ * HitTestingTreeNode instances to match the layer tree and for use in
+ * hit-testing on the controller thread. APZC instances may be preserved across
+ * calls to this function if the corresponding layers are still present in the layer
+ * tree.
+ *
+ * The other functions on this class are used by various pieces of client code to
+ * notify the APZC instances of events relevant to them. This includes, for example,
+ * user input events that drive panning and zooming, changes to the scroll viewport
+ * area, and changes to pan/zoom constraints.
+ *
+ * Note that the ClearTree function MUST be called when this class is no longer needed;
+ * see the method documentation for details.
+ *
+ * Behaviour of APZ is controlled by a number of preferences shown \ref APZCPrefs "here".
+ */
+class APZCTreeManager : public IAPZCTreeManager {
+
+ typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
+ typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics;
+
+ // Helper struct to hold some state while we build the hit-testing tree. The
+ // sole purpose of this struct is to shorten the argument list to
+ // UpdateHitTestingTree. All the state that we don't need to
+ // push on the stack during recursion and pop on unwind is stored here.
+ struct TreeBuildingState;
+
+public:
+ APZCTreeManager();
+
+ /**
+ * Initializes the global state used in AsyncPanZoomController.
+ * This is normally called when it is first needed in the constructor
+ * of APZCTreeManager, but can be called manually to force it to be
+ * initialized earlier.
+ */
+ static void InitializeGlobalState();
+
+ /**
+ * Rebuild the hit-testing tree based on the layer update that just came up.
+ * Preserve nodes and APZC instances where possible, but retire those whose
+ * layers are no longer in the layer tree.
+ *
+ * This must be called on the compositor thread as it walks the layer tree.
+ *
+ * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
+ * to this APZCTreeManager
+ * @param aRoot The root of the (full) layer tree
+ * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint
+ * applies.
+ * @param aIsFirstPaint True if the layers update that this is called in response
+ * to included a first-paint. If this is true, the part of
+ * the tree that is affected by the first-paint flag is
+ * indicated by the aFirstPaintLayersId parameter.
+ * @param aPaintSequenceNumber The sequence number of the paint that triggered
+ * this layer update. Note that every layer child
+ * process' layer subtree has its own sequence
+ * numbers.
+ */
+ void UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+ Layer* aRoot,
+ bool aIsFirstPaint,
+ uint64_t aOriginatingLayersId,
+ uint32_t aPaintSequenceNumber);
+
+ /**
+ * Walk the tree of APZCs and flushes the repaint requests for all the APZCS
+ * corresponding to the given layers id. Finally, sends a flush complete
+ * notification to the GeckoContentController for the layers id.
+ */
+ void FlushApzRepaints(uint64_t aLayersId);
+
+ /**
+ * General handler for incoming input events. Manipulates the frame metrics
+ * based on what type of input it is. For example, a PinchGestureEvent will
+ * cause scaling. This should only be called externally to this class, and
+ * must be called on the controller thread.
+ *
+ * This function transforms |aEvent| to have its coordinates in DOM space.
+ * This is so that the event can be passed through the DOM and content can
+ * handle them. The event may need to be converted to a WidgetInputEvent
+ * by the caller if it wants to do this.
+ *
+ * The following values may be returned by this function:
+ * nsEventStatus_eConsumeNoDefault is returned to indicate the
+ * APZ is consuming this event and the caller should discard the event with
+ * extreme prejudice. The exact scenarios under which this is returned is
+ * implementation-dependent and may vary.
+ * nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
+ * use this event. This might be because it was directed at a point on
+ * the screen where there was no APZ, or because the thing the user was
+ * trying to do was not allowed. (For example, attempting to pan a
+ * non-pannable document).
+ * nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
+ * code may have used this event to do some user-visible thing. Note that
+ * in some cases CONSUMED is returned even if the event was NOT used. This
+ * is because we cannot always know at the time of event delivery whether
+ * the event will be used or not. So we err on the side of sending
+ * CONSUMED when we are uncertain.
+ *
+ * @param aEvent input event object; is modified in-place
+ * @param aOutTargetGuid returns the guid of the apzc this event was
+ * delivered to. May be null.
+ * @param aOutInputBlockId returns the id of the input block that this event
+ * was added to, if that was the case. May be null.
+ */
+ nsEventStatus ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up. |aRect| must be given in CSS pixels, relative to the document.
+ * |aFlags| is a combination of the ZoomToRectBehavior enum values.
+ */
+ void ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ /**
+ * If we have touch listeners, this should always be called when we know
+ * definitively whether or not content has preventDefaulted any touch events
+ * that have come in. If |aPreventDefault| is true, any touch events in the
+ * queue will be discarded. This function must be called on the controller
+ * thread.
+ */
+ void ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ /**
+ * When the event regions code is enabled, this function should be invoked to
+ * to confirm the target of the input block. This is only needed in cases
+ * where the initial input event of the block hit a dispatch-to-content region
+ * but is safe to call for all input blocks. This function should always be
+ * invoked on the controller thread.
+ * The different elements in the array of targets correspond to the targets
+ * for the different touch points. In the case where the touch point has no
+ * target, or the target is not a scrollable frame, the target's |mScrollId|
+ * should be set to FrameMetrics::NULL_SCROLL_ID.
+ */
+ void SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ /**
+ * Helper function for SetTargetAPZC when used with single-target events,
+ * such as mouse wheel events.
+ */
+ void SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget);
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ * If the |aConstraints| is Nothing() then previously-provided constraints for
+ * the given |aGuid| are cleared.
+ */
+ void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ /**
+ * Cancels any currently running animation. Note that all this does is set the
+ * state of the AsyncPanZoomController back to NOTHING, but it is the
+ * animation's responsibility to check this before advancing.
+ */
+ void CancelAnimation(const ScrollableLayerGuid &aGuid) override;
+
+ /**
+ * Adjusts the root APZC to compensate for a shift in the surface. See the
+ * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
+ * some more details. This is only currently needed due to surface shifts
+ * caused by the dynamic toolbar on Android.
+ */
+ void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ /**
+ * Calls Destroy() on all APZC instances attached to the tree, and resets the
+ * tree back to empty. This function must be called exactly once during the
+ * lifetime of this APZCTreeManager, when this APZCTreeManager is no longer
+ * needed. Failing to call this function may prevent objects from being freed
+ * properly.
+ */
+ void ClearTree();
+
+ /**
+ * Tests if a screen point intersect an apz in the tree.
+ */
+ bool HitTestAPZC(const ScreenIntPoint& aPoint);
+
+ /**
+ * See AsyncPanZoomController::CalculatePendingDisplayPort. This
+ * function simply delegates to that one, so that non-layers code
+ * never needs to include AsyncPanZoomController.h
+ */
+ static const ScreenMargin CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ /**
+ * Sets the dpi value used by all AsyncPanZoomControllers.
+ * DPI defaults to 72 if not set using SetDPI() at any point.
+ */
+ void SetDPI(float aDpiValue) override { sDPI = aDpiValue; }
+
+ /**
+ * Returns the current dpi value in use.
+ */
+ static float GetDPI() { return sDPI; }
+
+ /**
+ * Find the hit testing node for the scrollbar thumb that matches these
+ * drag metrics.
+ */
+ RefPtr<HitTestingTreeNode> FindScrollNode(const AsyncDragMetrics& aDragMetrics);
+
+ /**
+ * Sets allowed touch behavior values for current touch-session for specific
+ * input block (determined by aInputBlock).
+ * Should be invoked by the widget. Each value of the aValues arrays
+ * corresponds to the different touch point that is currently active.
+ * Must be called after receiving the TOUCH_START event that starts the
+ * touch-session.
+ * This must be called on the controller thread.
+ */
+ void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ /**
+ * This is a callback for AsyncPanZoomController to call when it wants to
+ * scroll in response to a touch-move event, or when it needs to hand off
+ * overscroll to the next APZC. Note that because of scroll grabbing, the
+ * first APZC to scroll may not be the one that is receiving the touch events.
+ *
+ * |aAPZC| is the APZC that received the touch events triggering the scroll
+ * (in the case of an initial scroll), or the last APZC to scroll (in the
+ * case of overscroll)
+ * |aStartPoint| and |aEndPoint| are in |aAPZC|'s transformed screen
+ * coordinates (i.e. the same coordinates in which touch points are given to
+ * APZCs). The amount of (over)scroll is represented by two points rather
+ * than a displacement because with certain 3D transforms, the same
+ * displacement between different points in transformed coordinates can
+ * represent different displacements in untransformed coordinates.
+ * |aOverscrollHandoffChain| is the overscroll handoff chain used for
+ * determining the order in which scroll should be handed off between
+ * APZCs
+ * |aOverscrollHandoffChainIndex| is the next position in the overscroll
+ * handoff chain that should be scrolled.
+ *
+ * aStartPoint and aEndPoint will be modified depending on how much of the
+ * scroll each APZC consumes. This is to allow the sending APZC to go into
+ * an overscrolled state if no APZC further up in the handoff chain accepted
+ * the entire scroll.
+ *
+ * The way this method works is best illustrated with an example.
+ * Consider three nested APZCs, A, B, and C, with C being the innermost one.
+ * Say B is scroll-grabbing.
+ * The touch events go to C because it's the innermost one (so e.g. taps
+ * should go through C), but the overscroll handoff chain is B -> C -> A
+ * because B is scroll-grabbing.
+ * For convenience I'll refer to the three APZC objects as A, B, and C, and
+ * to the tree manager object as TM.
+ * Here's what happens when C receives a touch-move event:
+ * - C.TrackTouch() calls TM.DispatchScroll() with index = 0.
+ * - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain).
+ * - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1.
+ * - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain)
+ * - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2.
+ * - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain)
+ * - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3.
+ * - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain.
+ *
+ * Note: this should be used for panning only. For handing off overscroll for
+ * a fling, use DispatchFling().
+ */
+ void DispatchScroll(AsyncPanZoomController* aApzc,
+ ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ /**
+ * This is a callback for AsyncPanZoomController to call when it wants to
+ * start a fling in response to a touch-end event, or when it needs to hand
+ * off a fling to the next APZC. Note that because of scroll grabbing, the
+ * first APZC to fling may not be the one that is receiving the touch events.
+ *
+ * @param aApzc the APZC that wants to start or hand off the fling
+ * @param aHandoffState a collection of state about the operation,
+ * which contains the following:
+ *
+ * mVelocity the current velocity of the fling, in |aApzc|'s screen
+ * pixels per millisecond
+ * mChain the chain of APZCs along which the fling
+ * should be handed off
+ * mIsHandoff is true if |aApzc| is handing off an existing fling (in
+ * this case the fling is given to the next APZC in the
+ * handoff chain after |aApzc|), and false is |aApzc| wants
+ * start a fling (in this case the fling is given to the
+ * first APZC in the chain)
+ *
+ * aHandoffState.mVelocity will be modified depending on how much of that
+ * velocity has been consumed by APZCs in the overscroll hand-off chain.
+ * The caller can use this value to determine whether it should consume
+ * the excess velocity by going into an overscroll fling.
+ */
+ void DispatchFling(AsyncPanZoomController* aApzc,
+ FlingHandoffState& aHandoffState);
+
+ void StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ /*
+ * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|.
+ */
+ RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget);
+
+ /**
+ * Function used to disable LongTap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ void SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ // Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
+
+ void TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) override;
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~APZCTreeManager();
+
+ // Protected hooks for gtests subclass
+ virtual AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController);
+public:
+ // Public hooks for gtests subclass
+ virtual TimeStamp GetFrameTime();
+
+public:
+ /* Some helper functions to find an APZC given some identifying input. These functions
+ lock the tree of APZCs while they find the right one, and then return an addref'd
+ pointer to it. This allows caller code to just use the target APZC without worrying
+ about it going away. These are public for testing code and generally should not be
+ used by other production code.
+ */
+ RefPtr<HitTestingTreeNode> GetRootNode() const;
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar = nullptr);
+ ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
+ ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
+
+ /**
+ * Process touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
+private:
+ typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
+
+ /* Helpers */
+ void AttachNodeToTree(HitTestingTreeNode* aNode,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling);
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
+ already_AddRefed<HitTestingTreeNode> GetTargetNode(const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator);
+ HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode,
+ const ScrollableLayerGuid& aGuid,
+ GuidComparator aComparator);
+ AsyncPanZoomController* GetTargetApzcForNode(HitTestingTreeNode* aNode);
+ AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode,
+ const ParentLayerPoint& aHitTestPoint,
+ HitTestResult* aOutHitResult,
+ bool* aOutHitScrollbar);
+ AsyncPanZoomController* FindRootApzcForLayersId(uint64_t aLayersId) const;
+ AsyncPanZoomController* FindRootContentApzcForLayersId(uint64_t aLayersId) const;
+ AsyncPanZoomController* FindRootContentOrRootApzc() const;
+ already_AddRefed<AsyncPanZoomController> GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
+ already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
+ already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
+ nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
+ HitTestResult* aOutHitResult);
+ nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId);
+ void FlushRepaintsToClearScreenToGeckoTransform();
+
+ already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState,
+ AsyncPanZoomController* aApzc,
+ uint64_t aLayersId);
+ HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+ const FrameMetrics& aMetrics,
+ uint64_t aLayersId,
+ const gfx::Matrix4x4& aAncestorTransform,
+ HitTestingTreeNode* aParent,
+ HitTestingTreeNode* aNextSibling,
+ TreeBuildingState& aState);
+
+ void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+ const AsyncPanZoomController* apzc);
+
+protected:
+ /* The input queue where input events are held until we know enough to
+ * figure out where they're going. Protected so gtests can access it.
+ */
+ RefPtr<InputQueue> mInputQueue;
+
+private:
+ /* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
+ * This lock does not need to be held while manipulating a single APZC instance in
+ * isolation (that is, if its tree pointers are not being accessed or mutated). The
+ * lock also needs to be held when accessing the mRootNode instance variable, as that
+ * is considered part of the APZC tree management state.
+ * Finally, the lock needs to be held when accessing mZoomConstraints.
+ * IMPORTANT: See the note about lock ordering at the top of this file. */
+ mutable mozilla::Mutex mTreeLock;
+ RefPtr<HitTestingTreeNode> mRootNode;
+ /* Holds the zoom constraints for scrollable layers, as determined by the
+ * the main-thread gecko code. */
+ std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints;
+ /* This tracks the APZC that should receive all inputs for the current input event block.
+ * This allows touch points to move outside the thing they started on, but still have the
+ * touch events delivered to the same initial APZC. This will only ever be touched on the
+ * input delivery thread, and so does not require locking.
+ */
+ RefPtr<AsyncPanZoomController> mApzcForInputBlock;
+ /* The hit result for the current input event block; this should always be in
+ * sync with mApzcForInputBlock.
+ */
+ HitTestResult mHitResultForInputBlock;
+ /* Sometimes we want to ignore all touches except one. In such cases, this
+ * is set to the identifier of the touch we are not ignoring; in other cases,
+ * this is set to -1.
+ */
+ int32_t mRetainedTouchIdentifier;
+ /* Tracks the number of touch points we are tracking that are currently on
+ * the screen. */
+ TouchCounter mTouchCounter;
+ /* For logging the APZC tree for debugging (enabled by the apz.printtree
+ * pref). */
+ gfx::TreeLog mApzcTreeLog;
+
+ class CheckerboardFlushObserver;
+ friend class CheckerboardFlushObserver;
+ RefPtr<CheckerboardFlushObserver> mFlushObserver;
+
+ static float sDPI;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PanZoomController_h
diff --git a/gfx/layers/apz/src/APZUtils.h b/gfx/layers/apz/src/APZUtils.h
new file mode 100644
index 000000000..222788afa
--- /dev/null
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZUtils_h
+#define mozilla_layers_APZUtils_h
+
+#include <stdint.h> // for uint32_t
+#include "LayersTypes.h"
+#include "UnitTransforms.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/FloatingPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+enum HitTestResult {
+ HitNothing,
+ HitLayer,
+ HitLayerTouchActionNone,
+ HitLayerTouchActionPanX,
+ HitLayerTouchActionPanY,
+ HitLayerTouchActionPanXY,
+ HitDispatchToContentRegion,
+};
+
+enum CancelAnimationFlags : uint32_t {
+ Default = 0x0, /* Cancel all animations */
+ ExcludeOverscroll = 0x1, /* Don't clear overscroll */
+ ScrollSnap = 0x2 /* Snap to snap points */
+};
+
+inline CancelAnimationFlags
+operator|(CancelAnimationFlags a, CancelAnimationFlags b)
+{
+ return static_cast<CancelAnimationFlags>(static_cast<int>(a)
+ | static_cast<int>(b));
+}
+
+enum class ScrollSource {
+ // scrollTo() or something similar.
+ DOM,
+
+ // Touch-screen or trackpad with gesture support.
+ Touch,
+
+ // Mouse wheel.
+ Wheel
+};
+
+typedef uint32_t TouchBehaviorFlags;
+
+// Epsilon to be used when comparing 'float' coordinate values
+// with FuzzyEqualsAdditive. The rationale is that 'float' has 7 decimal
+// digits of precision, and coordinate values should be no larger than in the
+// ten thousands. Note also that the smallest legitimate difference in page
+// coordinates is 1 app unit, which is 1/60 of a (CSS pixel), so this epsilon
+// isn't too large.
+const float COORDINATE_EPSILON = 0.01f;
+
+template <typename Units>
+static bool IsZero(const gfx::PointTyped<Units>& aPoint)
+{
+ return FuzzyEqualsAdditive(aPoint.x, 0.0f, COORDINATE_EPSILON)
+ && FuzzyEqualsAdditive(aPoint.y, 0.0f, COORDINATE_EPSILON);
+}
+
+// Deem an AsyncTransformComponentMatrix (obtained by multiplying together
+// one or more AsyncTransformComponentMatrix objects) as constituting a
+// complete async transform.
+inline AsyncTransformMatrix
+CompleteAsyncTransform(const AsyncTransformComponentMatrix& aMatrix)
+{
+ return ViewAs<AsyncTransformMatrix>(aMatrix,
+ PixelCastJustification::MultipleAsyncTransforms);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZUtils_h
diff --git a/gfx/layers/apz/src/AndroidAPZ.cpp b/gfx/layers/apz/src/AndroidAPZ.cpp
new file mode 100644
index 000000000..70042a870
--- /dev/null
+++ b/gfx/layers/apz/src/AndroidAPZ.cpp
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AndroidAPZ.h"
+
+#include "AsyncPanZoomController.h"
+#include "GeneratedJNIWrappers.h"
+#include "gfxPrefs.h"
+#include "OverscrollHandoffState.h"
+#include "ViewConfiguration.h"
+
+#define ANDROID_APZ_LOG(...)
+// #define ANDROID_APZ_LOG(...) printf_stderr("ANDROID_APZ: " __VA_ARGS__)
+
+static float sMaxFlingSpeed = 0.0f;
+
+namespace mozilla {
+namespace layers {
+
+AndroidSpecificState::AndroidSpecificState() {
+ using namespace mozilla::java;
+
+ sdk::ViewConfiguration::LocalRef config;
+ if (sdk::ViewConfiguration::Get(GeckoAppShell::GetApplicationContext(), &config) == NS_OK) {
+ int32_t speed = 0;
+ if (config->GetScaledMaximumFlingVelocity(&speed) == NS_OK) {
+ sMaxFlingSpeed = (float)speed * 0.001f;
+ } else {
+ ANDROID_APZ_LOG("%p Failed to query ViewConfiguration for scaled maximum fling velocity\n", this);
+ }
+ } else {
+ ANDROID_APZ_LOG("%p Failed to get ViewConfiguration\n", this);
+ }
+
+ StackScroller::LocalRef scroller;
+ if (StackScroller::New(GeckoAppShell::GetApplicationContext(), &scroller) != NS_OK) {
+ ANDROID_APZ_LOG("%p Failed to create Android StackScroller\n", this);
+ return;
+ }
+ mOverScroller = scroller;
+}
+
+const float BOUNDS_EPSILON = 1.0f;
+
+// This function is used to convert the scroll offset from a float to an integer
+// suitable for using with the Android OverScroller Class.
+// The Android OverScroller class (unfortunately) operates in integers instead of floats.
+// When casting a float value such as 1.5 to an integer, the value is converted to 1.
+// If this value represents the max scroll offset, the OverScroller class will never scroll
+// to the end of the page as it will always be 0.5 pixels short. To work around this issue,
+// the min and max scroll extents are floor/ceil to convert them to the nearest integer
+// just outside of the actual scroll extents. This means, the starting
+// scroll offset must be converted the same way so that if the frame has already been
+// scrolled 1.5 pixels, it won't be snapped back when converted to an integer. This integer
+// rounding error was one of several causes of Bug 1276463.
+static int32_t
+ClampStart(float aOrigin, float aMin, float aMax)
+{
+ if (aOrigin <= aMin) {
+ return (int32_t)floor(aMin);
+ } else if (aOrigin >= aMax) {
+ return (int32_t)ceil(aMax);
+ }
+ return (int32_t)aOrigin;
+}
+
+AndroidFlingAnimation::AndroidFlingAnimation(AsyncPanZoomController& aApzc,
+ PlatformSpecificStateBase* aPlatformSpecificState,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ bool aFlingIsHandoff,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+ : mApzc(aApzc)
+ , mOverscrollHandoffChain(aOverscrollHandoffChain)
+ , mScrolledApzc(aScrolledApzc)
+ , mSentBounceX(false)
+ , mSentBounceY(false)
+ , mFlingDuration(0)
+{
+ MOZ_ASSERT(mOverscrollHandoffChain);
+ AndroidSpecificState* state = aPlatformSpecificState->AsAndroidSpecificState();
+ MOZ_ASSERT(state);
+ mOverScroller = state->mOverScroller;
+ MOZ_ASSERT(mOverScroller);
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't take the 'overscroll' path in Sample()
+ // on account of one axis which can't scroll having a velocity.
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mX.SetVelocity(0);
+ }
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mY.SetVelocity(0);
+ }
+
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ float scrollRangeStartX = mApzc.mX.GetPageStart().value;
+ float scrollRangeEndX = mApzc.mX.GetScrollRangeEnd().value;
+ float scrollRangeStartY = mApzc.mY.GetPageStart().value;
+ float scrollRangeEndY = mApzc.mY.GetScrollRangeEnd().value;
+ mStartOffset.x = mPreviousOffset.x = mApzc.mX.GetOrigin().value;
+ mStartOffset.y = mPreviousOffset.y = mApzc.mY.GetOrigin().value;
+ float length = velocity.Length();
+ if (length > 0.0f) {
+ mFlingDirection = velocity / length;
+
+ if ((sMaxFlingSpeed > 0.0f) && (length > sMaxFlingSpeed)) {
+ velocity = mFlingDirection * sMaxFlingSpeed;
+ }
+ }
+
+ mPreviousVelocity = velocity;
+
+ int32_t originX = ClampStart(mStartOffset.x, scrollRangeStartX, scrollRangeEndX);
+ int32_t originY = ClampStart(mStartOffset.y, scrollRangeStartY, scrollRangeEndY);
+ if (!state->mLastFling.IsNull()) {
+ // If it's been too long since the previous fling, or if the new fling's
+ // velocity is too low, don't allow flywheel to kick in. If we do allow
+ // flywheel to kick in, then we need to update the timestamp on the
+ // StackScroller because otherwise it might use a stale velocity.
+ TimeDuration flingDuration = TimeStamp::Now() - state->mLastFling;
+ if (flingDuration.ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
+ && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
+ bool unused = false;
+ mOverScroller->ComputeScrollOffset(flingDuration.ToMilliseconds(), &unused);
+ } else {
+ mOverScroller->ForceFinished(true);
+ }
+ }
+ mOverScroller->Fling(originX, originY,
+ // Android needs the velocity in pixels per second and it is in pixels per ms.
+ (int32_t)(velocity.x * 1000.0f), (int32_t)(velocity.y * 1000.0f),
+ (int32_t)floor(scrollRangeStartX), (int32_t)ceil(scrollRangeEndX),
+ (int32_t)floor(scrollRangeStartY), (int32_t)ceil(scrollRangeEndY),
+ 0, 0, 0);
+ state->mLastFling = TimeStamp::Now();
+}
+
+/**
+ * Advances a fling by an interpolated amount based on the Android OverScroller.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the fling animation should be advanced by one frame,
+ * or false if there is no fling or the fling has ended.
+ */
+bool
+AndroidFlingAnimation::DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta)
+{
+ bool shouldContinueFling = true;
+
+ mFlingDuration += aDelta.ToMilliseconds();
+ mOverScroller->ComputeScrollOffset(mFlingDuration, &shouldContinueFling);
+
+ int32_t currentX = 0;
+ int32_t currentY = 0;
+ mOverScroller->GetCurrX(&currentX);
+ mOverScroller->GetCurrY(&currentY);
+ ParentLayerPoint offset((float)currentX, (float)currentY);
+ ParentLayerPoint preCheckedOffset(offset);
+
+ bool hitBoundX = CheckBounds(mApzc.mX, offset.x, mFlingDirection.x, &(offset.x));
+ bool hitBoundY = CheckBounds(mApzc.mY, offset.y, mFlingDirection.y, &(offset.y));
+
+ ParentLayerPoint velocity = mPreviousVelocity;
+
+ // Sometimes the OverScroller fails to update the offset for a frame.
+ // If the frame can still scroll we just use the velocity from the previous
+ // frame. However, if the frame can no longer scroll in the direction
+ // of the fling, then end the animation.
+ if (offset != mPreviousOffset) {
+ if (aDelta.ToMilliseconds() > 0) {
+ mOverScroller->GetCurrSpeedX(&velocity.x);
+ mOverScroller->GetCurrSpeedY(&velocity.y);
+
+ velocity.x /= 1000;
+ velocity.y /= 1000;
+
+ mPreviousVelocity = velocity;
+ }
+ } else if ((fabsf(offset.x - preCheckedOffset.x) > BOUNDS_EPSILON) || (fabsf(offset.y - preCheckedOffset.y) > BOUNDS_EPSILON)) {
+ // The page is no longer scrolling but the fling animation is still animating beyond the page bounds. If it goes
+ // beyond the BOUNDS_EPSILON then it has overflowed and will never stop. In that case, stop the fling animation.
+ shouldContinueFling = false;
+ } else if (hitBoundX && hitBoundY) {
+ // We can't scroll any farther along either axis.
+ shouldContinueFling = false;
+ }
+
+ float speed = velocity.Length();
+
+ // gfxPrefs::APZFlingStoppedThreshold is only used in tests.
+ if (!shouldContinueFling || (speed < gfxPrefs::APZFlingStoppedThreshold())) {
+ if (shouldContinueFling) {
+ // The OverScroller thinks it should continue but the speed is below
+ // the stopping threshold so abort the animation.
+ mOverScroller->AbortAnimation();
+ }
+ // This animation is going to end. If DeferHandleFlingOverscroll
+ // has not been called and there is still some velocity left,
+ // call it so that fling hand off may occur if applicable.
+ if (!mSentBounceX && !mSentBounceY && (speed > 0.0f)) {
+ DeferHandleFlingOverscroll(velocity);
+ }
+ return false;
+ }
+
+ mPreviousOffset = offset;
+
+ mApzc.SetVelocityVector(velocity);
+ aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());
+
+ // If we hit a bounds while flinging, send the velocity so that the bounce
+ // animation can play.
+ if (hitBoundX || hitBoundY) {
+ ParentLayerPoint bounceVelocity = velocity;
+
+ if (!mSentBounceX && hitBoundX && fabsf(offset.x - mStartOffset.x) > BOUNDS_EPSILON) {
+ mSentBounceX = true;
+ } else {
+ bounceVelocity.x = 0.0f;
+ }
+
+ if (!mSentBounceY && hitBoundY && fabsf(offset.y - mStartOffset.y) > BOUNDS_EPSILON) {
+ mSentBounceY = true;
+ } else {
+ bounceVelocity.y = 0.0f;
+ }
+ if (!IsZero(bounceVelocity)) {
+ DeferHandleFlingOverscroll(bounceVelocity);
+ }
+ }
+
+ return true;
+}
+
+void
+AndroidFlingAnimation::DeferHandleFlingOverscroll(ParentLayerPoint& aVelocity)
+{
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint,
+ RefPtr<const OverscrollHandoffChain>,
+ RefPtr<const AsyncPanZoomController>>(&mApzc,
+ &AsyncPanZoomController::HandleFlingOverscroll,
+ aVelocity,
+ mOverscrollHandoffChain,
+ mScrolledApzc));
+
+}
+
+bool
+AndroidFlingAnimation::CheckBounds(Axis& aAxis, float aValue, float aDirection, float* aClamped)
+{
+ if ((aDirection < 0.0f) && (aValue <= aAxis.GetPageStart().value)) {
+ if (aClamped) {
+ *aClamped = aAxis.GetPageStart().value;
+ }
+ return true;
+ } else if ((aDirection > 0.0f) && (aValue >= aAxis.GetScrollRangeEnd().value)) {
+ if (aClamped) {
+ *aClamped = aAxis.GetScrollRangeEnd().value;
+ }
+ return true;
+ }
+ return false;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/AndroidAPZ.h b/gfx/layers/apz/src/AndroidAPZ.h
new file mode 100644
index 000000000..404892da5
--- /dev/null
+++ b/gfx/layers/apz/src/AndroidAPZ.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AndroidAPZ_h_
+#define mozilla_layers_AndroidAPZ_h_
+
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "GeneratedJNIWrappers.h"
+
+namespace mozilla {
+namespace layers {
+
+class AndroidSpecificState : public PlatformSpecificStateBase {
+public:
+ AndroidSpecificState();
+
+ virtual AndroidSpecificState* AsAndroidSpecificState() override {
+ return this;
+ }
+
+ java::StackScroller::GlobalRef mOverScroller;
+ TimeStamp mLastFling;
+};
+
+class AndroidFlingAnimation: public AsyncPanZoomAnimation {
+public:
+ AndroidFlingAnimation(AsyncPanZoomController& aApzc,
+ PlatformSpecificStateBase* aPlatformSpecificState,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ bool aFlingIsHandoff /* ignored */,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc);
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override;
+private:
+ void DeferHandleFlingOverscroll(ParentLayerPoint& aVelocity);
+ // Returns true if value is on or outside of axis bounds.
+ bool CheckBounds(Axis& aAxis, float aValue, float aDirection, float* aClamped);
+
+ AsyncPanZoomController& mApzc;
+ java::StackScroller::GlobalRef mOverScroller;
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+ bool mSentBounceX;
+ bool mSentBounceY;
+ long mFlingDuration;
+ ParentLayerPoint mStartOffset;
+ ParentLayerPoint mPreviousOffset;
+ // Unit vector in the direction of the fling.
+ ParentLayerPoint mFlingDirection;
+ ParentLayerPoint mPreviousVelocity;
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AndroidAPZ_h_
diff --git a/gfx/layers/apz/src/AsyncDragMetrics.h b/gfx/layers/apz/src/AsyncDragMetrics.h
new file mode 100644
index 000000000..54b60f823
--- /dev/null
+++ b/gfx/layers/apz/src/AsyncDragMetrics.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DragMetrics_h
+#define mozilla_layers_DragMetrics_h
+
+#include "FrameMetrics.h"
+
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+
+namespace layers {
+
+class AsyncDragMetrics {
+ friend struct IPC::ParamTraits<mozilla::layers::AsyncDragMetrics>;
+
+public:
+ enum DragDirection {
+ NONE,
+ VERTICAL,
+ HORIZONTAL,
+ SENTINEL,
+ };
+
+ // IPC constructor
+ AsyncDragMetrics()
+ : mViewId(0)
+ , mPresShellId(0)
+ , mDragStartSequenceNumber(0)
+ , mScrollbarDragOffset(0)
+ , mDirection(NONE)
+ {}
+
+ AsyncDragMetrics(const FrameMetrics::ViewID& aViewId,
+ uint32_t aPresShellId,
+ uint64_t aDragStartSequenceNumber,
+ CSSIntCoord aScrollbarDragOffset,
+ const CSSIntRect& aScrollTrack,
+ DragDirection aDirection)
+ : mViewId(aViewId)
+ , mPresShellId(aPresShellId)
+ , mDragStartSequenceNumber(aDragStartSequenceNumber)
+ , mScrollbarDragOffset(aScrollbarDragOffset)
+ , mScrollTrack(aScrollTrack)
+ , mDirection(aDirection)
+ {}
+
+ FrameMetrics::ViewID mViewId;
+ uint32_t mPresShellId;
+ uint64_t mDragStartSequenceNumber;
+ CSSIntCoord mScrollbarDragOffset;
+ CSSIntRect mScrollTrack;
+ DragDirection mDirection;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/apz/src/AsyncPanZoomAnimation.h b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
new file mode 100644
index 000000000..039c0684e
--- /dev/null
+++ b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AsyncPanZoomAnimation_h_
+#define mozilla_layers_AsyncPanZoomAnimation_h_
+
+#include "base/message_loop.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "FrameMetrics.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+class WheelScrollAnimation;
+class SmoothScrollAnimation;
+
+class AsyncPanZoomAnimation {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
+
+public:
+ explicit AsyncPanZoomAnimation()
+ { }
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) = 0;
+
+ bool Sample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) {
+ // In some situations, particularly when handoff is involved, it's possible
+ // for |aDelta| to be negative on the first call to sample. Ignore such a
+ // sample here, to avoid each derived class having to deal with this case.
+ if (aDelta.ToMilliseconds() <= 0) {
+ return true;
+ }
+
+ return DoSample(aFrameMetrics, aDelta);
+ }
+
+ /**
+ * Get the deferred tasks in |mDeferredTasks| and place them in |aTasks|. See
+ * |mDeferredTasks| for more information. Clears |mDeferredTasks|.
+ */
+ nsTArray<RefPtr<Runnable>> TakeDeferredTasks() {
+ return Move(mDeferredTasks);
+ }
+
+ virtual WheelScrollAnimation* AsWheelScrollAnimation() {
+ return nullptr;
+ }
+ virtual SmoothScrollAnimation* AsSmoothScrollAnimation() {
+ return nullptr;
+ }
+
+ virtual bool WantsRepaints() {
+ return true;
+ }
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~AsyncPanZoomAnimation()
+ { }
+
+ /**
+ * Tasks scheduled for execution after the APZC's mMonitor is released.
+ * Derived classes can add tasks here in Sample(), and the APZC can call
+ * ExecuteDeferredTasks() to execute them.
+ */
+ nsTArray<RefPtr<Runnable>> mDeferredTasks;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AsyncPanZoomAnimation_h_
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
new file mode 100644
index 000000000..102f282f3
--- /dev/null
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -0,0 +1,4030 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h> // for fabsf, fabs, atan2
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include <algorithm> // for max, min
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc
+#include "Axis.h" // for AxisX, AxisY, Axis, etc
+#include "CheckerboardEvent.h" // for CheckerboardEvent
+#include "Compositor.h" // for Compositor
+#include "FrameMetrics.h" // for FrameMetrics, etc
+#include "GenericFlingAnimation.h" // for GenericFlingAnimation
+#include "GestureEventListener.h" // for GestureEventListener
+#include "HitTestingTreeNode.h" // for HitTestingTreeNode
+#include "InputData.h" // for MultiTouchInput, etc
+#include "InputBlockState.h" // for InputBlockState, TouchBlockState
+#include "InputQueue.h" // for InputQueue
+#include "Overscroll.h" // for OverscrollAnimation
+#include "OverscrollHandoffState.h" // for OverscrollHandoffState
+#include "Units.h" // for CSSRect, CSSPoint, etc
+#include "UnitTransforms.h" // for TransformTo
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxTypes.h" // for gfxFloat
+#include "LayersLogging.h" // for print_stderr
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_*
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/EventForwards.h" // for nsEventStatus_*
+#include "mozilla/MouseEvents.h" // for WidgetWheelEvent
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/Telemetry.h" // for Telemetry
+#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
+#include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
+ // note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
+#include "mozilla/dom/Touch.h" // for Touch
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for Point, RoundedToInt, etc
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
+#include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
+#include "mozilla/layers/CompositorController.h" // for CompositorController
+#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
+#include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
+#include "mozilla/layers/ScrollInputMethods.h" // for ScrollInputMethod
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Unused.h" // for unused
+#include "mozilla/FloatingPoint.h" // for FuzzyEquals*
+#include "nsAlgorithm.h" // for clamped
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsIDOMWindowUtils.h" // for nsIDOMWindowUtils
+#include "nsMathUtils.h" // for NS_hypot
+#include "nsPoint.h" // for nsIntPoint
+#include "nsStyleConsts.h"
+#include "nsStyleStruct.h" // for nsTimingFunction
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsViewportInfo.h" // for kViewportMinScale, kViewportMaxScale
+#include "prsystem.h" // for PR_GetPhysicalMemorySize
+#include "SharedMemoryBasic.h" // for SharedMemoryBasic
+#include "ScrollSnap.h" // for ScrollSnapUtils
+#include "WheelScrollAnimation.h"
+#if defined(MOZ_WIDGET_ANDROID)
+#include "AndroidAPZ.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+#define ENABLE_APZC_LOGGING 0
+// #define ENABLE_APZC_LOGGING 1
+
+#if ENABLE_APZC_LOGGING
+# define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
+# define APZC_LOG_FM(fm, prefix, ...) \
+ { std::stringstream ss; \
+ ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
+ AppendToString(ss, fm, ":", "", true); \
+ APZC_LOG("%s\n", ss.str().c_str()); \
+ }
+#else
+# define APZC_LOG(...)
+# define APZC_LOG_FM(fm, prefix, ...)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
+typedef GeckoContentController::APZStateChange APZStateChange;
+typedef GeckoContentController::TapType TapType;
+typedef mozilla::gfx::Point Point;
+typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+using mozilla::gfx::PointTyped;
+
+// Choose between platform-specific implementations.
+#ifdef MOZ_WIDGET_ANDROID
+typedef WidgetOverscrollEffect OverscrollEffect;
+typedef AndroidSpecificState PlatformSpecificState;
+typedef AndroidFlingAnimation FlingAnimation;
+#else
+typedef GenericOverscrollEffect OverscrollEffect;
+typedef PlatformSpecificStateBase PlatformSpecificState; // no extra state, just use the base class
+typedef GenericFlingAnimation FlingAnimation;
+#endif
+
+/**
+ * \page APZCPrefs APZ preferences
+ *
+ * The following prefs are used to control the behaviour of the APZC.
+ * The default values are provided in gfxPrefs.h.
+ *
+ * \li\b apz.allow_checkerboarding
+ * Pref that allows or disallows checkerboarding
+ *
+ * \li\b apz.allow_immediate_handoff
+ * If set to true, scroll can be handed off from one APZC to another within
+ * a single input block. If set to false, a single input block can only
+ * scroll one APZC.
+ *
+ * \li\b apz.axis_lock.mode
+ * The preferred axis locking style. See AxisLockMode for possible values.
+ *
+ * \li\b apz.axis_lock.lock_angle
+ * Angle from axis within which we stay axis-locked.\n
+ * Units: radians
+ *
+ * \li\b apz.axis_lock.breakout_threshold
+ * Distance in inches the user must pan before axis lock can be broken.\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.axis_lock.breakout_angle
+ * Angle at which axis lock can be broken.\n
+ * Units: radians
+ *
+ * \li\b apz.axis_lock.direct_pan_angle
+ * If the angle from an axis to the line drawn by a pan move is less than
+ * this value, we can assume that panning can be done in the allowed direction
+ * (horizontal or vertical).\n
+ * Currently used only for touch-action css property stuff and was addded to
+ * keep behaviour consistent with IE.\n
+ * Units: radians
+ *
+ * \li\b apz.content_response_timeout
+ * Amount of time before we timeout response from content. For example, if
+ * content is being unruly/slow and we don't get a response back within this
+ * time, we will just pretend that content did not preventDefault any touch
+ * events we dispatched to it.\n
+ * Units: milliseconds
+ *
+ * \li\b apz.danger_zone_x
+ * \li\b apz.danger_zone_y
+ * When drawing high-res tiles, we drop down to drawing low-res tiles
+ * when we know we can't keep up with the scrolling. The way we determine
+ * this is by checking if we are entering the "danger zone", which is the
+ * boundary of the painted content. For example, if the painted content
+ * goes from y=0...1000 and the visible portion is y=250...750 then
+ * we're far from checkerboarding. If we get to y=490...990 though then we're
+ * only 10 pixels away from showing checkerboarding so we are probably in
+ * a state where we can't keep up with scrolling. The danger zone prefs specify
+ * how wide this margin is; in the above example a y-axis danger zone of 10
+ * pixels would make us drop to low-res at y=490...990.\n
+ * This value is in layer pixels.
+ *
+ * \li\b apz.disable_for_scroll_linked_effects
+ * Setting this pref to true will disable APZ scrolling on documents where
+ * scroll-linked effects are detected. A scroll linked effect is detected if
+ * positioning or transform properties are updated inside a scroll event
+ * dispatch; we assume that such an update is in response to the scroll event
+ * and is therefore a scroll-linked effect which will be laggy with APZ
+ * scrolling.
+ *
+ * \li\b apz.displayport_expiry_ms
+ * While a scrollable frame is scrolling async, we set a displayport on it
+ * to make sure it is layerized. However this takes up memory, so once the
+ * scrolling stops we want to remove the displayport. This pref controls how
+ * long after scrolling stops the displayport is removed. A value of 0 will
+ * disable the expiry behavior entirely.
+ * Units: milliseconds
+ *
+ * \li\b apz.enlarge_displayport_when_clipped
+ * Pref that enables enlarging of the displayport along one axis when the
+ * generated displayport's size is beyond that of the scrollable rect on the
+ * opposite axis.
+ *
+ * \li\b apz.fling_accel_interval_ms
+ * The time that determines whether a second fling will be treated as
+ * accelerated. If two flings are started within this interval, the second one
+ * will be accelerated. Setting an interval of 0 means that acceleration will
+ * be disabled.\n
+ * Units: milliseconds
+ *
+ * \li\b apz.fling_accel_min_velocity
+ * The minimum velocity of the second fling for it to be considered for fling
+ * acceleration.
+ * Units: screen pixels per milliseconds
+ *
+ * \li\b apz.fling_accel_base_mult
+ * \li\b apz.fling_accel_supplemental_mult
+ * When applying an acceleration on a fling, the new computed velocity is
+ * (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
+ * The base_mult and supplemental_mult multiplier values are controlled by
+ * these prefs. Note that "old_velocity" here is the initial velocity of the
+ * previous fling _after_ acceleration was applied to it (if applicable).
+ *
+ * \li\b apz.fling_curve_function_x1
+ * \li\b apz.fling_curve_function_y1
+ * \li\b apz.fling_curve_function_x2
+ * \li\b apz.fling_curve_function_y2
+ * \li\b apz.fling_curve_threshold_inches_per_ms
+ * These five parameters define a Bezier curve function and threshold used to
+ * increase the actual velocity relative to the user's finger velocity. When the
+ * finger velocity is below the threshold (or if the threshold is not positive),
+ * the velocity is used as-is. If the finger velocity exceeds the threshold
+ * velocity, then the function defined by the curve is applied on the part of
+ * the velocity that exceeds the threshold. Note that the upper bound of the
+ * velocity is still specified by the \b apz.max_velocity_inches_per_ms pref, and
+ * the function will smoothly curve the velocity from the threshold to the
+ * max. In general the function parameters chosen should define an ease-out
+ * curve in order to increase the velocity in this range, or an ease-in curve to
+ * decrease the velocity. A straight-line curve is equivalent to disabling the
+ * curve entirely by setting the threshold to -1. The max velocity pref must
+ * also be set in order for the curving to take effect, as it defines the upper
+ * bound of the velocity curve.\n
+ * The points (x1, y1) and (x2, y2) used as the two intermediate control points
+ * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
+ * Some example values for these prefs can be found at\n
+ * https://dxr.mozilla.org/mozilla-central/rev/70e05c6832e831374604ac3ce7433971368dffe0/layout/style/nsStyleStruct.cpp#2729
+ *
+ * \li\b apz.fling_friction
+ * Amount of friction applied during flings. This is used in the following
+ * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
+ * for a new sample, v(t0) is the velocity at the previous sample, f is the
+ * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
+ * that has elapsed between the two samples.\n
+ * NOTE: Not currently used in Android fling calculations.
+ *
+ * \li\b apz.fling_min_velocity_threshold
+ * Minimum velocity for a fling to actually kick off. If the user pans and lifts
+ * their finger such that the velocity is smaller than this amount, no fling
+ * is initiated.\n
+ * Units: screen pixels per millisecond
+ *
+ * \li\b apz.fling_stop_on_tap_threshold
+ * When flinging, if the velocity is above this number, then a tap on the
+ * screen will stop the fling without dispatching a tap to content. If the
+ * velocity is below this threshold a tap will also be dispatched.
+ * Note: when modifying this pref be sure to run the APZC gtests as some of
+ * them depend on the value of this pref.\n
+ * Units: screen pixels per millisecond
+ *
+ * \li\b apz.fling_stopped_threshold
+ * When flinging, if the velocity goes below this number, we just stop the
+ * animation completely. This is to prevent asymptotically approaching 0
+ * velocity and rerendering unnecessarily.\n
+ * Units: screen pixels per millisecond.\n
+ * NOTE: Should not be set to anything
+ * other than 0.0 for Android except for tests to disable flings.
+ *
+ * \li\b apz.max_velocity_inches_per_ms
+ * Maximum velocity. Velocity will be capped at this value if a faster fling
+ * occurs. Negative values indicate unlimited velocity.\n
+ * Units: (real-world, i.e. screen) inches per millisecond
+ *
+ * \li\b apz.max_velocity_queue_size
+ * Maximum size of velocity queue. The queue contains last N velocity records.
+ * On touch end we calculate the average velocity in order to compensate
+ * touch/mouse drivers misbehaviour.
+ *
+ * \li\b apz.min_skate_speed
+ * Minimum amount of speed along an axis before we switch to "skate" multipliers
+ * rather than using the "stationary" multipliers.\n
+ * Units: CSS pixels per millisecond
+ *
+ * \li\b apz.overscroll.enabled
+ * Pref that enables overscrolling. If this is disabled, excess scroll that
+ * cannot be handed off is discarded.
+ *
+ * \li\b apz.overscroll.min_pan_distance_ratio
+ * The minimum ratio of the pan distance along one axis to the pan distance
+ * along the other axis needed to initiate overscroll along the first axis
+ * during panning.
+ *
+ * \li\b apz.overscroll.stretch_factor
+ * How much overscrolling can stretch content along an axis.
+ * The maximum stretch along an axis is a factor of (1 + kStretchFactor).
+ * (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
+ * is 1, you can stretch at most by a factor of 2).
+ *
+ * \li\b apz.overscroll.spring_stiffness
+ * The stiffness of the spring used in the physics model for the overscroll
+ * animation.
+ *
+ * \li\b apz.overscroll.spring_friction
+ * The friction of the spring used in the physics model for the overscroll
+ * animation.
+ * Even though a realistic physics model would dictate that this be the same
+ * as \b apz.fling_friction, we allow it to be set to be something different,
+ * because in practice we want flings to skate smoothly (low friction), while
+ * we want the overscroll bounce-back to oscillate few times (high friction).
+ *
+ * \li\b apz.overscroll.stop_distance_threshold
+ * \li\b apz.overscroll.stop_velocity_threshold
+ * Thresholds for stopping the overscroll animation. When both the distance
+ * and the velocity fall below their thresholds, we stop oscillating.\n
+ * Units: screen pixels (for distance)
+ * screen pixels per millisecond (for velocity)
+ *
+ * \li\b apz.paint_skipping.enabled
+ * When APZ is scrolling and sending repaint requests to the main thread, often
+ * the main thread doesn't actually need to do a repaint. This pref allows the
+ * main thread to skip doing those repaints in cases where it doesn't need to.
+ *
+ * \li\b apz.record_checkerboarding
+ * Whether or not to record detailed info on checkerboarding events.
+ *
+ * \li\b apz.test.logging_enabled
+ * Enable logging of APZ test data (see bug 961289).
+ *
+ * \li\b apz.touch_move_tolerance
+ * See the description for apz.touch_start_tolerance below. This is a similar
+ * threshold, except it is used to suppress touchmove events from being delivered
+ * to content for NON-scrollable frames (or more precisely, for APZCs where
+ * ArePointerEventsConsumable returns false).\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.touch_start_tolerance
+ * Constant describing the tolerance in distance we use, multiplied by the
+ * device DPI, before we start panning the screen. This is to prevent us from
+ * accidentally processing taps as touch moves, and from very short/accidental
+ * touches moving the screen. touchmove events are also not delivered to content
+ * within this distance on scrollable frames.\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.velocity_bias
+ * How much to adjust the displayport in the direction of scrolling. This value
+ * is multiplied by the velocity and added to the displayport offset.
+ *
+ * \li\b apz.velocity_relevance_time_ms
+ * When computing a fling velocity from the most recently stored velocity
+ * information, only velocities within the most X milliseconds are used.
+ * This pref controls the value of X.\n
+ * Units: ms
+ *
+ * \li\b apz.x_skate_size_multiplier
+ * \li\b apz.y_skate_size_multiplier
+ * The multiplier we apply to the displayport size if it is skating (current
+ * velocity is above \b apz.min_skate_speed). We prefer to increase the size of
+ * the Y axis because it is more natural in the case that a user is reading a
+ * page page that scrolls up/down. Note that one, both or neither of these may be
+ * used at any instant.\n
+ * In general we want \b apz.[xy]_skate_size_multiplier to be smaller than the corresponding
+ * stationary size multiplier because when panning fast we would like to paint
+ * less and get faster, more predictable paint times. When panning slowly we
+ * can afford to paint more even though it's slower.
+ *
+ * \li\b apz.x_stationary_size_multiplier
+ * \li\b apz.y_stationary_size_multiplier
+ * The multiplier we apply to the displayport size if it is not skating (see
+ * documentation for the skate size multipliers above).
+ *
+ * \li\b apz.x_skate_highmem_adjust
+ * \li\b apz.y_skate_highmem_adjust
+ * On high memory systems, we adjust the displayport during skating
+ * to be larger so we can reduce checkerboarding.
+ *
+ * \li\b apz.zoom_animation_duration_ms
+ * This controls how long the zoom-to-rect animation takes.\n
+ * Units: ms
+ *
+ * \li\b apz.scale_repaint_delay_ms
+ * How long to delay between repaint requests during a scale.
+ * A negative number prevents repaint requests during a scale.\n
+ * Units: ms
+ *
+ */
+
+/**
+ * Computed time function used for sampling frames of a zoom to animation.
+ */
+StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
+
+/**
+ * Computed time function used for curving up velocity when it gets high.
+ */
+StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
+
+/**
+ * The estimated duration of a paint for the purposes of calculating a new
+ * displayport, in milliseconds.
+ */
+static const double kDefaultEstimatedPaintDurationMs = 50;
+
+/**
+ * Returns true if this is a high memory system and we can use
+ * extra memory for a larger displayport to reduce checkerboarding.
+ */
+static bool gIsHighMemSystem = false;
+static bool IsHighMemSystem()
+{
+ return gIsHighMemSystem;
+}
+
+/**
+ * Is aAngle within the given threshold of the horizontal axis?
+ * @param aAngle an angle in radians in the range [0, pi]
+ * @param aThreshold an angle in radians in the range [0, pi/2]
+ */
+static bool IsCloseToHorizontal(float aAngle, float aThreshold)
+{
+ return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
+}
+
+// As above, but for the vertical axis.
+static bool IsCloseToVertical(float aAngle, float aThreshold)
+{
+ return (fabs(aAngle - (M_PI / 2)) < aThreshold);
+}
+
+// Counter used to give each APZC a unique id
+static uint32_t sAsyncPanZoomControllerCount = 0;
+
+TimeStamp
+AsyncPanZoomController::GetFrameTime() const
+{
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
+}
+
+class MOZ_STACK_CLASS StateChangeNotificationBlocker {
+public:
+ explicit StateChangeNotificationBlocker(AsyncPanZoomController* aApzc)
+ : mApzc(aApzc)
+ {
+ ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
+ mInitialState = mApzc->mState;
+ mApzc->mNotificationBlockers++;
+ }
+
+ ~StateChangeNotificationBlocker()
+ {
+ AsyncPanZoomController::PanZoomState newState;
+ {
+ ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
+ mApzc->mNotificationBlockers--;
+ newState = mApzc->mState;
+ }
+ mApzc->DispatchStateChangeNotification(mInitialState, newState);
+ }
+
+private:
+ AsyncPanZoomController* mApzc;
+ AsyncPanZoomController::PanZoomState mInitialState;
+};
+
+class ZoomAnimation: public AsyncPanZoomAnimation {
+public:
+ ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale2D aStartZoom,
+ CSSPoint aEndOffset, CSSToParentLayerScale2D aEndZoom)
+ : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
+ , mStartOffset(aStartOffset)
+ , mStartZoom(aStartZoom)
+ , mEndOffset(aEndOffset)
+ , mEndZoom(aEndZoom)
+ {}
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ mDuration += aDelta;
+ double animPosition = mDuration / mTotalDuration;
+
+ if (animPosition >= 1.0) {
+ aFrameMetrics.SetZoom(mEndZoom);
+ aFrameMetrics.SetScrollOffset(mEndOffset);
+ return false;
+ }
+
+ // Sample the zoom at the current time point. The sampled zoom
+ // will affect the final computed resolution.
+ float sampledPosition =
+ gZoomAnimationFunction->GetValue(animPosition,
+ ComputedTimingFunction::BeforeFlag::Unset);
+
+ // We scale the scrollOffset linearly with sampledPosition, so the zoom
+ // needs to scale inversely to match.
+ aFrameMetrics.SetZoom(CSSToParentLayerScale2D(
+ 1 / (sampledPosition / mEndZoom.xScale + (1 - sampledPosition) / mStartZoom.xScale),
+ 1 / (sampledPosition / mEndZoom.yScale + (1 - sampledPosition) / mStartZoom.yScale)));
+
+ aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
+ mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
+ mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
+ )));
+
+ return true;
+ }
+
+ virtual bool WantsRepaints() override
+ {
+ return false;
+ }
+
+private:
+ TimeDuration mDuration;
+ const TimeDuration mTotalDuration;
+
+ // Old metrics from before we started a zoom animation. This is only valid
+ // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
+ // interpolate between the start and end frames. We only use the
+ // |mViewportScrollOffset| and |mResolution| fields on this.
+ CSSPoint mStartOffset;
+ CSSToParentLayerScale2D mStartZoom;
+
+ // Target metrics for a zoom to animation. This is only valid when we are in
+ // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
+ // |mResolution| fields on this.
+ CSSPoint mEndOffset;
+ CSSToParentLayerScale2D mEndZoom;
+};
+
+
+class SmoothScrollAnimation : public AsyncPanZoomAnimation {
+public:
+ SmoothScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint &aInitialPosition,
+ const nsPoint &aInitialVelocity,
+ const nsPoint& aDestination, double aSpringConstant,
+ double aDampingRatio)
+ : mApzc(aApzc)
+ , mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
+ aSpringConstant, aDampingRatio)
+ , mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
+ aSpringConstant, aDampingRatio)
+ {
+ }
+
+ /**
+ * Advances a smooth scroll simulation based on the time passed in |aDelta|.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the smooth scroll should be advanced by one frame,
+ * or false if the smooth scroll has ended.
+ */
+ bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override {
+ nsPoint oneParentLayerPixel =
+ CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
+ if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
+ mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
+ // Set the scroll offset to the exact destination. If we allow the scroll
+ // offset to end up being a bit off from the destination, we can get
+ // artefacts like "scroll to the next snap point in this direction"
+ // scrolling to the snap point we're already supposed to be at.
+ aFrameMetrics.SetScrollOffset(
+ aFrameMetrics.CalculateScrollRange().ClampPoint(
+ CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
+ mYAxisModel.GetDestination()))));
+ return false;
+ }
+
+ mXAxisModel.Simulate(aDelta);
+ mYAxisModel.Simulate(aDelta);
+
+ CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
+ mYAxisModel.GetPosition()));
+ CSSPoint css_velocity = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetVelocity(),
+ mYAxisModel.GetVelocity()));
+
+ // Convert from points/second to points/ms
+ ParentLayerPoint velocity = ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
+
+ // Keep the velocity updated for the Axis class so that any animations
+ // chained off of the smooth scroll will inherit it.
+ if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) {
+ mApzc.mX.SetVelocity(0);
+ } else {
+ mApzc.mX.SetVelocity(velocity.x);
+ }
+ if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
+ mApzc.mY.SetVelocity(0);
+ } else {
+ mApzc.mY.SetVelocity(velocity.y);
+ }
+ // If we overscroll, hand off to a fling animation that will complete the
+ // spring back.
+ CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
+ ParentLayerPoint displacement = (position - aFrameMetrics.GetScrollOffset()) * zoom;
+
+ ParentLayerPoint overscroll;
+ ParentLayerPoint adjustedOffset;
+ mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
+
+ aFrameMetrics.ScrollBy(adjustedOffset / zoom);
+
+ // The smooth scroll may have caused us to reach the end of our scroll range.
+ // This can happen if either the layout.css.scroll-behavior.damping-ratio
+ // preference is set to less than 1 (underdamped) or if a smooth scroll
+ // inherits velocity from a fling gesture.
+ if (!IsZero(overscroll)) {
+ // Hand off a fling with the remaining momentum to the next APZC in the
+ // overscroll handoff chain.
+
+ // We may have reached the end of the scroll range along one axis but
+ // not the other. In such a case we only want to hand off the relevant
+ // component of the fling.
+ if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ velocity.y = 0;
+ }
+
+ // To hand off the fling, we attempt to find a target APZC and start a new
+ // fling with the same velocity on that APZC. For simplicity, the actual
+ // overscroll of the current sample is discarded rather than being handed
+ // off. The compositor should sample animations sufficiently frequently
+ // that this is not noticeable. The target APZC is chosen by seeing if
+ // there is an APZC further in the handoff chain which is pannable; if
+ // there isn't, we take the new fling ourselves, entering an overscrolled
+ // state.
+ // Note: APZC is holding mMonitor, so directly calling
+ // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
+ // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
+ // called after mMonitor is released.
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint>(&mApzc,
+ &AsyncPanZoomController::HandleSmoothScrollOverscroll,
+ velocity));
+ return false;
+ }
+
+ return true;
+ }
+
+ void SetDestination(const nsPoint& aNewDestination) {
+ mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
+ mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
+ }
+
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(
+ nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
+ }
+
+ SmoothScrollAnimation* AsSmoothScrollAnimation() override {
+ return this;
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+ AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
+};
+
+/*static*/ void
+AsyncPanZoomController::InitializeGlobalState()
+{
+ static bool sInitialized = false;
+ if (sInitialized)
+ return;
+ sInitialized = true;
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gZoomAnimationFunction = new ComputedTimingFunction();
+ gZoomAnimationFunction->Init(
+ nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
+ ClearOnShutdown(&gZoomAnimationFunction);
+ gVelocityCurveFunction = new ComputedTimingFunction();
+ gVelocityCurveFunction->Init(
+ nsTimingFunction(gfxPrefs::APZCurveFunctionX1(),
+ gfxPrefs::APZCurveFunctionY2(),
+ gfxPrefs::APZCurveFunctionX2(),
+ gfxPrefs::APZCurveFunctionY2()));
+ ClearOnShutdown(&gVelocityCurveFunction);
+
+ uint64_t sysmem = PR_GetPhysicalMemorySize();
+ uint64_t threshold = 1LL << 32; // 4 GB in bytes
+ gIsHighMemSystem = sysmem >= threshold;
+}
+
+AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
+ APZCTreeManager* aTreeManager,
+ const RefPtr<InputQueue>& aInputQueue,
+ GeckoContentController* aGeckoContentController,
+ GestureBehavior aGestures)
+ : mLayersId(aLayersId),
+ mGeckoContentController(aGeckoContentController),
+ mRefPtrMonitor("RefPtrMonitor"),
+ // mTreeManager must be initialized before GetFrameTime() is called
+ mTreeManager(aTreeManager),
+ mFrameMetrics(mScrollMetadata.GetMetrics()),
+ mMonitor("AsyncPanZoomController"),
+ mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
+ mX(this),
+ mY(this),
+ mPanDirRestricted(false),
+ mZoomConstraints(false, false,
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1),
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)),
+ mLastSampleTime(GetFrameTime()),
+ mLastCheckerboardReport(GetFrameTime()),
+ mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
+ mState(NOTHING),
+ mNotificationBlockers(0),
+ mInputQueue(aInputQueue),
+ mPinchPaintTimerSet(false),
+ mAPZCId(sAsyncPanZoomControllerCount++),
+ mSharedLock(nullptr),
+ mAsyncTransformAppliedToContent(false),
+ mCheckerboardEventLock("APZCBELock")
+{
+ if (aGestures == USE_GESTURE_DETECTOR) {
+ mGestureEventListener = new GestureEventListener(this);
+ }
+}
+
+AsyncPanZoomController::~AsyncPanZoomController()
+{
+ MOZ_ASSERT(IsDestroyed());
+}
+
+PlatformSpecificStateBase*
+AsyncPanZoomController::GetPlatformSpecificState()
+{
+ if (!mPlatformSpecificState) {
+ mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
+ }
+ return mPlatformSpecificState.get();
+}
+
+already_AddRefed<GeckoContentController>
+AsyncPanZoomController::GetGeckoContentController() const {
+ MonitorAutoLock lock(mRefPtrMonitor);
+ RefPtr<GeckoContentController> controller = mGeckoContentController;
+ return controller.forget();
+}
+
+already_AddRefed<GestureEventListener>
+AsyncPanZoomController::GetGestureEventListener() const {
+ MonitorAutoLock lock(mRefPtrMonitor);
+ RefPtr<GestureEventListener> listener = mGestureEventListener;
+ return listener.forget();
+}
+
+const RefPtr<InputQueue>&
+AsyncPanZoomController::GetInputQueue() const {
+ return mInputQueue;
+}
+
+void
+AsyncPanZoomController::Destroy()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ CancelAnimation(CancelAnimationFlags::ScrollSnap);
+
+ { // scope the lock
+ MonitorAutoLock lock(mRefPtrMonitor);
+ mGeckoContentController = nullptr;
+ mGestureEventListener = nullptr;
+ }
+ mParent = nullptr;
+ mTreeManager = nullptr;
+
+ // Only send the release message if the SharedFrameMetrics has been created.
+ if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
+ Unused << mMetricsSharingController->StopSharingMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
+ }
+
+ { // scope the lock
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mSharedFrameMetricsBuffer = nullptr;
+ delete mSharedLock;
+ mSharedLock = nullptr;
+ }
+}
+
+bool
+AsyncPanZoomController::IsDestroyed() const
+{
+ return mTreeManager == nullptr;
+}
+
+/* static */ScreenCoord
+AsyncPanZoomController::GetTouchStartTolerance()
+{
+ return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
+}
+
+/* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
+{
+ return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
+}
+
+bool
+AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
+ if (aTouchPoints == 0) {
+ // Cant' do anything with zero touch points
+ return false;
+ }
+
+ // This logic is simplified, erring on the side of returning true
+ // if we're not sure. It's safer to pretend that we can consume the
+ // event and then not be able to than vice-versa.
+ // We could probably enhance this logic to determine things like "we're
+ // not pannable, so we can only zoom in, and the zoom is already maxed
+ // out, so we're not zoomable either" but no need for that at this point.
+
+ bool pannable = aBlock->GetOverscrollHandoffChain()->CanBePanned(this);
+ bool zoomable = mZoomConstraints.mAllowZoom;
+
+ pannable &= (aBlock->TouchActionAllowsPanningX() || aBlock->TouchActionAllowsPanningY());
+ zoomable &= (aBlock->TouchActionAllowsPinchZoom());
+
+ // XXX once we fix bug 1031443, consumable should be assigned
+ // pannable || zoomable if aTouchPoints > 1.
+ bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
+ if (!consumable) {
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisStart(AsyncDragMetrics::DragDirection aDir, const PointTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisStart(AsyncDragMetrics::DragDirection aDir, const RectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static IntCoordTyped<Units> GetAxisStart(AsyncDragMetrics::DragDirection aDir, const IntRectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x;
+ } else {
+ return aValue.y;
+ }
+}
+
+template <typename Units>
+static IntCoordTyped<Units> GetAxisEnd(AsyncDragMetrics::DragDirection aDir, const IntRectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.x + aValue.width;
+ } else {
+ return aValue.y + aValue.height;
+ }
+}
+
+template <typename Units>
+static CoordTyped<Units> GetAxisSize(AsyncDragMetrics::DragDirection aDir, const RectTyped<Units>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.width;
+ } else {
+ return aValue.height;
+ }
+}
+
+template <typename FromUnits, typename ToUnits>
+static float GetAxisScale(AsyncDragMetrics::DragDirection aDir, const ScaleFactors2D<FromUnits, ToUnits>& aValue) {
+ if (aDir == AsyncDragMetrics::HORIZONTAL) {
+ return aValue.xScale;
+ } else {
+ return aValue.yScale;
+ }
+}
+
+nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ if (!gfxPrefs::APZDragEnabled()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!GetApzcTreeManager()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ RefPtr<HitTestingTreeNode> node =
+ GetApzcTreeManager()->FindScrollNode(aDragMetrics);
+ if (!node) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethod::ApzScrollbarDrag);
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSPoint scrollFramePoint = aEvent.mLocalOrigin / GetFrameMetrics().GetZoom();
+ // The scrollbar can be transformed with the frame but the pres shell
+ // resolution is only applied to the scroll frame.
+ CSSPoint scrollbarPoint = scrollFramePoint * mFrameMetrics.GetPresShellResolution();
+ CSSRect cssCompositionBound = mFrameMetrics.CalculateCompositedRectInCssPixels();
+
+ CSSCoord mousePosition = GetAxisStart(aDragMetrics.mDirection, scrollbarPoint) -
+ CSSCoord(aDragMetrics.mScrollbarDragOffset) -
+ GetAxisStart(aDragMetrics.mDirection, cssCompositionBound) -
+ CSSCoord(GetAxisStart(aDragMetrics.mDirection, aDragMetrics.mScrollTrack));
+
+ CSSCoord scrollMax = CSSCoord(GetAxisEnd(aDragMetrics.mDirection, aDragMetrics.mScrollTrack));
+ scrollMax -= node->GetScrollSize() /
+ GetAxisScale(aDragMetrics.mDirection, mFrameMetrics.GetZoom()) *
+ mFrameMetrics.GetPresShellResolution();
+
+ float scrollPercent = mousePosition / scrollMax;
+
+ CSSCoord minScrollPosition =
+ GetAxisStart(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect().TopLeft());
+ CSSCoord maxScrollPosition =
+ GetAxisSize(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect()) -
+ GetAxisSize(aDragMetrics.mDirection, cssCompositionBound);
+ CSSCoord scrollPosition = scrollPercent * maxScrollPosition;
+
+ scrollPosition = std::max(scrollPosition, minScrollPosition);
+ scrollPosition = std::min(scrollPosition, maxScrollPosition);
+
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ if (aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) {
+ scrollOffset.x = scrollPosition;
+ } else {
+ scrollOffset.y = scrollPosition;
+ }
+ mFrameMetrics.SetScrollOffset(scrollOffset);
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
+ const ScreenToParentLayerMatrix4x4& aTransformToApzc) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
+ if (!multiTouchInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ if (listener) {
+ rv = listener->HandleInputEvent(multiTouchInput);
+ if (rv == nsEventStatus_eConsumeNoDefault) {
+ return rv;
+ }
+ }
+
+ switch (multiTouchInput.mType) {
+ case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
+ case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
+ default: NS_WARNING("Unhandled multitouch"); break;
+ }
+ break;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
+ if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ switch (panGestureInput.mType) {
+ case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
+ case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
+ case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
+ default: NS_WARNING("Unhandled pan gesture"); break;
+ }
+ break;
+ }
+ case MOUSE_INPUT: {
+ MouseInput mouseInput = aEvent.AsMouseInput();
+ if (!mouseInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ // TODO Need to implement blocks to properly handle this.
+ //rv = HandleDragEvent(mouseInput, dragMetrics);
+ break;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
+ if (!scrollInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = OnScrollWheel(scrollInput);
+ break;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
+ if (!pinchInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = HandleGestureEvent(pinchInput);
+ break;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput tapInput = aEvent.AsTapGestureInput();
+ if (!tapInput.TransformToLocal(aTransformToApzc)) {
+ return rv;
+ }
+
+ rv = HandleGestureEvent(tapInput);
+ break;
+ }
+ default: NS_WARNING("Unhandled input event type"); break;
+ }
+
+ return rv;
+}
+
+nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (aEvent.mInputType) {
+ case PINCHGESTURE_INPUT: {
+ const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
+ switch (pinchGestureInput.mType) {
+ case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
+ case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
+ case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
+ default: NS_WARNING("Unhandled pinch gesture"); break;
+ }
+ break;
+ }
+ case TAPGESTURE_INPUT: {
+ const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
+ switch (tapGestureInput.mType) {
+ case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_SECOND: rv = OnSecondTap(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
+ default: NS_WARNING("Unhandled tap gesture"); break;
+ }
+ break;
+ }
+ default: NS_WARNING("Unhandled input event"); break;
+ }
+
+ return rv;
+}
+
+void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
+{
+ mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-start in state %d\n", this, mState);
+ mPanDirRestricted = false;
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+
+ switch (mState) {
+ case FLING:
+ case ANIMATING_ZOOM:
+ case SMOOTH_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ case WHEEL_SCROLL:
+ case PAN_MOMENTUM:
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
+ MOZ_FALLTHROUGH;
+ case NOTHING: {
+ mX.StartTouch(point.x, aEvent.mTime);
+ mY.StartTouch(point.y, aEvent.mTime);
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eStartTouch,
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
+ }
+ SetState(TOUCHING);
+ break;
+ }
+ case TOUCHING:
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PINCHING:
+ NS_WARNING("Received impossible touch in OnTouchStart");
+ break;
+ default:
+ NS_WARNING("Unhandled case in OnTouchStart");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-move in state %d\n", this, mState);
+ switch (mState) {
+ case FLING:
+ case SMOOTH_SCROLL:
+ case NOTHING:
+ case ANIMATING_ZOOM:
+ // May happen if the user double-taps and drags without lifting after the
+ // second tap. Ignore the move if this happens.
+ return nsEventStatus_eIgnore;
+
+ case TOUCHING: {
+ ScreenCoord panThreshold = GetTouchStartTolerance();
+ UpdateWithTouchAtDevicePoint(aEvent);
+
+ if (PanDistance() < panThreshold) {
+ return nsEventStatus_eIgnore;
+ }
+
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (gfxPrefs::TouchActionEnabled() && GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+ // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
+ // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
+ // status immediately to trigger cancel event further. It should happen independent of
+ // the parent type (whether it is scrolling or not).
+ StartPanning(aEvent);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ return StartPanning(aEvent);
+ }
+
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PAN_MOMENTUM:
+ TrackTouch(aEvent);
+ return nsEventStatus_eConsumeNoDefault;
+
+ case PINCHING:
+ // The scale gesture listener should have handled this.
+ NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
+ return nsEventStatus_eIgnore;
+
+ case WHEEL_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
+ // as touch blocks that begin in an overscrolled state cancel the
+ // animation. The same is true for wheel scroll animations.
+ NS_WARNING("Received impossible touch in OnTouchMove");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-end in state %d\n", this, mState);
+
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->SetScrollingRootContent(false);
+ }
+
+ OnTouchEndOrCancel();
+
+ // In case no touch behavior triggered previously we can avoid sending
+ // scroll events or requesting content repaint. This condition is added
+ // to make tests consistent - in case touch-action is NONE (and therefore
+ // no pans/zooms can be performed) we expected neither scroll or repaint
+ // events.
+ if (mState != NOTHING) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ }
+
+ switch (mState) {
+ case FLING:
+ // Should never happen.
+ NS_WARNING("Received impossible touch end in OnTouchEnd.");
+ MOZ_FALLTHROUGH;
+ case ANIMATING_ZOOM:
+ case SMOOTH_SCROLL:
+ case NOTHING:
+ // May happen if the user double-taps and drags without lifting after the
+ // second tap. Ignore if this happens.
+ return nsEventStatus_eIgnore;
+
+ case TOUCHING:
+ // We may have some velocity stored on the axis from move events
+ // that were not big enough to trigger scrolling. Clear that out.
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ APZC_LOG("%p still has %u touch points active\n", this,
+ GetCurrentTouchBlock()->GetActiveTouchCount());
+ // In cases where the user is panning, then taps the second finger without
+ // entering a pinch, we will arrive here when the second finger is lifted.
+ // However the first finger is still down so we want to remain in state
+ // TOUCHING.
+ if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
+ // It's possible we may be overscrolled if the user tapped during a
+ // previous overscroll pan. Make sure to snap back in this situation.
+ // An ancestor APZC could be overscrolled instead of this APZC, so
+ // walk the handoff chain as well.
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
+ // SnapBackOverscrolledApzc() will put any APZC it causes to snap back
+ // into the OVERSCROLL_ANIMATION state. If that's not us, since we're
+ // done TOUCHING enter the NOTHING state.
+ if (mState != OVERSCROLL_ANIMATION) {
+ SetState(NOTHING);
+ }
+ }
+ return nsEventStatus_eIgnore;
+
+ case PANNING:
+ case PANNING_LOCKED_X:
+ case PANNING_LOCKED_Y:
+ case PAN_MOMENTUM:
+ {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
+ mX.EndTouch(aEvent.mTime);
+ mY.EndTouch(aEvent.mTime);
+ ParentLayerPoint flingVelocity = GetVelocityVector();
+ // Clear our velocities; if DispatchFling() gives the fling to us,
+ // the fling velocity gets *added* to our existing velocity in
+ // AcceptFling().
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ // Clear our state so that we don't stay in the PANNING state
+ // if DispatchFling() gives the fling to somone else. However,
+ // don't send the state change notification until we've determined
+ // what our final state is to avoid notification churn.
+ StateChangeNotificationBlocker blocker(this);
+ SetState(NOTHING);
+
+ APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
+ flingVelocity.Length().value, gfxPrefs::APZFlingMinVelocityThreshold());
+
+ if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ // Make a local copy of the tree manager pointer and check that it's not
+ // null before calling DispatchFling(). This is necessary because Destroy(),
+ // which nulls out mTreeManager, could be called concurrently.
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ FlingHandoffState handoffState{flingVelocity,
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
+ false /* not handoff */,
+ GetCurrentTouchBlock()->GetScrolledApzc()};
+ treeManagerLocal->DispatchFling(this, handoffState);
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ case PINCHING:
+ SetState(NOTHING);
+ // Scale gesture listener should have handled this.
+ NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
+ return nsEventStatus_eIgnore;
+
+ case WHEEL_SCROLL:
+ case OVERSCROLL_ANIMATION:
+ // Should not receive a touch-end in the OVERSCROLL_ANIMATION state
+ // as touch blocks that begin in an overscrolled state cancel the
+ // animation. The same is true for WHEEL_SCROLL.
+ NS_WARNING("Received impossible touch in OnTouchEnd");
+ break;
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
+ APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
+ OnTouchEndOrCancel();
+ CancelAnimationAndGestureState();
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
+
+ mPinchPaintTimerSet = false;
+ // Note that there may not be a touch block at this point, if we received the
+ // PinchGestureEvent directly from widget code without any touch events.
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // For platforms that don't support APZ zooming, dispatch a message to the
+ // content controller, it may want to do something else with this gesture.
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
+ }
+ }
+
+ SetState(PINCHING);
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale in state %d\n", this, mState);
+
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (mState != PINCHING) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(),
+ ViewAs<LayoutDevicePixel>(aEvent.mCurrentSpan - aEvent.mPreviousSpan,
+ PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
+ aEvent.modifiers);
+ }
+ }
+
+ // Only the root APZC is zoomable, and the root APZC is not allowed to have
+ // different x and y scales. If it did, the calculations in this function
+ // would have to be adjusted (as e.g. it would no longer be valid to take
+ // the minimum or maximum of the ratios of the widths and heights of the
+ // page rect and the composition bounds).
+ MOZ_ASSERT(mFrameMetrics.IsRootContent());
+ MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom().ToScaleFactor();
+ ParentLayerPoint focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
+ CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
+
+ ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
+ mLastZoomFocus = focusPoint;
+ // If displacing by the change in focus point will take us off page bounds,
+ // then reduce the displacement such that it doesn't.
+ focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
+ focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
+ ScrollBy(focusChange / userZoom);
+
+ // If the span is zero or close to it, we don't want to process this zoom
+ // change because we're going to get wonky numbers for the spanRatio. So
+ // let's bail out here. Note that we do this after the focus-change-scroll
+ // above, so that if we have a pinch with zero span but changing focus,
+ // such as generated by some Synaptics touchpads on Windows, we still
+ // scroll properly.
+ float prevSpan = aEvent.mPreviousSpan;
+ if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
+ // We might have done a nonzero ScrollBy above, so update metrics and
+ // repaint/recomposite
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
+
+ // When we zoom in with focus, we can zoom too much towards the boundaries
+ // that we actually go over them. These are the needed displacements along
+ // either axis such that we don't overscroll the boundaries when zooming.
+ CSSPoint neededDisplacement;
+
+ CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
+ CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
+ realMinZoom.scale = std::max(realMinZoom.scale,
+ mFrameMetrics.GetCompositionBounds().width / mFrameMetrics.GetScrollableRect().width);
+ realMinZoom.scale = std::max(realMinZoom.scale,
+ mFrameMetrics.GetCompositionBounds().height / mFrameMetrics.GetScrollableRect().height);
+ if (realMaxZoom < realMinZoom) {
+ realMaxZoom = realMinZoom;
+ }
+
+ bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
+ (spanRatio < 1.0 && userZoom > realMinZoom);
+
+ if (!mZoomConstraints.mAllowZoom) {
+ doScale = false;
+ }
+
+ if (doScale) {
+ spanRatio = clamped(spanRatio,
+ realMinZoom.scale / userZoom.scale,
+ realMaxZoom.scale / userZoom.scale);
+
+ // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
+ // up above we clamped it.
+ neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
+ neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
+
+ ScaleWithFocus(spanRatio, cssFocusPoint);
+
+ if (neededDisplacement != CSSPoint()) {
+ ScrollBy(neededDisplacement);
+ }
+
+ // We don't want to redraw on every scale, so throttle it.
+ if (!mPinchPaintTimerSet) {
+ const int delay = gfxPrefs::APZScaleRepaintDelay();
+ if (delay >= 0) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ mPinchPaintTimerSet = true;
+ controller->PostDelayedTask(
+ NewRunnableMethod(this,
+ &AsyncPanZoomController::DoDelayedRequestContentRepaint),
+ delay);
+ }
+ }
+ }
+
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // We did a ScrollBy call above even if we didn't do a scale, so we
+ // should composite for that.
+ ScheduleComposite();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
+ APZC_LOG("%p got a scale-end in state %d\n", this, mState);
+
+ mPinchPaintTimerSet = false;
+
+ if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!gfxPrefs::APZAllowZooming()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
+ }
+ }
+
+ SetState(NOTHING);
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ScheduleComposite();
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // Non-negative focus point would indicate that one finger is still down
+ if (aEvent.mLocalFocusPoint.x != -1 && aEvent.mLocalFocusPoint.y != -1) {
+ mPanDirRestricted = false;
+ mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
+ SetState(TOUCHING);
+ } else {
+ // Otherwise, handle the fingers being lifted.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // We can get into a situation where we are overscrolled at the end of a
+ // pinch if we go into overscroll with a two-finger pan, and then turn
+ // that into a pinch by increasing the span sufficiently. In such a case,
+ // there is no snap-back animation to get us out of overscroll, so we need
+ // to get out of it somehow.
+ // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
+ // further up in the handoff chain rather than on the current APZC, so
+ // we need to clear overscroll along the entire handoff chain.
+ if (HasReadyTouchBlock()) {
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
+ } else {
+ ClearOverscroll();
+ }
+ // Along with clearing the overscroll, we also want to snap to the nearest
+ // snap point as appropriate.
+ ScrollSnap();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+bool
+AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut)
+{
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ ScreenToScreenMatrix4x4 transformScreenToGecko =
+ treeManagerLocal->GetScreenToApzcTransform(this)
+ * treeManagerLocal->GetApzcToGeckoTransform(this);
+
+ Maybe<ScreenIntPoint> layoutPoint = UntransformBy(
+ transformScreenToGecko, aPoint);
+ if (!layoutPoint) {
+ return false;
+ }
+
+ *aOut = LayoutDevicePoint(ViewAs<LayoutDevicePixel>(*layoutPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ return true;
+ }
+ return false;
+}
+
+static bool
+AllowsScrollingMoreThanOnePage(double aMultiplier)
+{
+ const int32_t kMinAllowPageScroll =
+ EventStateManager::MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
+ return Abs(aMultiplier) >= kMinAllowPageScroll;
+}
+
+ParentLayerPoint
+AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) const
+{
+ ParentLayerSize scrollAmount;
+ ParentLayerSize pageScrollSize;
+
+ {
+ // Grab the lock to access the frame metrics.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ LayoutDeviceIntSize scrollAmountLD = mScrollMetadata.GetLineScrollAmount();
+ LayoutDeviceIntSize pageScrollSizeLD = mScrollMetadata.GetPageScrollAmount();
+ scrollAmount = scrollAmountLD /
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
+ pageScrollSize = pageScrollSizeLD /
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
+ }
+
+ ParentLayerPoint delta;
+ switch (aEvent.mDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_LINE: {
+ delta.x = aEvent.mDeltaX * scrollAmount.width;
+ delta.y = aEvent.mDeltaY * scrollAmount.height;
+ break;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PAGE: {
+ delta.x = aEvent.mDeltaX * pageScrollSize.width;
+ delta.y = aEvent.mDeltaY * pageScrollSize.height;
+ break;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PIXEL: {
+ delta = ToParentLayerCoordinates(ScreenPoint(aEvent.mDeltaX, aEvent.mDeltaY), aEvent.mOrigin);
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
+ }
+
+ // Apply user-set multipliers.
+ delta.x *= aEvent.mUserDeltaMultiplierX;
+ delta.y *= aEvent.mUserDeltaMultiplierY;
+
+ // For the conditions under which we allow system scroll overrides, see
+ // EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction
+ // and WheelTransaction::OverrideSystemScrollSpeed. Note that we do *not*
+ // restrict this to the root content, see bug 1217715 for discussion on this.
+ if (gfxPrefs::MouseWheelHasRootScrollDeltaOverride() &&
+ !aEvent.IsCustomizedByUserPrefs() &&
+ aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
+ aEvent.mAllowToOverrideSystemScrollSpeed) {
+ delta.x = WidgetWheelEvent::ComputeOverriddenDelta(delta.x, false);
+ delta.y = WidgetWheelEvent::ComputeOverriddenDelta(delta.y, true);
+ }
+
+ // If this is a line scroll, and this event was part of a scroll series, then
+ // it might need extra acceleration. See WheelHandlingHelper.cpp.
+ if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
+ aEvent.mScrollSeriesNumber > 0)
+ {
+ int32_t start = gfxPrefs::MouseWheelAccelerationStart();
+ if (start >= 0 && aEvent.mScrollSeriesNumber >= uint32_t(start)) {
+ int32_t factor = gfxPrefs::MouseWheelAccelerationFactor();
+ if (factor > 0) {
+ delta.x = ComputeAcceleratedWheelDelta(delta.x, aEvent.mScrollSeriesNumber, factor);
+ delta.y = ComputeAcceleratedWheelDelta(delta.y, aEvent.mScrollSeriesNumber, factor);
+ }
+ }
+ }
+
+ // We shouldn't scroll more than one page at once except when the
+ // user preference is large.
+ if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierX) &&
+ Abs(delta.x) > pageScrollSize.width) {
+ delta.x = (delta.x >= 0)
+ ? pageScrollSize.width
+ : -pageScrollSize.width;
+ }
+ if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierY) &&
+ Abs(delta.y) > pageScrollSize.height) {
+ delta.y = (delta.y >= 0)
+ ? pageScrollSize.height
+ : -pageScrollSize.height;
+ }
+
+ return delta;
+}
+
+// Return whether or not the underlying layer can be scrolled on either axis.
+bool
+AsyncPanZoomController::CanScroll(const InputData& aEvent) const
+{
+ ParentLayerPoint delta;
+ if (aEvent.mInputType == SCROLLWHEEL_INPUT) {
+ delta = GetScrollWheelDelta(aEvent.AsScrollWheelInput());
+ } else if (aEvent.mInputType == PANGESTURE_INPUT) {
+ const PanGestureInput& panInput = aEvent.AsPanGestureInput();
+ delta = ToParentLayerCoordinates(panInput.UserMultipliedPanDisplacement(), panInput.mPanStartPoint);
+ }
+ if (!delta.x && !delta.y) {
+ return false;
+ }
+
+ return CanScrollWithWheel(delta);
+}
+
+bool
+AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mX.CanScroll(aDelta.x)) {
+ return true;
+ }
+ if (mY.CanScroll(aDelta.y) && mScrollMetadata.AllowVerticalScrollWithWheel()) {
+ return true;
+ }
+ return false;
+}
+
+bool
+AsyncPanZoomController::CanScroll(Layer::ScrollDirection aDirection) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ switch (aDirection) {
+ case Layer::HORIZONTAL: return mX.CanScroll();
+ case Layer::VERTICAL: return mY.CanScroll();
+ default: MOZ_ASSERT(false); return false;
+ }
+}
+
+bool
+AsyncPanZoomController::AllowScrollHandoffInCurrentBlock() const
+{
+ bool result = mInputQueue->AllowScrollHandoff();
+ if (!gfxPrefs::APZAllowImmediateHandoff()) {
+ if (InputBlockState* currentBlock = GetCurrentInputBlock()) {
+ // Do not allow handoff beyond the first APZC to scroll.
+ if (currentBlock->GetScrolledApzc() == this) {
+ result = false;
+ }
+ }
+ }
+ return result;
+}
+
+void AsyncPanZoomController::DoDelayedRequestContentRepaint()
+{
+ if (!IsDestroyed() && mPinchPaintTimerSet) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ }
+ mPinchPaintTimerSet = false;
+}
+
+static ScrollInputMethod
+ScrollInputMethodForWheelDeltaType(ScrollWheelInput::ScrollDeltaType aDeltaType)
+{
+ switch (aDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_LINE: {
+ return ScrollInputMethod::ApzWheelLine;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PAGE: {
+ return ScrollInputMethod::ApzWheelPage;
+ }
+ case ScrollWheelInput::SCROLLDELTA_PIXEL: {
+ return ScrollInputMethod::ApzWheelPixel;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
+ return ScrollInputMethod::ApzWheelLine;
+ }
+}
+
+nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
+{
+ ParentLayerPoint delta = GetScrollWheelDelta(aEvent);
+ APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str());
+
+ if ((delta.x || delta.y) && !CanScrollWithWheel(delta)) {
+ // We can't scroll this apz anymore, so we simply drop the event.
+ if (mInputQueue->GetActiveWheelTransaction() &&
+ gfxPrefs::MouseScrollTestingEnabled()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyMozMouseScrollEvent(
+ mFrameMetrics.GetScrollId(),
+ NS_LITERAL_STRING("MozMouseScrollFailed"));
+ }
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (delta.x == 0 && delta.y == 0) {
+ // Avoid spurious state changes and unnecessary work
+ return nsEventStatus_eIgnore;
+ }
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethodForWheelDeltaType(aEvent.mDeltaType));
+
+
+ switch (aEvent.mScrollMode) {
+ case ScrollWheelInput::SCROLLMODE_INSTANT: {
+
+ // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
+ // next snap point. Check for this, and adjust the delta to take into
+ // account the snap point.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
+
+ ScreenPoint distance = ToScreenCoordinates(
+ ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
+
+ CancelAnimation();
+
+ MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
+ OverscrollHandoffState handoffState(
+ *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(),
+ distance,
+ ScrollSource::Wheel);
+ ParentLayerPoint startPoint = aEvent.mLocalOrigin;
+ ParentLayerPoint endPoint = aEvent.mLocalOrigin - delta;
+ CallDispatchScroll(startPoint, endPoint, handoffState);
+
+ SetState(NOTHING);
+
+ // The calls above handle their own locking; moreover,
+ // ToScreenCoordinates() and CallDispatchScroll() can grab the tree lock.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+
+ break;
+ }
+
+ case ScrollWheelInput::SCROLLMODE_SMOOTH: {
+ // The lock must be held across the entire update operation, so the
+ // compositor doesn't end the animation before we get a chance to
+ // update it.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // Perform scroll snapping if appropriate.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ // If we're already in a wheel scroll or smooth scroll animation,
+ // the delta is applied to its destination, not to the current
+ // scroll position. Take this into account when finding a snap point.
+ if (mState == WHEEL_SCROLL) {
+ startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
+ } else if (mState == SMOOTH_SCROLL) {
+ startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
+ }
+ if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
+ // If we're scroll snapping, use a smooth scroll animation to get
+ // the desired physics. Note that SmoothScrollTo() will re-use an
+ // existing smooth scroll animation if there is one.
+ APZC_LOG("%p wheel scrolling to snap point %s\n", this, Stringify(startPosition).c_str());
+ SmoothScrollTo(startPosition);
+ break;
+ }
+
+ // Otherwise, use a wheel scroll animation, also reusing one if possible.
+ if (mState != WHEEL_SCROLL) {
+ CancelAnimation();
+ SetState(WHEEL_SCROLL);
+
+ nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
+ StartAnimation(new WheelScrollAnimation(
+ *this, initialPosition, aEvent.mDeltaType));
+ }
+
+ nsPoint deltaInAppUnits =
+ CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
+ // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
+ // appunits/second
+ nsPoint velocity =
+ CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
+
+ WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
+ animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
+ break;
+ }
+
+ case ScrollWheelInput::SCROLLMODE_SENTINEL: {
+ MOZ_ASSERT_UNREACHABLE("Invalid ScrollMode.");
+ break;
+ }
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+void
+AsyncPanZoomController::NotifyMozMouseScrollEvent(const nsString& aString) const
+{
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+
+ controller->NotifyMozMouseScrollEvent(mFrameMetrics.GetScrollId(), aString);
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
+
+ mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ GetCurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
+
+ mX.CancelGesture();
+ mY.CancelGesture();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+
+nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
+ mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
+
+ if (GetAxisLockMode() == FREE) {
+ SetState(PANNING);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ float dx = aEvent.mPanDisplacement.x, dy = aEvent.mPanDisplacement.y;
+
+ if (dx || dy) {
+ double angle = atan2(dy, dx); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+ HandlePanning(angle);
+ } else {
+ SetState(PANNING);
+ }
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, true);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
+ APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ if (!aFingersOnTouchpad) {
+ // When a SMOOTH_SCROLL scroll is being processed on a frame, mouse
+ // wheel and trackpad momentum scroll position updates will not cancel the
+ // SMOOTH_SCROLL scroll animations, enabling scripts that depend on
+ // them to be responsive without forcing the user to wait for the momentum
+ // scrolling to completely stop.
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ if (mState == NOTHING) {
+ // This event block was interrupted by something else. If the user's fingers
+ // are still on on the touchpad we want to resume scrolling, otherwise we
+ // ignore the rest of the scroll gesture.
+ if (!aFingersOnTouchpad) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ // Resume / restart the pan.
+ // PanBegin will call back into this function with mState == PANNING.
+ return OnPanBegin(aEvent);
+ }
+
+ // Note that there is a multiplier that applies onto the "physical" pan
+ // displacement (how much the user's fingers moved) that produces the "logical"
+ // pan displacement (how much the page should move). For some of the code
+ // below it makes more sense to use the physical displacement rather than
+ // the logical displacement, and vice-versa.
+ ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement;
+ ParentLayerPoint logicalPanDisplacement = aEvent.UserMultipliedLocalPanDisplacement();
+
+ // We need to update the axis velocity in order to get a useful display port
+ // size and position. We need to do so even if this is a momentum pan (i.e.
+ // aFingersOnTouchpad == false); in that case the "with touch" part is not
+ // really appropriate, so we may want to rethink this at some point.
+ mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.x, logicalPanDisplacement.x, aEvent.mTime);
+ mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.y, logicalPanDisplacement.y, aEvent.mTime);
+
+ HandlePanningUpdate(physicalPanDisplacement);
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethod::ApzPanGesture);
+
+ ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y));
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ OverscrollHandoffState handoffState(
+ *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(),
+ panDistance,
+ ScrollSource::Wheel);
+
+ // Create fake "touch" positions that will result in the desired scroll motion.
+ // Note that the pan displacement describes the change in scroll position:
+ // positive displacement values mean that the scroll position increases.
+ // However, an increase in scroll position means that the scrolled contents
+ // are moved to the left / upwards. Since our simulated "touches" determine
+ // the motion of the scrolled contents, not of the scroll position, they need
+ // to move in the opposite direction of the pan displacement.
+ ParentLayerPoint startPoint = aEvent.mLocalPanStartPoint;
+ ParentLayerPoint endPoint = aEvent.mLocalPanStartPoint - logicalPanDisplacement;
+ CallDispatchScroll(startPoint, endPoint, handoffState);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-end in state %d\n", this, mState);
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, true);
+
+ mX.EndTouch(aEvent.mTime);
+ mY.EndTouch(aEvent.mTime);
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't enlarge the display port unnecessarily.
+ MOZ_ASSERT(GetCurrentPanGestureBlock());
+ RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
+ GetCurrentPanGestureBlock()->GetOverscrollHandoffChain();
+ if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL)) {
+ mX.SetVelocity(0);
+ }
+ if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL)) {
+ mY.SetVelocity(0);
+ }
+
+ SetState(NOTHING);
+ RequestContentRepaint();
+
+ if (!aEvent.mFollowedByMomentum) {
+ ScrollSnap();
+ }
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
+
+ if (mState == SMOOTH_SCROLL) {
+ // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
+ CancelAnimation();
+ }
+
+ SetState(PAN_MOMENTUM);
+ ScrollSnapToDestination();
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, false);
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnPanMomentumEnd(const PanGestureInput& aEvent) {
+ APZC_LOG("%p got a pan-momentumend in state %d\n", this, mState);
+
+ // Call into OnPan in order to process any delta included in this event.
+ OnPan(aEvent, false);
+
+ // We need to reset the velocity to zero. We don't really have a "touch"
+ // here because the touch has already ended long before the momentum
+ // animation started, but I guess it doesn't really matter for now.
+ mX.CancelGesture();
+ mY.CancelGesture();
+ SetState(NOTHING);
+
+ RequestContentRepaint();
+
+ return nsEventStatus_eConsumeNoDefault;
+}
+
+nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a long-press in state %d\n", this, mState);
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
+ TouchBlockState* touch = GetCurrentTouchBlock();
+ if (!touch) {
+ APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ if (touch->IsDuringFastFling()) {
+ APZC_LOG("%p dropping long-press because of fast fling\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
+ controller->HandleTap(TapType::eLongTap, geckoScreenPoint, aEvent.modifiers, GetGuid(), blockId);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eLongTapUp, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::GenerateSingleTap(TapType aType,
+ const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
+ TouchBlockState* touch = GetCurrentTouchBlock();
+ // |touch| may be null in the case where this function is
+ // invoked by GestureEventListener on a timeout. In that case we already
+ // verified that the single tap is allowed so we let it through.
+ // XXX there is a bug here that in such a case the touch block that
+ // generated this tap will not get its mSingleTapOccurred flag set.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1256344#c6
+ if (touch) {
+ if (touch->IsDuringFastFling()) {
+ APZC_LOG("%p dropping single-tap because it was during a fast-fling\n", this);
+ return nsEventStatus_eIgnore;
+ }
+ touch->SetSingleTapOccurred();
+ }
+ // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
+ // calling controller->HandleTap directly might mean that content receives
+ // the single tap message before the corresponding touch-up. To avoid that we
+ // schedule the singletap message to run on the next spin of the event loop.
+ // See bug 965381 for the issue this was causing.
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<TapType, LayoutDevicePoint, mozilla::Modifiers,
+ ScrollableLayerGuid, uint64_t>(controller,
+ &GeckoContentController::HandleTap,
+ aType, geckoScreenPoint,
+ aModifiers, GetGuid(),
+ touch ? touch->GetBlockId() : 0);
+
+ controller->PostDelayedTask(runnable.forget(), 0);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ return nsEventStatus_eIgnore;
+}
+
+void AsyncPanZoomController::OnTouchEndOrCancel() {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eEndTouch, GetCurrentTouchBlock()->SingleTapOccurred());
+ }
+}
+
+nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
+ // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
+ // sending event to content
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (!(mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
+ return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a double-tap in state %d\n", this, mState);
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
+ LayoutDevicePoint geckoScreenPoint;
+ if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
+ controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
+ aEvent.modifiers, GetGuid(), GetCurrentTouchBlock()->GetBlockId());
+ }
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus AsyncPanZoomController::OnSecondTap(const TapGestureInput& aEvent)
+{
+ APZC_LOG("%p got a second-tap in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eSecondTap, aEvent.mPoint, aEvent.modifiers);
+}
+
+nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
+ APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
+ // XXX: Implement this.
+ return nsEventStatus_eIgnore;
+}
+
+
+ScreenToParentLayerMatrix4x4 AsyncPanZoomController::GetTransformToThis() const {
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ return treeManagerLocal->GetScreenToApzcTransform(this);
+ }
+ return ScreenToParentLayerMatrix4x4();
+}
+
+ScreenPoint AsyncPanZoomController::ToScreenCoordinates(const ParentLayerPoint& aVector,
+ const ParentLayerPoint& aAnchor) const {
+ return TransformVector(GetTransformToThis().Inverse(), aVector, aAnchor);
+}
+
+// TODO: figure out a good way to check the w-coordinate is positive and return the result
+ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPoint& aVector,
+ const ScreenPoint& aAnchor) const {
+ return TransformVector(GetTransformToThis(), aVector, aAnchor);
+}
+
+bool AsyncPanZoomController::Contains(const ScreenIntPoint& aPoint) const
+{
+ ScreenToParentLayerMatrix4x4 transformToThis = GetTransformToThis();
+ Maybe<ParentLayerIntPoint> point = UntransformBy(transformToThis, aPoint);
+ if (!point) {
+ return false;
+ }
+
+ ParentLayerIntRect cb;
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ GetFrameMetrics().GetCompositionBounds().ToIntRect(&cb);
+ }
+ return cb.Contains(*point);
+}
+
+ScreenCoord AsyncPanZoomController::PanDistance() const {
+ ParentLayerPoint panVector;
+ ParentLayerPoint panStart;
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ panVector = ParentLayerPoint(mX.PanDistance(), mY.PanDistance());
+ panStart = PanStart();
+ }
+ return ToScreenCoordinates(panVector, panStart).Length();
+}
+
+ParentLayerPoint AsyncPanZoomController::PanStart() const {
+ return ParentLayerPoint(mX.PanStart(), mY.PanStart());
+}
+
+const ParentLayerPoint AsyncPanZoomController::GetVelocityVector() const {
+ return ParentLayerPoint(mX.GetVelocity(), mY.GetVelocity());
+}
+
+void AsyncPanZoomController::SetVelocityVector(const ParentLayerPoint& aVelocityVector) {
+ mX.SetVelocity(aVelocityVector.x);
+ mY.SetVelocity(aVelocityVector.y);
+}
+
+void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
+ // Handling of cross sliding will need to be added in this method after touch-action released
+ // enabled by default.
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ if (GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
+ if (mX.CanScrollNow() && mY.CanScrollNow()) {
+ if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mY.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_X);
+ } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mX.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_Y);
+ } else {
+ SetState(PANNING);
+ }
+ } else if (mX.CanScrollNow() || mY.CanScrollNow()) {
+ SetState(PANNING);
+ } else {
+ SetState(NOTHING);
+ }
+ } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningX()) {
+ // Using bigger angle for panning to keep behavior consistent
+ // with IE.
+ if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
+ mY.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_X);
+ mPanDirRestricted = true;
+ } else {
+ // Don't treat these touches as pan/zoom movements since 'touch-action' value
+ // requires it.
+ SetState(NOTHING);
+ }
+ } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningY()) {
+ if (IsCloseToVertical(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
+ mX.SetAxisLocked(true);
+ SetState(PANNING_LOCKED_Y);
+ mPanDirRestricted = true;
+ } else {
+ SetState(NOTHING);
+ }
+ } else {
+ SetState(NOTHING);
+ }
+ if (!IsInPanningState()) {
+ // If we didn't enter a panning state because touch-action disallowed it,
+ // make sure to clear any leftover velocity from the pre-threshold
+ // touchmoves.
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ }
+}
+
+void AsyncPanZoomController::HandlePanning(double aAngle) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ MOZ_ASSERT(GetCurrentInputBlock());
+ RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
+ GetCurrentInputBlock()->GetOverscrollHandoffChain();
+ bool canScrollHorizontal = !mX.IsAxisLocked() &&
+ overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL);
+ bool canScrollVertical = !mY.IsAxisLocked() &&
+ overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
+
+ if (!canScrollHorizontal || !canScrollVertical) {
+ SetState(PANNING);
+ } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mY.SetAxisLocked(true);
+ if (canScrollHorizontal) {
+ SetState(PANNING_LOCKED_X);
+ }
+ } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
+ mX.SetAxisLocked(true);
+ if (canScrollVertical) {
+ SetState(PANNING_LOCKED_Y);
+ }
+ } else {
+ SetState(PANNING);
+ }
+}
+
+void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
+ // If we're axis-locked, check if the user is trying to break the lock
+ if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
+
+ double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+
+ float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
+
+ if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
+ if (mState == PANNING_LOCKED_X) {
+ if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
+ mY.SetAxisLocked(false);
+ SetState(PANNING);
+ }
+ } else if (mState == PANNING_LOCKED_Y) {
+ if (!IsCloseToVertical(angle, gfxPrefs::APZAxisBreakoutAngle())) {
+ mX.SetAxisLocked(false);
+ SetState(PANNING);
+ }
+ }
+ }
+ }
+}
+
+nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+ float dx = mX.PanDistance(point.x);
+ float dy = mY.PanDistance(point.y);
+
+ double angle = atan2(dy, dx); // range [-pi, pi]
+ angle = fabs(angle); // range [0, pi]
+
+ if (gfxPrefs::TouchActionEnabled()) {
+ HandlePanningWithTouchAction(angle);
+ } else {
+ if (GetAxisLockMode() == FREE) {
+ SetState(PANNING);
+ } else {
+ HandlePanning(angle);
+ }
+ }
+
+ if (IsInPanningState()) {
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ controller->NotifyAPZStateChange(GetGuid(), APZStateChange::eStartPanning);
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ // Don't consume an event that didn't trigger a panning.
+ return nsEventStatus_eIgnore;
+}
+
+void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
+ ParentLayerPoint point = GetFirstTouchPoint(aEvent);
+ mX.UpdateWithTouchAtDevicePoint(point.x, 0, aEvent.mTime);
+ mY.UpdateWithTouchAtDevicePoint(point.y, 0, aEvent.mTime);
+}
+
+bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState) {
+
+ // "start - end" rather than "end - start" because e.g. moving your finger
+ // down (*positive* direction along y axis) causes the vertical scroll offset
+ // to *decrease* as the page follows your finger.
+ ParentLayerPoint displacement = aStartPoint - aEndPoint;
+
+ ParentLayerPoint overscroll; // will be used outside monitor block
+
+ // If the direction of panning is reversed within the same input block,
+ // a later event in the block could potentially scroll an APZC earlier
+ // in the handoff chain, than an earlier event in the block (because
+ // the earlier APZC was scrolled to its extent in the original direction).
+ // We want to disallow this.
+ bool scrollThisApzc = false;
+ if (InputBlockState* block = GetCurrentInputBlock()) {
+ scrollThisApzc = !block->GetScrolledApzc() || block->IsDownchainOfScrolledApzc(this);
+ }
+
+ if (scrollThisApzc) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerPoint adjustedDisplacement;
+ bool forceVerticalOverscroll =
+ (aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
+ !mScrollMetadata.AllowVerticalScrollWithWheel());
+ bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y,
+ forceVerticalOverscroll);
+ bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
+
+ if (xChanged || yChanged) {
+ ScheduleComposite();
+ }
+
+ if (!IsZero(adjustedDisplacement)) {
+ ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
+ if (CancelableBlockState* block = GetCurrentInputBlock()) {
+ if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) {
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->SetScrollingRootContent(IsRootContent());
+ }
+ }
+ block->SetScrolledApzc(this);
+ }
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+ }
+
+ // Adjust the start point to reflect the consumed portion of the scroll.
+ aStartPoint = aEndPoint + overscroll;
+ } else {
+ overscroll = displacement;
+ }
+
+ // If we consumed the entire displacement as a normal scroll, great.
+ if (IsZero(overscroll)) {
+ return true;
+ }
+
+ if (AllowScrollHandoffInCurrentBlock()) {
+ // If there is overscroll, first try to hand it off to an APZC later
+ // in the handoff chain to consume (either as a normal scroll or as
+ // overscroll).
+ // Note: "+ overscroll" rather than "- overscroll" because "overscroll"
+ // is what's left of "displacement", and "displacement" is "start - end".
+ ++aOverscrollHandoffState.mChainIndex;
+ CallDispatchScroll(aStartPoint, aEndPoint, aOverscrollHandoffState);
+
+ overscroll = aStartPoint - aEndPoint;
+ if (IsZero(overscroll)) {
+ return true;
+ }
+ }
+
+ // If there is no APZC later in the handoff chain that accepted the
+ // overscroll, try to accept it ourselves. We only accept it if we
+ // are pannable.
+ APZC_LOG("%p taking overscroll during panning\n", this);
+ OverscrollForPanning(overscroll, aOverscrollHandoffState.mPanDistance);
+ aStartPoint = aEndPoint + overscroll;
+
+ return IsZero(overscroll);
+}
+
+void AsyncPanZoomController::OverscrollForPanning(ParentLayerPoint& aOverscroll,
+ const ScreenPoint& aPanDistance) {
+ // Only allow entering overscroll along an axis if the pan distance along
+ // that axis is greater than the pan distance along the other axis by a
+ // configurable factor. If we are already overscrolled, don't check this.
+ if (!IsOverscrolled()) {
+ if (aPanDistance.x < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.y) {
+ aOverscroll.x = 0;
+ }
+ if (aPanDistance.y < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.x) {
+ aOverscroll.y = 0;
+ }
+ }
+
+ OverscrollBy(aOverscroll);
+}
+
+void AsyncPanZoomController::OverscrollBy(ParentLayerPoint& aOverscroll) {
+ if (!gfxPrefs::APZOverscrollEnabled()) {
+ return;
+ }
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ // Do not go into overscroll in a direction in which we have no room to
+ // scroll to begin with.
+ bool xCanScroll = mX.CanScroll();
+ bool yCanScroll = mY.CanScroll();
+ bool xConsumed = FuzzyEqualsAdditive(aOverscroll.x, 0.0f, COORDINATE_EPSILON);
+ bool yConsumed = FuzzyEqualsAdditive(aOverscroll.y, 0.0f, COORDINATE_EPSILON);
+
+ bool shouldOverscrollX = xCanScroll && !xConsumed;
+ bool shouldOverscrollY = yCanScroll && !yConsumed;
+
+ mOverscrollEffect->ConsumeOverscroll(aOverscroll, shouldOverscrollX, shouldOverscrollY);
+}
+
+RefPtr<const OverscrollHandoffChain> AsyncPanZoomController::BuildOverscrollHandoffChain() {
+ if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+ return treeManagerLocal->BuildOverscrollHandoffChain(this);
+ }
+
+ // This APZC IsDestroyed(). To avoid callers having to special-case this
+ // scenario, just build a 1-element chain containing ourselves.
+ OverscrollHandoffChain* result = new OverscrollHandoffChain;
+ result->Add(this);
+ return result;
+}
+
+void AsyncPanZoomController::AcceptFling(FlingHandoffState& aHandoffState) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // We may have a pre-existing velocity for whatever reason (for example,
+ // a previously handed off fling). We don't want to clobber that.
+ APZC_LOG("%p accepting fling with velocity %s\n", this,
+ Stringify(aHandoffState.mVelocity).c_str());
+ if (mX.CanScroll()) {
+ mX.SetVelocity(mX.GetVelocity() + aHandoffState.mVelocity.x);
+ aHandoffState.mVelocity.x = 0;
+ }
+ if (mY.CanScroll()) {
+ mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y);
+ aHandoffState.mVelocity.y = 0;
+ }
+
+ // If there's a scroll snap point near the predicted fling destination,
+ // scroll there using a smooth scroll animation. Otherwise, start a
+ // fling animation.
+ ScrollSnapToDestination();
+ if (mState != SMOOTH_SCROLL) {
+ SetState(FLING);
+ FlingAnimation *fling = new FlingAnimation(*this,
+ GetPlatformSpecificState(),
+ aHandoffState.mChain,
+ aHandoffState.mIsHandoff,
+ aHandoffState.mScrolledApzc);
+ StartAnimation(fling);
+ }
+}
+
+bool AsyncPanZoomController::AttemptFling(FlingHandoffState& aHandoffState) {
+ // If we are pannable, take over the fling ourselves.
+ if (IsPannable()) {
+ AcceptFling(aHandoffState);
+ return true;
+ }
+
+ return false;
+}
+
+void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc) {
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ if (treeManagerLocal) {
+ FlingHandoffState handoffState{aVelocity,
+ aOverscrollHandoffChain,
+ true /* handoff */,
+ aScrolledApzc};
+ treeManagerLocal->DispatchFling(this, handoffState);
+ if (!IsZero(handoffState.mVelocity) && IsPannable() && gfxPrefs::APZOverscrollEnabled()) {
+ mOverscrollEffect->HandleFlingOverscroll(handoffState.mVelocity);
+ }
+ }
+}
+
+void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity) {
+ // We must call BuildOverscrollHandoffChain from this deferred callback
+ // function in order to avoid a deadlock when acquiring the tree lock.
+ HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr);
+}
+
+void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) {
+ if (mState == SMOOTH_SCROLL && mAnimation) {
+ APZC_LOG("%p updating destination on existing animation\n", this);
+ RefPtr<SmoothScrollAnimation> animation(
+ static_cast<SmoothScrollAnimation*>(mAnimation.get()));
+ animation->SetDestination(CSSPoint::ToAppUnits(aDestination));
+ } else {
+ CancelAnimation();
+ SetState(SMOOTH_SCROLL);
+ nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
+ // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
+ // appunits/second
+ nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
+ mY.GetVelocity())) * 1000.0f;
+ nsPoint destination = CSSPoint::ToAppUnits(aDestination);
+
+ StartAnimation(new SmoothScrollAnimation(*this,
+ initialPosition, initialVelocity,
+ destination,
+ gfxPrefs::ScrollBehaviorSpringConstant(),
+ gfxPrefs::ScrollBehaviorDampingRatio()));
+ }
+}
+
+void AsyncPanZoomController::StartOverscrollAnimation(const ParentLayerPoint& aVelocity) {
+ SetState(OVERSCROLL_ANIMATION);
+ StartAnimation(new OverscrollAnimation(*this, aVelocity));
+}
+
+void AsyncPanZoomController::CallDispatchScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState) {
+ // Make a local copy of the tree manager pointer and check if it's not
+ // null before calling DispatchScroll(). This is necessary because
+ // Destroy(), which nulls out mTreeManager, could be called concurrently.
+ APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
+ if (!treeManagerLocal) {
+ return;
+ }
+ treeManagerLocal->DispatchScroll(this,
+ aStartPoint, aEndPoint,
+ aOverscrollHandoffState);
+}
+
+void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
+ ParentLayerPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
+ ParentLayerPoint touchPoint = GetFirstTouchPoint(aEvent);
+
+ ScreenPoint panDistance = ToScreenCoordinates(
+ ParentLayerPoint(mX.PanDistance(touchPoint.x),
+ mY.PanDistance(touchPoint.y)),
+ PanStart());
+ HandlePanningUpdate(panDistance);
+
+ UpdateWithTouchAtDevicePoint(aEvent);
+
+ if (prevTouchPoint != touchPoint) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
+ (uint32_t) ScrollInputMethod::ApzTouch);
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ OverscrollHandoffState handoffState(
+ *GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
+ panDistance,
+ ScrollSource::Touch);
+ CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
+ }
+}
+
+ParentLayerPoint AsyncPanZoomController::GetFirstTouchPoint(const MultiTouchInput& aEvent) {
+ return ((SingleTouchData&)aEvent.mTouches[0]).mLocalScreenPoint;
+}
+
+void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mAnimation = aAnimation;
+ mLastSampleTime = GetFrameTime();
+ ScheduleComposite();
+}
+
+void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
+ SetState(NOTHING);
+ mAnimation = nullptr;
+ // Since there is no animation in progress now the axes should
+ // have no velocity either. If we are dropping the velocity from a non-zero
+ // value we should trigger a repaint as the displayport margins are dependent
+ // on the velocity and the last repaint request might not have good margins
+ // any more.
+ bool repaint = !IsZero(GetVelocityVector());
+ mX.SetVelocity(0);
+ mY.SetVelocity(0);
+ mX.SetAxisLocked(false);
+ mY.SetAxisLocked(false);
+ // Setting the state to nothing and cancelling the animation can
+ // preempt normal mechanisms for relieving overscroll, so we need to clear
+ // overscroll here.
+ if (!(aFlags & ExcludeOverscroll) && IsOverscrolled()) {
+ ClearOverscroll();
+ repaint = true;
+ }
+ // Similar to relieving overscroll, we also need to snap to any snap points
+ // if appropriate.
+ if (aFlags & CancelAnimationFlags::ScrollSnap) {
+ ScrollSnap();
+ }
+ if (repaint) {
+ RequestContentRepaint();
+ ScheduleComposite();
+ UpdateSharedCompositorFrameMetrics();
+ }
+}
+
+void AsyncPanZoomController::ClearOverscroll() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mX.ClearOverscroll();
+ mY.ClearOverscroll();
+}
+
+void AsyncPanZoomController::SetCompositorController(CompositorController* aCompositorController)
+{
+ mCompositorController = aCompositorController;
+}
+
+void AsyncPanZoomController::SetMetricsSharingController(MetricsSharingController* aMetricsSharingController)
+{
+ mMetricsSharingController = aMetricsSharingController;
+}
+
+void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSPoint adjustment =
+ ViewAs<ParentLayerPixel>(aShift, PixelCastJustification::ScreenIsParentLayerForRoot)
+ / mFrameMetrics.GetZoom();
+ APZC_LOG("%p adjusting scroll position by %s for surface shift\n",
+ this, Stringify(adjustment).c_str());
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ scrollOffset.y = mY.ClampOriginToScrollableRect(scrollOffset.y + adjustment.y);
+ scrollOffset.x = mX.ClampOriginToScrollableRect(scrollOffset.x + adjustment.x);
+ mFrameMetrics.SetScrollOffset(scrollOffset);
+ ScheduleCompositeAndMaybeRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
+ mFrameMetrics.ScrollBy(aOffset);
+}
+
+void AsyncPanZoomController::ScaleWithFocus(float aScale,
+ const CSSPoint& aFocus) {
+ mFrameMetrics.ZoomBy(aScale);
+ // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
+ // at the same position on the screen before and after the change in zoom. The below code
+ // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
+ // in-depth explanation of how.
+ mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
+}
+
+/**
+ * Enlarges the displayport along both axes based on the velocity.
+ */
+static CSSSize
+CalculateDisplayPortSize(const CSSSize& aCompositionSize,
+ const CSSPoint& aVelocity)
+{
+ bool xIsStationarySpeed = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed();
+ bool yIsStationarySpeed = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed();
+ float xMultiplier = xIsStationarySpeed
+ ? gfxPrefs::APZXStationarySizeMultiplier()
+ : gfxPrefs::APZXSkateSizeMultiplier();
+ float yMultiplier = yIsStationarySpeed
+ ? gfxPrefs::APZYStationarySizeMultiplier()
+ : gfxPrefs::APZYSkateSizeMultiplier();
+
+ if (IsHighMemSystem() && !xIsStationarySpeed) {
+ xMultiplier += gfxPrefs::APZXSkateHighMemAdjust();
+ }
+
+ if (IsHighMemSystem() && !yIsStationarySpeed) {
+ yMultiplier += gfxPrefs::APZYSkateHighMemAdjust();
+ }
+
+ return aCompositionSize * CSSSize(xMultiplier, yMultiplier);
+}
+
+/**
+ * Ensures that the displayport is at least as large as the visible area
+ * inflated by the danger zone. If this is not the case then the
+ * "AboutToCheckerboard" function in TiledContentClient.cpp will return true
+ * even in the stable state.
+ */
+static CSSSize
+ExpandDisplayPortToDangerZone(const CSSSize& aDisplayPortSize,
+ const FrameMetrics& aFrameMetrics)
+{
+ CSSSize dangerZone(0.0f, 0.0f);
+ if (aFrameMetrics.LayersPixelsPerCSSPixel().xScale != 0 &&
+ aFrameMetrics.LayersPixelsPerCSSPixel().yScale != 0) {
+ dangerZone = LayerSize(
+ gfxPrefs::APZDangerZoneX(),
+ gfxPrefs::APZDangerZoneY()) / aFrameMetrics.LayersPixelsPerCSSPixel();
+ }
+ const CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
+
+ const float xSize = std::max(aDisplayPortSize.width,
+ compositionSize.width + (2 * dangerZone.width));
+
+ const float ySize = std::max(aDisplayPortSize.height,
+ compositionSize.height + (2 * dangerZone.height));
+
+ return CSSSize(xSize, ySize);
+}
+
+/**
+ * Attempts to redistribute any area in the displayport that would get clipped
+ * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
+ * other axis, while maintaining total displayport area.
+ */
+static void
+RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
+ const CSSRect& aScrollableRect)
+{
+ // As aDisplayPortSize.height * aDisplayPortSize.width does not change,
+ // we are just scaling by the ratio and its inverse.
+ if (aDisplayPortSize.height > aScrollableRect.height) {
+ aDisplayPortSize.width *= (aDisplayPortSize.height / aScrollableRect.height);
+ aDisplayPortSize.height = aScrollableRect.height;
+ } else if (aDisplayPortSize.width > aScrollableRect.width) {
+ aDisplayPortSize.height *= (aDisplayPortSize.width / aScrollableRect.width);
+ aDisplayPortSize.width = aScrollableRect.width;
+ }
+}
+
+/* static */
+const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ if (aFrameMetrics.IsScrollInfoLayer()) {
+ // Don't compute margins. Since we can't asynchronously scroll this frame,
+ // we don't want to paint anything more than the composition bounds.
+ return ScreenMargin();
+ }
+
+ CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
+ CSSPoint velocity;
+ if (aFrameMetrics.GetZoom() != CSSToParentLayerScale2D(0, 0)) {
+ velocity = aVelocity / aFrameMetrics.GetZoom(); // avoid division by zero
+ }
+ CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
+
+ // Calculate the displayport size based on how fast we're moving along each axis.
+ CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
+
+ displayPortSize = ExpandDisplayPortToDangerZone(displayPortSize, aFrameMetrics);
+
+ if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
+ RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
+ }
+
+ // We calculate a "displayport" here which is relative to the scroll offset.
+ // Note that the scroll offset we have here in the APZ code may not be the
+ // same as the base rect that gets used on the layout side when the displayport
+ // margins are actually applied, so it is important to only consider the
+ // displayport as margins relative to a scroll offset rather than relative to
+ // something more unchanging like the scrollable rect origin.
+
+ // Center the displayport based on its expansion over the composition size.
+ CSSRect displayPort((compositionSize.width - displayPortSize.width) / 2.0f,
+ (compositionSize.height - displayPortSize.height) / 2.0f,
+ displayPortSize.width, displayPortSize.height);
+
+ // Offset the displayport, depending on how fast we're moving and the
+ // estimated time it takes to paint, to try to minimise checkerboarding.
+ float paintFactor = kDefaultEstimatedPaintDurationMs;
+ displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
+
+ APZC_LOG_FM(aFrameMetrics,
+ "Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
+ displayPort.x, displayPort.y, displayPort.width, displayPort.height,
+ ToString(aVelocity).c_str(), paintFactor);
+
+ CSSMargin cssMargins;
+ cssMargins.left = -displayPort.x;
+ cssMargins.top = -displayPort.y;
+ cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
+ cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
+
+ return cssMargins * aFrameMetrics.DisplayportPixelsPerCSSPixel();
+}
+
+void AsyncPanZoomController::ScheduleComposite() {
+ if (mCompositorController) {
+ mCompositorController->ScheduleRenderOnCompositorThread();
+ }
+}
+
+void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
+ ScheduleComposite();
+ RequestContentRepaint();
+}
+
+void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
+ APZC_LOG("%p flushing repaint for new input block\n", this);
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ RequestContentRepaint();
+ UpdateSharedCompositorFrameMetrics();
+}
+
+bool AsyncPanZoomController::SnapBackIfOverscrolled() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ // It's possible that we're already in the middle of an overscroll
+ // animation - if so, don't start a new one.
+ if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) {
+ APZC_LOG("%p is overscrolled, starting snap-back\n", this);
+ StartOverscrollAnimation(ParentLayerPoint(0, 0));
+ return true;
+ }
+ // If we don't kick off an overscroll animation, we still need to ask the
+ // main thread to snap to any nearby snap points, assuming we haven't already
+ // done so when we started this fling
+ if (mState != FLING) {
+ ScrollSnap();
+ }
+ return false;
+}
+
+bool AsyncPanZoomController::IsFlingingFast() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mState == FLING &&
+ GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+ APZC_LOG("%p is moving fast\n", this);
+ return true;
+ }
+ return false;
+}
+
+bool AsyncPanZoomController::IsPannable() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mX.CanScroll() || mY.CanScroll();
+}
+
+int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ return listener ? listener->GetLastTouchIdentifier() : -1;
+}
+
+void AsyncPanZoomController::RequestContentRepaint(bool aUserAction) {
+ // Reinvoke this method on the repaint thread if it's not there already. It's
+ // important to do this before the call to CalculatePendingDisplayPort, so
+ // that CalculatePendingDisplayPort uses the most recent available version of
+ // mFrameMetrics, just before the paint request is dispatched to content.
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ if (!controller->IsRepaintThread()) {
+ // use the local variable to resolve the function overload.
+ auto func = static_cast<void (AsyncPanZoomController::*)(bool)>
+ (&AsyncPanZoomController::RequestContentRepaint);
+ controller->DispatchToRepaintThread(NewRunnableMethod<bool>(this, func, aUserAction));
+ return;
+ }
+
+ MOZ_ASSERT(controller->IsRepaintThread());
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ParentLayerPoint velocity = GetVelocityVector();
+ mFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(mFrameMetrics, velocity));
+ mFrameMetrics.SetUseDisplayPortMargins(true);
+ mFrameMetrics.SetPaintRequestTime(TimeStamp::Now());
+ mFrameMetrics.SetRepaintDrivenByUserAction(aUserAction);
+ RequestContentRepaint(mFrameMetrics, velocity);
+}
+
+/*static*/ CSSRect
+GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
+{
+ // This computation is based on what happens in CalculatePendingDisplayPort. If that
+ // changes then this might need to change too
+ CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
+ aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
+ baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
+ return baseRect;
+}
+
+void
+AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity)
+{
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ MOZ_ASSERT(controller->IsRepaintThread());
+
+ // If we're trying to paint what we already think is painted, discard this
+ // request since it's a pointless paint.
+ ScreenMargin marginDelta = (mLastPaintRequestMetrics.GetDisplayPortMargins()
+ - aFrameMetrics.GetDisplayPortMargins());
+ if (fabsf(marginDelta.left) < EPSILON &&
+ fabsf(marginDelta.top) < EPSILON &&
+ fabsf(marginDelta.right) < EPSILON &&
+ fabsf(marginDelta.bottom) < EPSILON &&
+ fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
+ aFrameMetrics.GetScrollOffset().x) < EPSILON &&
+ fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
+ aFrameMetrics.GetScrollOffset().y) < EPSILON &&
+ aFrameMetrics.GetPresShellResolution() == mLastPaintRequestMetrics.GetPresShellResolution() &&
+ aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
+ fabsf(aFrameMetrics.GetViewport().width -
+ mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
+ fabsf(aFrameMetrics.GetViewport().height -
+ mLastPaintRequestMetrics.GetViewport().height) < EPSILON &&
+ aFrameMetrics.GetScrollGeneration() ==
+ mLastPaintRequestMetrics.GetScrollGeneration() &&
+ aFrameMetrics.GetScrollUpdateType() ==
+ mLastPaintRequestMetrics.GetScrollUpdateType()) {
+ return;
+ }
+
+ APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
+ std::stringstream info;
+ info << " velocity " << aVelocity;
+ std::string str = info.str();
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::RequestedDisplayPort, GetDisplayPortRect(aFrameMetrics),
+ str);
+ }
+ }
+
+ MOZ_ASSERT(aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eNone ||
+ aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eUserAction);
+ controller->RequestContentRepaint(aFrameMetrics);
+ mExpectedGeckoMetrics = aFrameMetrics;
+ mLastPaintRequestMetrics = aFrameMetrics;
+}
+
+bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
+ nsTArray<RefPtr<Runnable>>* aOutDeferredTasks)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // This function may get called multiple with the same sample time, because
+ // there may be multiple layers with this APZC, and each layer invokes this
+ // function during composition. However we only want to do one animation step
+ // per composition so we need to deduplicate these calls first.
+ if (mLastSampleTime == aSampleTime) {
+ return false;
+ }
+ TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime;
+ mLastSampleTime = aSampleTime;
+
+ if (mAnimation) {
+ bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta);
+ bool wantsRepaints = mAnimation->WantsRepaints();
+ *aOutDeferredTasks = mAnimation->TakeDeferredTasks();
+ if (!continueAnimation) {
+ mAnimation = nullptr;
+ SetState(NOTHING);
+ }
+ // Request a repaint at the end of the animation in case something such as a
+ // call to NotifyLayersUpdated was invoked during the animation and Gecko's
+ // current state is some intermediate point of the animation.
+ if (!continueAnimation || wantsRepaints) {
+ RequestContentRepaint();
+ }
+ UpdateSharedCompositorFrameMetrics();
+ return true;
+ }
+ return false;
+}
+
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetOverscrollTransform(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return AsyncTransformComponentMatrix();
+ }
+
+ if (!IsOverscrolled()) {
+ return AsyncTransformComponentMatrix();
+ }
+
+ // The overscroll effect is a uniform stretch along the overscrolled axis,
+ // with the edge of the content where we have reached the end of the
+ // scrollable area pinned into place.
+
+ // The kStretchFactor parameter determines how much overscroll can stretch the
+ // content.
+ const float kStretchFactor = gfxPrefs::APZOverscrollStretchFactor();
+
+ // Compute the amount of the stretch along each axis. The stretch is
+ // proportional to the amount by which we are overscrolled along that axis.
+ ParentLayerSize compositionSize(mX.GetCompositionLength(), mY.GetCompositionLength());
+ float scaleX = 1 + kStretchFactor * fabsf(mX.GetOverscroll()) / mX.GetCompositionLength();
+ float scaleY = 1 + kStretchFactor * fabsf(mY.GetOverscroll()) / mY.GetCompositionLength();
+
+ // The scale is applied relative to the origin of the composition bounds, i.e.
+ // it keeps the top-left corner of the content in place. This is fine if we
+ // are overscrolling at the top or on the left, but if we are overscrolling
+ // at the bottom or on the right, we want the bottom or right edge of the
+ // content to stay in place instead, so we add a translation to compensate.
+ ParentLayerPoint translation;
+ bool overscrolledOnRight = mX.GetOverscroll() > 0;
+ if (overscrolledOnRight) {
+ ParentLayerCoord overscrolledCompositionWidth = scaleX * compositionSize.width;
+ ParentLayerCoord extraCompositionWidth = overscrolledCompositionWidth - compositionSize.width;
+ translation.x = -extraCompositionWidth;
+ }
+ bool overscrolledAtBottom = mY.GetOverscroll() > 0;
+ if (overscrolledAtBottom) {
+ ParentLayerCoord overscrolledCompositionHeight = scaleY * compositionSize.height;
+ ParentLayerCoord extraCompositionHeight = overscrolledCompositionHeight - compositionSize.height;
+ translation.y = -extraCompositionHeight;
+ }
+
+ // Combine the transformations into a matrix.
+ return AsyncTransformComponentMatrix::Scaling(scaleX, scaleY, 1)
+ .PostTranslate(translation.x, translation.y, 0);
+}
+
+bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // Don't send any state-change notifications until the end of the function,
+ // because we may go through some intermediate states while we finish
+ // animations and start new ones.
+ StateChangeNotificationBlocker blocker(this);
+
+ // The eventual return value of this function. The compositor needs to know
+ // whether or not to advance by a frame as soon as it can. For example, if a
+ // fling is happening, it has to keep compositing so that the animation is
+ // smooth. If an animation frame is requested, it is the compositor's
+ // responsibility to schedule a composite.
+ mAsyncTransformAppliedToContent = false;
+ bool requestAnimationFrame = false;
+ nsTArray<RefPtr<Runnable>> deferredTasks;
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ requestAnimationFrame = UpdateAnimation(aSampleTime, &deferredTasks);
+
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent) {
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::UserVisible,
+ CSSRect(mFrameMetrics.GetScrollOffset(),
+ mFrameMetrics.CalculateCompositedSizeInCssPixels()));
+ }
+ }
+ }
+
+ // Execute any deferred tasks queued up by mAnimation's Sample() (called by
+ // UpdateAnimation()). This needs to be done after the monitor is released
+ // since the tasks are allowed to call APZCTreeManager methods which can grab
+ // the tree lock.
+ for (uint32_t i = 0; i < deferredTasks.Length(); ++i) {
+ deferredTasks[i]->Run();
+ deferredTasks[i] = nullptr;
+ }
+
+ // One of the deferred tasks may have started a new animation. In this case,
+ // we want to ask the compositor to schedule a new composite.
+ requestAnimationFrame |= (mAnimation != nullptr);
+
+ return requestAnimationFrame;
+}
+
+ParentLayerPoint
+AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
+ }
+
+ return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset)
+ * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
+}
+
+AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncTransform(AsyncMode aMode) const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (aMode == RESPECT_FORCE_DISABLE && mScrollMetadata.IsApzForceDisabled()) {
+ return AsyncTransform();
+ }
+
+ CSSPoint lastPaintScrollOffset;
+ if (mLastContentPaintMetrics.IsScrollable()) {
+ lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
+ }
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
+ mTestAsyncScrollOffset;
+
+ // If checkerboarding has been disallowed, clamp the scroll position to stay
+ // within rendered content.
+ if (!gfxPrefs::APZAllowCheckerboarding() &&
+ !mLastContentPaintMetrics.GetDisplayPort().IsEmpty()) {
+ CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
+ CSSPoint maxScrollOffset = lastPaintScrollOffset +
+ CSSPoint(mLastContentPaintMetrics.GetDisplayPort().XMost() - compositedSize.width,
+ mLastContentPaintMetrics.GetDisplayPort().YMost() - compositedSize.height);
+ CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.GetDisplayPort().TopLeft();
+
+ if (minScrollOffset.x < maxScrollOffset.x) {
+ currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
+ }
+ if (minScrollOffset.y < maxScrollOffset.y) {
+ currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
+ }
+ }
+
+ ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
+ * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
+
+ return AsyncTransform(
+ LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale),
+ -translation);
+}
+
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const
+{
+ return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
+ * GetOverscrollTransform(aMode);
+}
+
+Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ LayerPoint scrollChange =
+ (mLastContentPaintMetrics.GetScrollOffset() - mExpectedGeckoMetrics.GetScrollOffset())
+ * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
+ * mLastContentPaintMetrics.GetCumulativeResolution();
+
+ // We're interested in the async zoom change. Factor out the content scale
+ // that may change when dragging the window to a monitor with a different
+ // content scale.
+ LayoutDeviceToParentLayerScale2D lastContentZoom =
+ mLastContentPaintMetrics.GetZoom() / mLastContentPaintMetrics.GetDevPixelsPerCSSPixel();
+ LayoutDeviceToParentLayerScale2D lastDispatchedZoom =
+ mExpectedGeckoMetrics.GetZoom() / mExpectedGeckoMetrics.GetDevPixelsPerCSSPixel();
+ gfxSize zoomChange = lastContentZoom / lastDispatchedZoom;
+
+ return Matrix4x4::Translation(scrollChange.x, scrollChange.y, 0).
+ PostScale(zoomChange.width, zoomChange.height, 1);
+}
+
+uint32_t
+AsyncPanZoomController::GetCheckerboardMagnitude() const
+{
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
+ CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
+ CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
+
+ CSSIntRegion checkerboard;
+ // Round so as to minimize checkerboarding; if we're only showing fractional
+ // pixels of checkerboarding it's not really worth counting
+ checkerboard.Sub(RoundedIn(visible), RoundedOut(painted));
+ return checkerboard.Area();
+}
+
+void
+AsyncPanZoomController::ReportCheckerboard(const TimeStamp& aSampleTime)
+{
+ if (mLastCheckerboardReport == aSampleTime) {
+ // This function will get called multiple times for each APZC on a single
+ // composite (once for each layer it is attached to). Only report the
+ // checkerboard once per composite though.
+ return;
+ }
+ mLastCheckerboardReport = aSampleTime;
+
+ bool recordTrace = gfxPrefs::APZRecordCheckerboarding();
+ bool forTelemetry = Telemetry::CanRecordExtended();
+ uint32_t magnitude = GetCheckerboardMagnitude();
+
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (!mCheckerboardEvent && (recordTrace || forTelemetry)) {
+ mCheckerboardEvent = MakeUnique<CheckerboardEvent>(recordTrace);
+ }
+ mPotentialCheckerboardTracker.InTransform(IsTransformingState(mState));
+ if (magnitude) {
+ mPotentialCheckerboardTracker.CheckerboardSeen();
+ }
+ UpdateCheckerboardEvent(lock, magnitude);
+}
+
+void
+AsyncPanZoomController::UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+ uint32_t aMagnitude)
+{
+ if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(aMagnitude)) {
+ // This checkerboard event is done. Report some metrics to telemetry.
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_SEVERITY,
+ mCheckerboardEvent->GetSeverity());
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_PEAK,
+ mCheckerboardEvent->GetPeak());
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_DURATION,
+ (uint32_t)mCheckerboardEvent->GetDuration().ToMilliseconds());
+
+ mPotentialCheckerboardTracker.CheckerboardDone();
+
+ if (gfxPrefs::APZRecordCheckerboarding()) {
+ // if the pref is enabled, also send it to the storage class. it may be
+ // chosen for public display on about:checkerboard, the hall of fame for
+ // checkerboard events.
+ uint32_t severity = mCheckerboardEvent->GetSeverity();
+ std::string log = mCheckerboardEvent->GetLog();
+ CheckerboardEventStorage::Report(severity, log);
+ }
+ mCheckerboardEvent = nullptr;
+ }
+}
+
+void
+AsyncPanZoomController::FlushActiveCheckerboardReport()
+{
+ MutexAutoLock lock(mCheckerboardEventLock);
+ // Pretend like we got a frame with 0 pixels checkerboarded. This will
+ // terminate the checkerboard event and flush it out
+ UpdateCheckerboardEvent(lock, 0);
+}
+
+bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ if (!gfxPrefs::APZAllowCheckerboarding() || mScrollMetadata.IsApzForceDisabled()) {
+ return false;
+ }
+
+ CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
+ CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
+ painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1))); // fuzz for rounding error
+ CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
+ if (painted.Contains(visible)) {
+ return false;
+ }
+ APZC_LOG_FM(mFrameMetrics, "%p is currently checkerboarding (painted %s visble %s)",
+ this, Stringify(painted).c_str(), Stringify(visible).c_str());
+ return true;
+}
+
+void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata,
+ bool aIsFirstPaint,
+ bool aThisLayerTreeUpdated)
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ bool isDefault = mScrollMetadata.IsDefault();
+
+ const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics();
+
+ if ((aScrollMetadata == mLastContentPaintMetadata) && !isDefault) {
+ // No new information here, skip it.
+ APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
+ return;
+ }
+
+ // If the mFrameMetrics scroll offset is different from the last scroll offset
+ // that the main-thread sent us, then we know that the user has been doing
+ // something that triggers a scroll. This check is the APZ equivalent of the
+ // check on the main-thread at
+ // https://hg.mozilla.org/mozilla-central/file/97a52326b06a/layout/generic/nsGfxScrollFrame.cpp#l4050
+ // There is code below (the use site of userScrolled) that prevents a restored-
+ // scroll-position update from overwriting a user scroll, again equivalent to
+ // how the main thread code does the same thing.
+ CSSPoint lastScrollOffset = mLastContentPaintMetadata.GetMetrics().GetScrollOffset();
+ bool userScrolled =
+ !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().x, lastScrollOffset.x) ||
+ !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().y, lastScrollOffset.y);
+
+ if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) {
+ mLastContentPaintMetadata = aScrollMetadata;
+ }
+
+ mScrollMetadata.SetScrollParentId(aScrollMetadata.GetScrollParentId());
+ APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
+ this, aIsFirstPaint, aThisLayerTreeUpdated);
+
+ { // scope lock
+ MutexAutoLock lock(mCheckerboardEventLock);
+ if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
+ std::string str;
+ if (aThisLayerTreeUpdated) {
+ if (!aLayerMetrics.GetPaintRequestTime().IsNull()) {
+ // Note that we might get the paint request time as non-null, but with
+ // aThisLayerTreeUpdated false. That can happen if we get a layer transaction
+ // from a different process right after we get the layer transaction with
+ // aThisLayerTreeUpdated == true. In this case we want to ignore the
+ // paint request time because it was already dumped in the previous layer
+ // transaction.
+ TimeDuration paintTime = TimeStamp::Now() - aLayerMetrics.GetPaintRequestTime();
+ std::stringstream info;
+ info << " painttime " << paintTime.ToMilliseconds();
+ str = info.str();
+ } else {
+ // This might be indicative of a wasted paint particularly if it happens
+ // during a checkerboard event.
+ str = " (this layertree updated)";
+ }
+ }
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::Page, aLayerMetrics.GetScrollableRect());
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::PaintedDisplayPort,
+ aLayerMetrics.GetDisplayPort() + aLayerMetrics.GetScrollOffset(),
+ str);
+ if (!aLayerMetrics.GetCriticalDisplayPort().IsEmpty()) {
+ mCheckerboardEvent->UpdateRendertraceProperty(
+ CheckerboardEvent::PaintedCriticalDisplayPort,
+ aLayerMetrics.GetCriticalDisplayPort() + aLayerMetrics.GetScrollOffset());
+ }
+ }
+ }
+
+ bool needContentRepaint = false;
+ bool viewportUpdated = false;
+ if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().width, mFrameMetrics.GetCompositionBounds().width) &&
+ FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().height, mFrameMetrics.GetCompositionBounds().height)) {
+ // Remote content has sync'd up to the composition geometry
+ // change, so we can accept the viewport it's calculated.
+ if (mFrameMetrics.GetViewport().width != aLayerMetrics.GetViewport().width ||
+ mFrameMetrics.GetViewport().height != aLayerMetrics.GetViewport().height) {
+ needContentRepaint = true;
+ viewportUpdated = true;
+ }
+ mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
+ }
+
+ // If the layers update was not triggered by our own repaint request, then
+ // we want to take the new scroll offset. Check the scroll generation as well
+ // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
+ // update message.
+ bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
+ && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
+
+ if (scrollOffsetUpdated && userScrolled &&
+ aLayerMetrics.GetScrollUpdateType() == FrameMetrics::ScrollOffsetUpdateType::eRestore) {
+ APZC_LOG("%p dropping scroll update of type eRestore because of user scroll\n", this);
+ scrollOffsetUpdated = false;
+ }
+
+ bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
+ && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
+
+ // TODO if we're in a drag and scrollOffsetUpdated is set then we want to
+ // ignore it
+
+ if ((aIsFirstPaint && aThisLayerTreeUpdated) || isDefault) {
+ // Initialize our internal state to something sane when the content
+ // that was just painted is something we knew nothing about previously
+ CancelAnimation();
+
+ mScrollMetadata = aScrollMetadata;
+ mExpectedGeckoMetrics = aLayerMetrics;
+ ShareCompositorFrameMetrics();
+
+ if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
+ // A non-zero display port margin here indicates a displayport has
+ // been set by a previous APZC for the content at this guid. The
+ // scrollable rect may have changed since then, making the margins
+ // wrong, so we need to calculate a new display port.
+ APZC_LOG("%p detected non-empty margins which probably need updating\n", this);
+ needContentRepaint = true;
+ }
+ } else {
+ // If we're not taking the aLayerMetrics wholesale we still need to pull
+ // in some things into our local mFrameMetrics because these things are
+ // determined by Gecko and our copy in mFrameMetrics may be stale.
+
+ if (FuzzyEqualsAdditive(mFrameMetrics.GetCompositionBounds().width, aLayerMetrics.GetCompositionBounds().width) &&
+ mFrameMetrics.GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel() &&
+ !viewportUpdated) {
+ // Any change to the pres shell resolution was requested by APZ and is
+ // already included in our zoom; however, other components of the
+ // cumulative resolution (a parent document's pres-shell resolution, or
+ // the css-driven resolution) may have changed, and we need to update
+ // our zoom to reflect that. Note that we can't just take
+ // aLayerMetrics.mZoom because the APZ may have additional async zoom
+ // since the repaint request.
+ gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution()
+ / mFrameMetrics.GetCumulativeResolution();
+ float presShellResolutionChange = aLayerMetrics.GetPresShellResolution()
+ / mFrameMetrics.GetPresShellResolution();
+ if (presShellResolutionChange != 1.0f) {
+ needContentRepaint = true;
+ }
+ mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
+ } else {
+ // Take the new zoom as either device scale or composition width or
+ // viewport size got changed (e.g. due to orientation change, or content
+ // changing the meta-viewport tag).
+ mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
+ mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel());
+ }
+ if (!mFrameMetrics.GetScrollableRect().IsEqualEdges(aLayerMetrics.GetScrollableRect())) {
+ mFrameMetrics.SetScrollableRect(aLayerMetrics.GetScrollableRect());
+ needContentRepaint = true;
+ }
+ mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
+ mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
+ mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
+ mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
+ mScrollMetadata.SetHasScrollgrab(aScrollMetadata.GetHasScrollgrab());
+ mScrollMetadata.SetLineScrollAmount(aScrollMetadata.GetLineScrollAmount());
+ mScrollMetadata.SetPageScrollAmount(aScrollMetadata.GetPageScrollAmount());
+ mScrollMetadata.SetSnapInfo(ScrollSnapInfo(aScrollMetadata.GetSnapInfo()));
+ // The scroll clip can differ between layers associated a given scroll frame,
+ // so APZC (which keeps a single copy of ScrollMetadata per scroll frame)
+ // has no business using it.
+ mScrollMetadata.SetScrollClip(Nothing());
+ mScrollMetadata.SetIsLayersIdRoot(aScrollMetadata.IsLayersIdRoot());
+ mScrollMetadata.SetUsesContainerScrolling(aScrollMetadata.UsesContainerScrolling());
+ mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer());
+ mScrollMetadata.SetForceDisableApz(aScrollMetadata.IsApzForceDisabled());
+
+ if (scrollOffsetUpdated) {
+ APZC_LOG("%p updating scroll offset from %s to %s\n", this,
+ ToString(mFrameMetrics.GetScrollOffset()).c_str(),
+ ToString(aLayerMetrics.GetScrollOffset()).c_str());
+
+ // Send an acknowledgement with the new scroll generation so that any
+ // repaint requests later in this function go through.
+ // Because of the scroll generation update, any inflight paint requests are
+ // going to be ignored by layout, and so mExpectedGeckoMetrics
+ // becomes incorrect for the purposes of calculating the LD transform. To
+ // correct this we need to update mExpectedGeckoMetrics to be the
+ // last thing we know was painted by Gecko.
+ mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
+ mExpectedGeckoMetrics = aLayerMetrics;
+
+ // Cancel the animation (which might also trigger a repaint request)
+ // after we update the scroll offset above. Otherwise we can be left
+ // in a state where things are out of sync.
+ CancelAnimation();
+
+ // Since the scroll offset has changed, we need to recompute the
+ // displayport margins and send them to layout. Otherwise there might be
+ // scenarios where for example we scroll from the top of a page (where the
+ // top displayport margin is zero) to the bottom of a page, which will
+ // result in a displayport that doesn't extend upwards at all.
+ // Note that even if the CancelAnimation call above requested a repaint
+ // this is fine because we already have repaint request deduplication.
+ needContentRepaint = true;
+ }
+ }
+
+ if (smoothScrollRequested) {
+ // A smooth scroll has been requested for animation on the compositor
+ // thread. This flag will be reset by the main thread when it receives
+ // the scroll update acknowledgement.
+
+ APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this,
+ Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
+ Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(),
+ mState);
+
+ // See comment on the similar code in the |if (scrollOffsetUpdated)| block
+ // above.
+ mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
+ needContentRepaint = true;
+ mExpectedGeckoMetrics = aLayerMetrics;
+
+ SmoothScrollTo(mFrameMetrics.GetSmoothScrollOffset());
+ }
+
+ if (needContentRepaint) {
+ // This repaint request is not driven by a user action on the APZ side
+ RequestContentRepaint(false);
+ }
+ UpdateSharedCompositorFrameMetrics();
+}
+
+const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const {
+ mMonitor.AssertCurrentThreadIn();
+ return mFrameMetrics;
+}
+
+APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
+ mMonitor.AssertNotCurrentThreadIn();
+ return mTreeManager;
+}
+
+void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
+ if (!aRect.IsFinite()) {
+ NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...");
+ return;
+ } else if (aRect.IsEmpty() && (aFlags & DISABLE_ZOOM_OUT)) {
+ // Double-tap-to-zooming uses an empty rect to mean "zoom out".
+ // If zooming out is disabled, an empty rect is nonsensical
+ // and will produce undesirable scrolling.
+ NS_WARNING("ZoomToRect got called with an empty rect and zoom out disabled; ignoring...");
+ return;
+ }
+
+ // Only the root APZC is zoomable, and the root APZC is not allowed to have
+ // different x and y scales. If it did, the calculations in this function
+ // would have to be adjusted (as e.g. it would no longer be valid to take
+ // the minimum or maximum of the ratios of the widths and heights of the
+ // page rect and the composition bounds).
+ MOZ_ASSERT(mFrameMetrics.IsRootContent());
+ MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
+
+ SetState(ANIMATING_ZOOM);
+
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ ParentLayerRect compositionBounds = mFrameMetrics.GetCompositionBounds();
+ CSSRect cssPageRect = mFrameMetrics.GetScrollableRect();
+ CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
+ CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoom().ToScaleFactor();
+ CSSToParentLayerScale targetZoom;
+
+ // The minimum zoom to prevent over-zoom-out.
+ // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
+ // then the CSS content rect, in layers pixels, will be smaller than the
+ // composition bounds. If this happens, we can't fill the target composited
+ // area with this frame.
+ CSSToParentLayerScale localMinZoom(std::max(mZoomConstraints.mMinZoom.scale,
+ std::max(compositionBounds.width / cssPageRect.width,
+ compositionBounds.height / cssPageRect.height)));
+ CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom;
+
+ if (!aRect.IsEmpty()) {
+ // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
+ aRect = aRect.Intersect(cssPageRect);
+ targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+ compositionBounds.height / aRect.height));
+ }
+
+ // 1. If the rect is empty, the content-side logic for handling a double-tap
+ // requested that we zoom out.
+ // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
+ // 3. currentZoom is equal to localMinZoom and user still double-tapping it
+ // Treat these three cases as a request to zoom out as much as possible.
+ bool zoomOut;
+ if (aFlags & DISABLE_ZOOM_OUT) {
+ zoomOut = false;
+ } else {
+ zoomOut = aRect.IsEmpty() ||
+ (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
+ (currentZoom == localMinZoom && targetZoom <= localMinZoom);
+ }
+
+ if (zoomOut) {
+ CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
+ float y = scrollOffset.y;
+ float newHeight =
+ cssPageRect.width * (compositedSize.height / compositedSize.width);
+ float dh = compositedSize.height - newHeight;
+
+ aRect = CSSRect(0.0f,
+ y + dh/2,
+ cssPageRect.width,
+ newHeight);
+ aRect = aRect.Intersect(cssPageRect);
+ targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+ compositionBounds.height / aRect.height));
+ }
+
+ targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
+ FrameMetrics endZoomToMetrics = mFrameMetrics;
+ if (aFlags & PAN_INTO_VIEW_ONLY) {
+ targetZoom = currentZoom;
+ } else if(aFlags & ONLY_ZOOM_TO_DEFAULT_SCALE) {
+ CSSToParentLayerScale zoomAtDefaultScale =
+ mFrameMetrics.GetDevPixelsPerCSSPixel() * LayoutDeviceToParentLayerScale(1.0);
+ if (targetZoom.scale > zoomAtDefaultScale.scale) {
+ // Only change the zoom if we are less than the default zoom
+ if (currentZoom.scale < zoomAtDefaultScale.scale) {
+ targetZoom = zoomAtDefaultScale;
+ } else {
+ targetZoom = currentZoom;
+ }
+ }
+ }
+ endZoomToMetrics.SetZoom(CSSToParentLayerScale2D(targetZoom));
+
+ // Adjust the zoomToRect to a sensible position to prevent overscrolling.
+ CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
+
+ // Vertically center the zoomed element in the screen.
+ if (!zoomOut && (sizeAfterZoom.height > aRect.height)) {
+ aRect.y -= (sizeAfterZoom.height - aRect.height) * 0.5f;
+ if (aRect.y < 0.0f) {
+ aRect.y = 0.0f;
+ }
+ }
+
+ // If either of these conditions are met, the page will be
+ // overscrolled after zoomed
+ if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
+ aRect.y = cssPageRect.height - sizeAfterZoom.height;
+ aRect.y = aRect.y > 0 ? aRect.y : 0;
+ }
+ if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
+ aRect.x = cssPageRect.width - sizeAfterZoom.width;
+ aRect.x = aRect.x > 0 ? aRect.x : 0;
+ }
+
+ endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
+
+ StartAnimation(new ZoomAnimation(
+ mFrameMetrics.GetScrollOffset(),
+ mFrameMetrics.GetZoom(),
+ endZoomToMetrics.GetScrollOffset(),
+ endZoomToMetrics.GetZoom()));
+
+ // Schedule a repaint now, so the new displayport will be painted before the
+ // animation finishes.
+ ParentLayerPoint velocity(0, 0);
+ endZoomToMetrics.SetDisplayPortMargins(
+ CalculatePendingDisplayPort(endZoomToMetrics, velocity));
+ endZoomToMetrics.SetUseDisplayPortMargins(true);
+ endZoomToMetrics.SetPaintRequestTime(TimeStamp::Now());
+ endZoomToMetrics.SetRepaintDrivenByUserAction(true);
+
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (!controller) {
+ return;
+ }
+ if (controller->IsRepaintThread()) {
+ RequestContentRepaint(endZoomToMetrics, velocity);
+ } else {
+ // use a local var to resolve the function overload
+ auto func = static_cast<void (AsyncPanZoomController::*)(const FrameMetrics&, const ParentLayerPoint&)>
+ (&AsyncPanZoomController::RequestContentRepaint);
+ controller->DispatchToRepaintThread(
+ NewRunnableMethod<FrameMetrics, ParentLayerPoint>(
+ this, func, endZoomToMetrics, velocity));
+ }
+ }
+}
+
+CancelableBlockState*
+AsyncPanZoomController::GetCurrentInputBlock() const
+{
+ return GetInputQueue()->GetCurrentBlock();
+}
+
+TouchBlockState*
+AsyncPanZoomController::GetCurrentTouchBlock() const
+{
+ return GetInputQueue()->GetCurrentTouchBlock();
+}
+
+PanGestureBlockState*
+AsyncPanZoomController::GetCurrentPanGestureBlock() const
+{
+ return GetInputQueue()->GetCurrentPanGestureBlock();
+}
+
+void
+AsyncPanZoomController::ResetTouchInputState()
+{
+ MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
+ RefPtr<GestureEventListener> listener = GetGestureEventListener();
+ if (listener) {
+ listener->HandleInputEvent(cancel);
+ }
+ CancelAnimationAndGestureState();
+ // Clear overscroll along the entire handoff chain, in case an APZC
+ // later in the chain is overscrolled.
+ if (TouchBlockState* block = GetCurrentTouchBlock()) {
+ block->GetOverscrollHandoffChain()->ClearOverscroll();
+ }
+}
+
+void
+AsyncPanZoomController::CancelAnimationAndGestureState()
+{
+ mX.CancelGesture();
+ mY.CancelGesture();
+ CancelAnimation(CancelAnimationFlags::ScrollSnap);
+}
+
+bool
+AsyncPanZoomController::HasReadyTouchBlock() const
+{
+ return GetInputQueue()->HasReadyTouchBlock();
+}
+
+void AsyncPanZoomController::SetState(PanZoomState aNewState)
+{
+ PanZoomState oldState;
+
+ // Intentional scoping for mutex
+ {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ APZC_LOG("%p changing from state %d to %d\n", this, mState, aNewState);
+ oldState = mState;
+ mState = aNewState;
+ }
+
+ DispatchStateChangeNotification(oldState, aNewState);
+}
+
+void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldState,
+ PanZoomState aNewState)
+{
+ { // scope the lock
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ if (mNotificationBlockers > 0) {
+ return;
+ }
+ }
+
+ if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+ if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eTransformBegin);
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // Let the compositor know about scroll state changes so it can manage
+ // windowed plugins.
+ if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+ mCompositorController->ScheduleHideAllPluginWindows();
+ }
+#endif
+ } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eTransformEnd);
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+ mCompositorController->ScheduleShowAllPluginWindows();
+ }
+#endif
+ }
+ }
+}
+
+bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
+ return !(aState == NOTHING || aState == TOUCHING);
+}
+
+bool AsyncPanZoomController::IsInPanningState() const {
+ return (mState == PANNING || mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y);
+}
+
+void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
+ APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
+ aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
+ if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
+ NS_WARNING("APZC received zoom constraints with NaN values; dropping...");
+ return;
+ }
+
+ CSSToParentLayerScale min = mFrameMetrics.GetDevPixelsPerCSSPixel()
+ * kViewportMinScale / ParentLayerToScreenScale(1);
+ CSSToParentLayerScale max = mFrameMetrics.GetDevPixelsPerCSSPixel()
+ * kViewportMaxScale / ParentLayerToScreenScale(1);
+
+ // inf float values and other bad cases should be sanitized by the code below.
+ mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
+ mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
+ mZoomConstraints.mMinZoom = (min > aConstraints.mMinZoom ? min : aConstraints.mMinZoom);
+ mZoomConstraints.mMaxZoom = (max > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : max);
+ if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
+ mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
+ }
+}
+
+ZoomConstraints
+AsyncPanZoomController::GetZoomConstraints() const
+{
+ return mZoomConstraints;
+}
+
+
+void AsyncPanZoomController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ APZThreadUtils::AssertOnControllerThread();
+ RefPtr<Runnable> task = aTask;
+ RefPtr<GeckoContentController> controller = GetGeckoContentController();
+ if (controller) {
+ controller->PostDelayedTask(task.forget(), aDelayMs);
+ }
+ // If there is no controller, that means this APZC has been destroyed, and
+ // we probably don't need to run the task. It will get destroyed when the
+ // RefPtr goes out of scope.
+}
+
+bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
+{
+ return aGuid == GetGuid();
+}
+
+bool AsyncPanZoomController::HasTreeManager(const APZCTreeManager* aTreeManager) const
+{
+ return GetApzcTreeManager() == aTreeManager;
+}
+
+void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const
+{
+ if (aGuidOut) {
+ *aGuidOut = GetGuid();
+ }
+}
+
+ScrollableLayerGuid AsyncPanZoomController::GetGuid() const
+{
+ return ScrollableLayerGuid(mLayersId, mFrameMetrics);
+}
+
+void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
+{
+ mMonitor.AssertCurrentThreadIn();
+
+ FrameMetrics* frame = mSharedFrameMetricsBuffer ?
+ static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
+
+ if (frame && mSharedLock && gfxPrefs::ProgressivePaint()) {
+ mSharedLock->Lock();
+ *frame = mFrameMetrics;
+ mSharedLock->Unlock();
+ }
+}
+
+void AsyncPanZoomController::ShareCompositorFrameMetrics()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ // Only create the shared memory buffer if it hasn't already been created,
+ // we are using progressive tile painting, and we have a
+ // controller to pass the shared memory back to the content process/thread.
+ if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
+
+ // Create shared memory and initialize it with the current FrameMetrics value
+ mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
+ FrameMetrics* frame = nullptr;
+ mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
+ mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
+ frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
+
+ if (frame) {
+
+ { // scope the monitor, only needed to copy the FrameMetrics.
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ *frame = mFrameMetrics;
+ }
+
+ // Get the process id of the content process
+ base::ProcessId otherPid = mMetricsSharingController->RemotePid();
+ ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
+
+ // Get the shared memory handle to share with the content process
+ mSharedFrameMetricsBuffer->ShareToProcess(otherPid, &mem);
+
+ // Get the cross process mutex handle to share with the content process
+ mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
+ CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(otherPid);
+
+ // Send the shared memory handle and cross process handle to the content
+ // process by an asynchronous ipc call. Include the APZC unique ID
+ // so the content process know which APZC sent this shared FrameMetrics.
+ if (!mMetricsSharingController->StartSharingMetrics(mem, handle, mLayersId, mAPZCId)) {
+ APZC_LOG("%p failed to share FrameMetrics with content process.", this);
+ }
+ }
+ }
+}
+
+Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
+ const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) {
+ mMonitor.AssertCurrentThreadIn();
+ APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str());
+ CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
+ if (Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
+ mScrollMetadata.GetSnapInfo(),
+ aUnit,
+ CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()),
+ CSSRect::ToAppUnits(scrollRange),
+ CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()),
+ CSSPoint::ToAppUnits(aDestination))) {
+ CSSPoint cssSnapPoint = CSSPoint::FromAppUnits(snapPoint.ref());
+ // GetSnapPointForDestination() can produce a destination that's outside
+ // of the scroll frame's scroll range. Clamp it here (this matches the
+ // behaviour of the main-thread code path, which clamps it in
+ // nsGfxScrollFrame::ScrollTo()).
+ return Some(scrollRange.ClampPoint(cssSnapPoint));
+ }
+ return Nothing();
+}
+
+void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
+ if (Maybe<CSSPoint> snapPoint =
+ FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) {
+ if (*snapPoint != mFrameMetrics.GetScrollOffset()) {
+ APZC_LOG("%p smooth scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str());
+ SmoothScrollTo(*snapPoint);
+ }
+ }
+}
+
+void AsyncPanZoomController::ScrollSnap() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ ScrollSnapNear(mFrameMetrics.GetScrollOffset());
+}
+
+void AsyncPanZoomController::ScrollSnapToDestination() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ float friction = gfxPrefs::APZFlingFriction();
+ ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity());
+ ParentLayerPoint predictedDelta;
+ // "-velocity / log(1.0 - friction)" is the integral of the deceleration
+ // curve modeled for flings in the "Axis" class.
+ if (velocity.x != 0.0f) {
+ predictedDelta.x = -velocity.x / log(1.0 - friction);
+ }
+ if (velocity.y != 0.0f) {
+ predictedDelta.y = -velocity.y / log(1.0 - friction);
+ }
+ CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom();
+
+ // If the fling will overscroll, don't scroll snap, because then the user
+ // user would not see any overscroll animation.
+ bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) ||
+ (velocity.y * mY.GetOverscroll() >= 0));
+ if (!flingWillOverscroll) {
+ APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f "
+ "predictedDelta: %f, %f position: %f, %f "
+ "predictedDestination: %f, %f\n",
+ this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
+ (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
+ (float)mFrameMetrics.GetScrollOffset().y,
+ (float)predictedDestination.x, (float)predictedDestination.y);
+
+ ScrollSnapNear(predictedDestination);
+ }
+}
+
+bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
+ const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition)
+{
+ // Don't scroll snap for pixel scrolls. This matches the main thread
+ // behaviour in EventStateManager::DoScrollText().
+ if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
+ return false;
+ }
+
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom();
+ CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint(
+ aStartPosition + (aDelta / zoom));
+ nsIScrollableFrame::ScrollUnit unit =
+ ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType);
+
+ if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, unit)) {
+ aDelta = (*snapPoint - aStartPosition) * zoom;
+ aStartPosition = *snapPoint;
+ return true;
+ }
+ return false;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h
new file mode 100644
index 000000000..36f2f308c
--- /dev/null
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -0,0 +1,1224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_AsyncPanZoomController_h
+#define mozilla_layers_AsyncPanZoomController_h
+
+#include "CrossProcessMutex.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/AsyncPanZoomAnimation.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Atomics.h"
+#include "InputData.h"
+#include "Axis.h"
+#include "InputQueue.h"
+#include "APZUtils.h"
+#include "Layers.h" // for Layer::ScrollDirection
+#include "LayersTypes.h"
+#include "mozilla/gfx/Matrix.h"
+#include "nsIScrollableFrame.h"
+#include "nsRegion.h"
+#include "nsTArray.h"
+#include "PotentialCheckerboardDurationTracker.h"
+
+#include "base/message_loop.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class SharedMemoryBasic;
+
+} // namespace ipc
+
+namespace layers {
+
+class AsyncDragMetrics;
+struct ScrollableLayerGuid;
+class CompositorController;
+class MetricsSharingController;
+class GestureEventListener;
+struct AsyncTransform;
+class AsyncPanZoomAnimation;
+class AndroidFlingAnimation;
+class GenericFlingAnimation;
+class InputBlockState;
+class TouchBlockState;
+class PanGestureBlockState;
+class OverscrollHandoffChain;
+class StateChangeNotificationBlocker;
+class CheckerboardEvent;
+class OverscrollEffectBase;
+class WidgetOverscrollEffect;
+class GenericOverscrollEffect;
+class AndroidSpecificState;
+
+// Base class for grouping platform-specific APZC state variables.
+class PlatformSpecificStateBase {
+public:
+ virtual ~PlatformSpecificStateBase() {}
+ virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; }
+};
+
+/**
+ * Controller for all panning and zooming logic. Any time a user input is
+ * detected and it must be processed in some way to affect what the user sees,
+ * it goes through here. Listens for any input event from InputData and can
+ * optionally handle WidgetGUIEvent-derived touch events, but this must be done
+ * on the main thread. Note that this class completely cross-platform.
+ *
+ * Input events originate on the UI thread of the platform that this runs on,
+ * and are then sent to this class. This class processes the event in some way;
+ * for example, a touch move will usually lead to a panning of content (though
+ * of course there are exceptions, such as if content preventDefaults the event,
+ * or if the target frame is not scrollable). The compositor interacts with this
+ * class by locking it and querying it for the current transform matrix based on
+ * the panning and zooming logic that was invoked on the UI thread.
+ *
+ * Currently, each outer DOM window (i.e. a website in a tab, but not any
+ * subframes) has its own AsyncPanZoomController. In the future, to support
+ * asynchronously scrolled subframes, we want to have one AsyncPanZoomController
+ * per frame.
+ */
+class AsyncPanZoomController {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController)
+
+ typedef mozilla::MonitorAutoLock MonitorAutoLock;
+ typedef mozilla::gfx::Matrix4x4 Matrix4x4;
+
+public:
+ enum GestureBehavior {
+ // The platform code is responsible for forwarding gesture events here. We
+ // will not attempt to generate gesture events from MultiTouchInputs.
+ DEFAULT_GESTURES,
+ // An instance of GestureEventListener is used to detect gestures. This is
+ // handled completely internally within this class.
+ USE_GESTURE_DETECTOR
+ };
+
+ /**
+ * Constant describing the tolerance in distance we use, multiplied by the
+ * device DPI, before we start panning the screen. This is to prevent us from
+ * accidentally processing taps as touch moves, and from very short/accidental
+ * touches moving the screen.
+ * Note: It's an abuse of the 'Coord' class to use it to represent a 2D
+ * distance, but it's the closest thing we currently have.
+ */
+ static ScreenCoord GetTouchStartTolerance();
+
+ AsyncPanZoomController(uint64_t aLayersId,
+ APZCTreeManager* aTreeManager,
+ const RefPtr<InputQueue>& aInputQueue,
+ GeckoContentController* aController,
+ GestureBehavior aGestures = DEFAULT_GESTURES);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the gecko thread.
+ //
+
+ /**
+ * Read the various prefs and do any global initialization for all APZC instances.
+ * This must be run on the gecko thread before any APZC instances are actually
+ * used for anything meaningful.
+ */
+ static void InitializeGlobalState();
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the controller/UI thread.
+ //
+
+ /**
+ * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+ * in. The actual animation is done on the compositor thread after being set
+ * up.
+ */
+ void ZoomToRect(CSSRect aRect, const uint32_t aFlags);
+
+ /**
+ * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+ */
+ void UpdateZoomConstraints(const ZoomConstraints& aConstraints);
+
+ /**
+ * Return the zoom constraints last set for this APZC (in the constructor
+ * or in UpdateZoomConstraints()).
+ */
+ ZoomConstraints GetZoomConstraints() const;
+
+ /**
+ * Schedules a runnable to run on the controller/UI thread at some time
+ * in the future.
+ */
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the compositor thread.
+ //
+
+ /**
+ * Advances any animations currently running to the given timestamp.
+ * This may be called multiple times with the same timestamp.
+ *
+ * The return value indicates whether or not any currently running animation
+ * should continue. If true, the compositor should schedule another composite.
+ */
+ bool AdvanceAnimations(const TimeStamp& aSampleTime);
+
+ bool UpdateAnimation(const TimeStamp& aSampleTime,
+ nsTArray<RefPtr<Runnable>>* aOutDeferredTasks);
+
+ /**
+ * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
+ * for the container layer corresponding to this APZC.
+ * |aIsFirstPaint| is a flag passed from the shadow
+ * layers code indicating that the scroll metadata being sent with this call are
+ * the initial metadata and the initial paint of the frame has just happened.
+ */
+ void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
+ bool aThisLayerTreeUpdated);
+
+ /**
+ * The platform implementation must set the compositor controller so that we can
+ * request composites.
+ */
+ void SetCompositorController(CompositorController* aCompositorController);
+
+ /**
+ * If we need to share the frame metrics with some other thread, this controller
+ * needs to be set and provides relevant information/APIs.
+ */
+ void SetMetricsSharingController(MetricsSharingController* aMetricsSharingController);
+
+ // --------------------------------------------------------------------------
+ // These methods can be called from any thread.
+ //
+
+ /**
+ * Shut down the controller/UI thread state and prepare to be
+ * deleted (which may happen from any thread).
+ */
+ void Destroy();
+
+ /**
+ * Returns true if Destroy() has already been called on this APZC instance.
+ */
+ bool IsDestroyed() const;
+
+ /**
+ * Returns the transform to take something from the coordinate space of the
+ * last thing we know gecko painted, to the coordinate space of the last thing
+ * we asked gecko to paint. In cases where that last request has not yet been
+ * processed, this is needed to transform input events properly into a space
+ * gecko will understand.
+ */
+ Matrix4x4 GetTransformToLastDispatchedPaint() const;
+
+ /**
+ * Returns the number of CSS pixels of checkerboard according to the metrics
+ * in this APZC.
+ */
+ uint32_t GetCheckerboardMagnitude() const;
+
+ /**
+ * Report the number of CSSPixel-milliseconds of checkerboard to telemetry.
+ */
+ void ReportCheckerboard(const TimeStamp& aSampleTime);
+
+ /**
+ * Flush any active checkerboard report that's in progress. This basically
+ * pretends like any in-progress checkerboard event has terminated, and pushes
+ * out the report to the checkerboard reporting service and telemetry. If the
+ * checkerboard event has not really finished, it will start a new event
+ * on the next composite.
+ */
+ void FlushActiveCheckerboardReport();
+
+ /**
+ * Returns whether or not the APZC is currently in a state of checkerboarding.
+ * This is a simple computation based on the last-painted content and whether
+ * the async transform has pushed it so far that it doesn't fully contain the
+ * composition bounds.
+ */
+ bool IsCurrentlyCheckerboarding() const;
+
+ /**
+ * Recalculates the displayport. Ideally, this should paint an area bigger
+ * than the composite-to dimensions so that when you scroll down, you don't
+ * checkerboard immediately. This includes a bunch of logic, including
+ * algorithms to bias painting in the direction of the velocity.
+ */
+ static const ScreenMargin CalculatePendingDisplayPort(
+ const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ nsEventStatus HandleDragEvent(const MouseInput& aEvent,
+ const AsyncDragMetrics& aDragMetrics);
+
+ /**
+ * Handler for events which should not be intercepted by the touch listener.
+ */
+ nsEventStatus HandleInputEvent(const InputData& aEvent,
+ const ScreenToParentLayerMatrix4x4& aTransformToApzc);
+
+ /**
+ * Handler for gesture events.
+ * Currently some gestures are detected in GestureEventListener that calls
+ * APZC back through this handler in order to avoid recursive calls to
+ * APZC::HandleInputEvent() which is supposed to do the work for
+ * ReceiveInputEvent().
+ */
+ nsEventStatus HandleGestureEvent(const InputData& aEvent);
+
+ /**
+ * Handler for touch velocity.
+ * Sometimes the touch move event will have a velocity even though no scrolling
+ * is occurring such as when the toolbar is being hidden/shown in Fennec.
+ * This function can be called to have the y axis' velocity queue updated.
+ */
+ void HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY);
+
+ /**
+ * Populates the provided object (if non-null) with the scrollable guid of this apzc.
+ */
+ void GetGuid(ScrollableLayerGuid* aGuidOut) const;
+
+ /**
+ * Returns the scrollable guid of this apzc.
+ */
+ ScrollableLayerGuid GetGuid() const;
+
+ /**
+ * Returns true if this APZC instance is for the layer identified by the guid.
+ */
+ bool Matches(const ScrollableLayerGuid& aGuid);
+
+ /**
+ * Returns true if the tree manager of this APZC is the same as the one
+ * passed in.
+ */
+ bool HasTreeManager(const APZCTreeManager* aTreeManager) const;
+
+ void StartAnimation(AsyncPanZoomAnimation* aAnimation);
+
+ /**
+ * Cancels any currently running animation.
+ * aFlags is a bit-field to provide specifics of how to cancel the animation.
+ * See CancelAnimationFlags.
+ */
+ void CancelAnimation(CancelAnimationFlags aFlags = Default);
+
+ /**
+ * Adjusts the scroll position to compensate for a shift in the surface, such
+ * that the content appears to remain visually in the same position. i.e. if
+ * the surface moves up by 10 screenpixels, the scroll position should also
+ * move up by 10 pixels so that what used to be at the top of the surface is
+ * now 10 pixels down the surface.
+ */
+ void AdjustScrollForSurfaceShift(const ScreenPoint& aShift);
+
+ /**
+ * Clear any overscroll on this APZC.
+ */
+ void ClearOverscroll();
+
+ /**
+ * Returns whether this APZC is for an element marked with the 'scrollgrab'
+ * attribute.
+ */
+ bool HasScrollgrab() const { return mScrollMetadata.GetHasScrollgrab(); }
+
+ /**
+ * Returns whether this APZC has room to be panned (in any direction).
+ */
+ bool IsPannable() const;
+
+ /**
+ * Returns true if the APZC has been flung with a velocity greater than the
+ * stop-on-tap fling velocity threshold (which is pref-controlled).
+ */
+ bool IsFlingingFast() const;
+
+ /**
+ * Returns the identifier of the touch in the last touch event processed by
+ * this APZC. This should only be called when the last touch event contained
+ * only one touch.
+ */
+ int32_t GetLastTouchIdentifier() const;
+
+ /**
+ * Returns the matrix that transforms points from global screen space into
+ * this APZC's ParentLayer space.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ScreenToParentLayerMatrix4x4 GetTransformToThis() const;
+
+ /**
+ * Convert the vector |aVector|, rooted at the point |aAnchor|, from
+ * this APZC's ParentLayer coordinates into screen coordinates.
+ * The anchor is necessary because with 3D tranforms, the location of the
+ * vector can affect the result of the transform.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ScreenPoint ToScreenCoordinates(const ParentLayerPoint& aVector,
+ const ParentLayerPoint& aAnchor) const;
+
+ /**
+ * Convert the vector |aVector|, rooted at the point |aAnchor|, from
+ * screen coordinates into this APZC's ParentLayer coordinates.
+ * The anchor is necessary because with 3D tranforms, the location of the
+ * vector can affect the result of the transform.
+ * To respect the lock ordering, mMonitor must NOT be held when calling
+ * this function (since this function acquires the tree lock).
+ */
+ ParentLayerPoint ToParentLayerCoordinates(const ScreenPoint& aVector,
+ const ScreenPoint& aAnchor) const;
+
+ // Return whether or not a wheel event will be able to scroll in either
+ // direction.
+ bool CanScroll(const InputData& aEvent) const;
+
+ // Return whether or not a scroll delta will be able to scroll in either
+ // direction.
+ bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const;
+
+ // Return whether or not there is room to scroll this APZC
+ // in the given direction.
+ bool CanScroll(Layer::ScrollDirection aDirection) const;
+
+ void NotifyMozMouseScrollEvent(const nsString& aString) const;
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~AsyncPanZoomController();
+
+ // Returns the cached current frame time.
+ TimeStamp GetFrameTime() const;
+
+ /**
+ * Helper method for touches beginning. Sets everything up for panning and any
+ * multitouch gestures.
+ */
+ nsEventStatus OnTouchStart(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches moving. Does any transforms needed when panning.
+ */
+ nsEventStatus OnTouchMove(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches ending. Redraws the screen if necessary and does
+ * any cleanup after a touch has ended.
+ */
+ nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for touches being cancelled. Treated roughly the same as a
+ * touch ending (OnTouchEnd()).
+ */
+ nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent);
+
+ /**
+ * Helper method for scales beginning. Distinct from the OnTouch* handlers in
+ * that this implies some outside implementation has determined that the user
+ * is pinching.
+ */
+ nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper method for scaling. As the user moves their fingers when pinching,
+ * this changes the scale of the page.
+ */
+ nsEventStatus OnScale(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper method for scales ending. Redraws the screen if necessary and does
+ * any cleanup after a scale has ended.
+ */
+ nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
+
+ /**
+ * Helper methods for handling pan events.
+ */
+ nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent);
+ nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
+ nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
+ nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
+ nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
+ nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
+ nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
+
+ /**
+ * Helper methods for handling scroll wheel events.
+ */
+ nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent);
+
+ ParentLayerPoint GetScrollWheelDelta(const ScrollWheelInput& aEvent) const;
+
+ /**
+ * Helper methods for long press gestures.
+ */
+ nsEventStatus OnLongPress(const TapGestureInput& aEvent);
+ nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for single tap gestures.
+ */
+ nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for a single tap confirmed.
+ */
+ nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for double taps.
+ */
+ nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method for double taps where the double-tap gesture is disabled.
+ */
+ nsEventStatus OnSecondTap(const TapGestureInput& aEvent);
+
+ /**
+ * Helper method to cancel any gesture currently going to Gecko. Used
+ * primarily when a user taps the screen over some clickable content but then
+ * pans down instead of letting go (i.e. to cancel a previous touch so that a
+ * new one can properly take effect.
+ */
+ nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
+
+ /**
+ * Scrolls the viewport by an X,Y offset.
+ */
+ void ScrollBy(const CSSPoint& aOffset);
+
+ /**
+ * Scales the viewport by an amount (note that it multiplies this scale in to
+ * the current scale, it doesn't set it to |aScale|). Also considers a focus
+ * point so that the page zooms inward/outward from that point.
+ */
+ void ScaleWithFocus(float aScale,
+ const CSSPoint& aFocus);
+
+ /**
+ * Schedules a composite on the compositor thread.
+ */
+ void ScheduleComposite();
+
+ /**
+ * Schedules a composite, and if enough time has elapsed since the last
+ * paint, a paint.
+ */
+ void ScheduleCompositeAndMaybeRepaint();
+
+ /**
+ * Gets the displacement of the current touch since it began. That is, it is
+ * the distance between the current position and the initial position of the
+ * current touch (this only makes sense if a touch is currently happening and
+ * OnTouchMove() or the equivalent for pan gestures is being invoked).
+ * Note: It's an abuse of the 'Coord' class to use it to represent a 2D
+ * distance, but it's the closest thing we currently have.
+ */
+ ScreenCoord PanDistance() const;
+
+ /**
+ * Gets the start point of the current touch.
+ * Like PanDistance(), this only makes sense if a touch is currently
+ * happening and OnTouchMove() or the equivalent for pan gestures is
+ * being invoked.
+ */
+ ParentLayerPoint PanStart() const;
+
+ /**
+ * Gets a vector of the velocities of each axis.
+ */
+ const ParentLayerPoint GetVelocityVector() const;
+
+ /**
+ * Sets the velocities of each axis.
+ */
+ void SetVelocityVector(const ParentLayerPoint& aVelocityVector);
+
+ /**
+ * Gets the first touch point from a MultiTouchInput. This gets only
+ * the first one and assumes the rest are either missing or not relevant.
+ */
+ ParentLayerPoint GetFirstTouchPoint(const MultiTouchInput& aEvent);
+
+ /**
+ * Sets the panning state basing on the pan direction angle and current touch-action value.
+ */
+ void HandlePanningWithTouchAction(double angle);
+
+ /**
+ * Sets the panning state ignoring the touch action value.
+ */
+ void HandlePanning(double angle);
+
+ /**
+ * Update the panning state and axis locks.
+ */
+ void HandlePanningUpdate(const ScreenPoint& aDelta);
+
+ /**
+ * Sets up anything needed for panning. This takes us out of the "TOUCHING"
+ * state and starts actually panning us.
+ */
+ nsEventStatus StartPanning(const MultiTouchInput& aStartPoint);
+
+ /**
+ * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for
+ * both axes and factors in the time delta from the last update.
+ */
+ void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent);
+
+ /**
+ * Does any panning required due to a new touch event.
+ */
+ void TrackTouch(const MultiTouchInput& aEvent);
+
+ /**
+ * Utility function to send updated FrameMetrics to Gecko so that it can paint
+ * the displayport area. Calls into GeckoContentController to do the actual
+ * work. This call will use the current metrics. If this function is called
+ * from a non-main thread, it will redispatch itself to the main thread, and
+ * use the latest metrics during the redispatch.
+ */
+ void RequestContentRepaint(bool aUserAction = true);
+
+ /**
+ * Send the provided metrics to Gecko to trigger a repaint. This function
+ * may filter duplicate calls with the same metrics. This function must be
+ * called on the main thread.
+ */
+ void RequestContentRepaint(const FrameMetrics& aFrameMetrics,
+ const ParentLayerPoint& aVelocity);
+
+ /**
+ * Gets the current frame metrics. This is *not* the Gecko copy stored in the
+ * layers code.
+ */
+ const FrameMetrics& GetFrameMetrics() const;
+
+ /**
+ * Gets the pointer to the apzc tree manager. All the access to tree manager
+ * should be made via this method and not via private variable since this method
+ * ensures that no lock is set.
+ */
+ APZCTreeManager* GetApzcTreeManager() const;
+
+ /**
+ * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
+ * to the parent document. This excludes the transient compositor transform.
+ * NOTE: This must be converted to LayoutDevicePoint relative to the child
+ * document before sending over IPC to a child process.
+ */
+ bool ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut);
+
+ enum AxisLockMode {
+ FREE, /* No locking at all */
+ STANDARD, /* Default axis locking mode that remains locked until pan ends*/
+ STICKY, /* Allow lock to be broken, with hysteresis */
+ };
+
+ static AxisLockMode GetAxisLockMode();
+
+ // Helper function for OnSingleTapUp(), OnSingleTapConfirmed(), and
+ // OnLongPressUp().
+ nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType,
+ const ScreenIntPoint& aPoint,
+ mozilla::Modifiers aModifiers);
+
+ // Common processing at the end of a touch block.
+ void OnTouchEndOrCancel();
+
+ uint64_t mLayersId;
+ RefPtr<CompositorController> mCompositorController;
+ RefPtr<MetricsSharingController> mMetricsSharingController;
+
+ /* Access to the following two fields is protected by the mRefPtrMonitor,
+ since they are accessed on the UI thread but can be cleared on the
+ compositor thread. */
+ RefPtr<GeckoContentController> mGeckoContentController;
+ RefPtr<GestureEventListener> mGestureEventListener;
+ mutable Monitor mRefPtrMonitor;
+
+ // This is a raw pointer to avoid introducing a reference cycle between
+ // AsyncPanZoomController and APZCTreeManager. Since these objects don't
+ // live on the main thread, we can't use the cycle collector with them.
+ // The APZCTreeManager owns the lifetime of the APZCs, so nulling this
+ // pointer out in Destroy() will prevent accessing deleted memory.
+ Atomic<APZCTreeManager*> mTreeManager;
+
+ /* Utility functions that return a addrefed pointer to the corresponding fields. */
+ already_AddRefed<GeckoContentController> GetGeckoContentController() const;
+ already_AddRefed<GestureEventListener> GetGestureEventListener() const;
+
+ PlatformSpecificStateBase* GetPlatformSpecificState();
+
+protected:
+ // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
+ // monitor. Do not read from or modify either of them without locking.
+ ScrollMetadata mScrollMetadata;
+ FrameMetrics& mFrameMetrics; // for convenience, refers to mScrollMetadata.mMetrics
+
+ // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
+ // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
+ // monitor should be held. When setting |mState|, either the SetState()
+ // function can be used, or the monitor can be held and then |mState| updated.
+ // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h.
+ // This is mutable to allow entering it from 'const' methods; doing otherwise
+ // would significantly limit what methods could be 'const'.
+ mutable ReentrantMonitor mMonitor;
+
+private:
+ // Metadata of the container layer corresponding to this APZC. This is
+ // stored here so that it is accessible from the UI/controller thread.
+ // These are the metrics at last content paint, the most recent
+ // values we were notified of in NotifyLayersUpdate(). Since it represents
+ // the Gecko state, it should be used as a basis for untransformation when
+ // sending messages back to Gecko.
+ ScrollMetadata mLastContentPaintMetadata;
+ FrameMetrics& mLastContentPaintMetrics; // for convenience, refers to mLastContentPaintMetadata.mMetrics
+ // The last metrics used for a content repaint request.
+ FrameMetrics mLastPaintRequestMetrics;
+ // The metrics that we expect content to have. This is updated when we
+ // request a content repaint, and when we receive a shadow layers update.
+ // This allows us to transform events into Gecko's coordinate space.
+ FrameMetrics mExpectedGeckoMetrics;
+
+ AxisX mX;
+ AxisY mY;
+
+ // This flag is set to true when we are in a axis-locked pan as a result of
+ // the touch-action CSS property.
+ bool mPanDirRestricted;
+
+ // Most up-to-date constraints on zooming. These should always be reasonable
+ // values; for example, allowing a min zoom of 0.0 can cause very bad things
+ // to happen.
+ ZoomConstraints mZoomConstraints;
+
+ // The last time the compositor has sampled the content transform for this
+ // frame.
+ TimeStamp mLastSampleTime;
+
+ // The last sample time at which we submitted a checkerboarding report.
+ TimeStamp mLastCheckerboardReport;
+
+ // Stores the previous focus point if there is a pinch gesture happening. Used
+ // to allow panning by moving multiple fingers (thus moving the focus point).
+ ParentLayerPoint mLastZoomFocus;
+
+ RefPtr<AsyncPanZoomAnimation> mAnimation;
+
+ UniquePtr<OverscrollEffectBase> mOverscrollEffect;
+
+ // Groups state variables that are specific to a platform.
+ // Initialized on first use.
+ UniquePtr<PlatformSpecificStateBase> mPlatformSpecificState;
+
+ friend class Axis;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to expose
+ * the current async transform state to callers.
+ */
+public:
+ /**
+ * Allows callers to specify which type of async transform they want:
+ * NORMAL provides the actual async transforms of the APZC, whereas
+ * RESPECT_FORCE_DISABLE will provide empty async transforms if and only if
+ * the metrics has the mForceDisableApz flag set. In general the latter should
+ * only be used by call sites that are applying the transform to update
+ * a layer's position.
+ */
+ enum AsyncMode {
+ NORMAL,
+ RESPECT_FORCE_DISABLE,
+ };
+
+ /**
+ * Query the transforms that should be applied to the layer corresponding
+ * to this APZC due to asynchronous panning and zooming.
+ * This function returns the async transform via the |aOutTransform|
+ * out parameter.
+ */
+ ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncMode aMode) const;
+
+ /**
+ * Return a visual effect that reflects this apzc's
+ * overscrolled state, if any.
+ */
+ AsyncTransformComponentMatrix GetOverscrollTransform(AsyncMode aMode) const;
+
+ /**
+ * Returns the incremental transformation corresponding to the async pan/zoom
+ * in progress. That is, when this transform is multiplied with the layer's
+ * existing transform, it will make the layer appear with the desired pan/zoom
+ * amount.
+ */
+ AsyncTransform GetCurrentAsyncTransform(AsyncMode aMode) const;
+
+ /**
+ * Returns the same transform as GetCurrentAsyncTransform(), but includes
+ * any transform due to axis over-scroll.
+ */
+ AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * the state that tracks what this APZC is doing with the input events.
+ */
+protected:
+ enum PanZoomState {
+ NOTHING, /* no touch-start events received */
+ FLING, /* all touches removed, but we're still scrolling page */
+ TOUCHING, /* one touch-start event received */
+
+ PANNING, /* panning the frame */
+ PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
+ PANNING_LOCKED_Y, /* as above for Y axis */
+
+ PAN_MOMENTUM, /* like PANNING, but controlled by momentum PanGestureInput events */
+
+ PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
+ ANIMATING_ZOOM, /* animated zoom to a new rect */
+ OVERSCROLL_ANIMATION, /* Spring-based animation used to relieve overscroll once
+ the finger is lifted. */
+ SMOOTH_SCROLL, /* Smooth scrolling to destination. Used by
+ CSSOM-View smooth scroll-behavior */
+ WHEEL_SCROLL /* Smooth scrolling to a destination for a wheel event. */
+ };
+
+ // This is in theory protected by |mMonitor|; that is, it should be held whenever
+ // this is updated. In practice though... see bug 897017.
+ PanZoomState mState;
+
+private:
+ friend class StateChangeNotificationBlocker;
+ /**
+ * A counter of how many StateChangeNotificationBlockers are active.
+ * A non-zero count will prevent state change notifications from
+ * being dispatched. Only code that holds mMonitor should touch this.
+ */
+ int mNotificationBlockers;
+
+ /**
+ * Helper to set the current state. Holds the monitor before actually setting
+ * it and fires content controller events based on state changes. Always set
+ * the state using this call, do not set it directly.
+ */
+ void SetState(PanZoomState aState);
+ /**
+ * Fire content controller notifications about state changes, assuming no
+ * StateChangeNotificationBlocker has been activated.
+ */
+ void DispatchStateChangeNotification(PanZoomState aOldState, PanZoomState aNewState);
+ /**
+ * Internal helpers for checking general state of this apzc.
+ */
+ static bool IsTransformingState(PanZoomState aState);
+
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * blocks of touch events and the state needed to deal with content
+ * listeners.
+ */
+public:
+ /**
+ * Flush a repaint request if one is needed, without throttling it with the
+ * paint throttler.
+ */
+ void FlushRepaintForNewInputBlock();
+
+ /**
+ * Given the number of touch points in an input event and touch block they
+ * belong to, check if the event can result in a panning/zooming behavior.
+ * This is primarily used to figure out when to dispatch the pointercancel
+ * event for the pointer events spec.
+ */
+ bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
+
+ /**
+ * Clear internal state relating to touch input handling.
+ */
+ void ResetTouchInputState();
+
+ /**
+ * Gets a ref to the input queue that is shared across the entire tree manager.
+ */
+ const RefPtr<InputQueue>& GetInputQueue() const;
+
+private:
+ void CancelAnimationAndGestureState();
+
+ RefPtr<InputQueue> mInputQueue;
+ CancelableBlockState* GetCurrentInputBlock() const;
+ TouchBlockState* GetCurrentTouchBlock() const;
+ bool HasReadyTouchBlock() const;
+
+ PanGestureBlockState* GetCurrentPanGestureBlock() const;
+
+private:
+ /* ===================================================================
+ * The functions and members in this section are used to manage
+ * fling animations, smooth scroll animations, and overscroll
+ * during a fling or smooth scroll.
+ */
+public:
+ /**
+ * Attempt a fling with the velocity specified in |aHandoffState|.
+ * If we are not pannable, the fling is handed off to the next APZC in
+ * the handoff chain via mTreeManager->DispatchFling().
+ * Returns true iff. the entire velocity of the fling was consumed by
+ * this APZC. |aHandoffState.mVelocity| is modified to contain any
+ * unused, residual velocity.
+ * |aHandoffState.mIsHandoff| should be true iff. the fling was handed off
+ * from a previous APZC, and determines whether acceleration is applied
+ * to the fling.
+ */
+ bool AttemptFling(FlingHandoffState& aHandoffState);
+
+private:
+ friend class AndroidFlingAnimation;
+ friend class GenericFlingAnimation;
+ friend class OverscrollAnimation;
+ friend class SmoothScrollAnimation;
+ friend class WheelScrollAnimation;
+
+ friend class GenericOverscrollEffect;
+ friend class WidgetOverscrollEffect;
+
+ // The initial velocity of the most recent fling.
+ ParentLayerPoint mLastFlingVelocity;
+ // The time at which the most recent fling started.
+ TimeStamp mLastFlingTime;
+ // Indicates if the repaint-during-pinch timer is currently set
+ bool mPinchPaintTimerSet;
+
+ // Deal with overscroll resulting from a fling animation. This is only ever
+ // called on APZC instances that were actually performing a fling.
+ // The overscroll is handled by trying to hand the fling off to an APZC
+ // later in the handoff chain, or if there are no takers, continuing the
+ // fling and entering an overscrolled state.
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc);
+
+ void HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity);
+
+ // Helper function used by AttemptFling().
+ void AcceptFling(FlingHandoffState& aHandoffState);
+
+ // Start an overscroll animation with the given initial velocity.
+ void StartOverscrollAnimation(const ParentLayerPoint& aVelocity);
+
+ void SmoothScrollTo(const CSSPoint& aDestination);
+
+ // Returns whether overscroll is allowed during an event.
+ bool AllowScrollHandoffInCurrentBlock() const;
+
+ // Invoked by the pinch repaint timer.
+ void DoDelayedRequestContentRepaint();
+
+ /* ===================================================================
+ * The functions and members in this section are used to make ancestor chains
+ * out of APZC instances. These chains can only be walked or manipulated
+ * while holding the lock in the associated APZCTreeManager instance.
+ */
+public:
+ void SetParent(AsyncPanZoomController* aParent) {
+ mParent = aParent;
+ }
+
+ AsyncPanZoomController* GetParent() const {
+ return mParent;
+ }
+
+ /* Returns true if there is no APZC higher in the tree with the same
+ * layers id.
+ */
+ bool HasNoParentWithSameLayersId() const {
+ return !mParent || (mParent->mLayersId != mLayersId);
+ }
+
+ bool IsRootForLayersId() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mScrollMetadata.IsLayersIdRoot();
+ }
+
+ bool IsRootContent() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics.IsRootContent();
+ }
+
+private:
+ // |mTreeManager| belongs in this section but it's declaration is a bit
+ // further above due to initialization-order constraints.
+
+ RefPtr<AsyncPanZoomController> mParent;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for scrolling,
+ * including handing off scroll to another APZC, and overscrolling.
+ */
+public:
+ FrameMetrics::ViewID GetScrollHandoffParentId() const {
+ return mScrollMetadata.GetScrollParentId();
+ }
+
+ /**
+ * Attempt to scroll in response to a touch-move from |aStartPoint| to
+ * |aEndPoint|, which are in our (transformed) screen coordinates.
+ * Due to overscroll handling, there may not actually have been a touch-move
+ * at these points, but this function will scroll as if there had been.
+ * If this attempt causes overscroll (i.e. the layer cannot be scrolled
+ * by the entire amount requested), the overscroll is passed back to the
+ * tree manager via APZCTreeManager::DispatchScroll(). If the tree manager
+ * does not find an APZC further in the handoff chain to accept the
+ * overscroll, and this APZC is pannable, this APZC enters an overscrolled
+ * state.
+ * |aOverscrollHandoffChain| and |aOverscrollHandoffChainIndex| are used by
+ * the tree manager to keep track of which APZC to hand off the overscroll
+ * to; this function increments the chain and the index and passes it on to
+ * APZCTreeManager::DispatchScroll() in the event of overscroll.
+ * Returns true iff. this APZC, or an APZC further down the
+ * handoff chain, accepted the scroll (possibly entering an overscrolled
+ * state). If this returns false, the caller APZC knows that it should enter
+ * an overscrolled state itself if it can.
+ * aStartPoint and aEndPoint are modified depending on how much of the
+ * scroll gesture was consumed by APZCs in the handoff chain.
+ */
+ bool AttemptScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ void FlushRepaintForOverscrollHandoff();
+
+ /**
+ * If overscrolled, start a snap-back animation and return true.
+ * Otherwise return false.
+ */
+ bool SnapBackIfOverscrolled();
+
+ /**
+ * Build the chain of APZCs along which scroll will be handed off when
+ * this APZC receives input events.
+ *
+ * Notes on lifetime and const-correctness:
+ * - The returned handoff chain is |const|, to indicate that it cannot be
+ * changed after being built.
+ * - When passing the chain to a function that uses it without storing it,
+ * pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
+ * - When storing the chain, store it by RefPtr-to-const (as in
+ * |RefPtr<const OverscrollHandoffChain>|). This ensures the chain is
+ * kept alive. Note that queueing a task that uses the chain as an
+ * argument constitutes storing, as the task may outlive its queuer.
+ * - When passing the chain to a function that will store it, pass it as
+ * |const RefPtr<const OverscrollHandoffChain>&|. This allows the
+ * function to copy it into the |RefPtr<const OverscrollHandoffChain>|
+ * that will store it, while avoiding an unnecessary copy (and thus
+ * AddRef() and Release()) when passing it.
+ */
+ RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
+
+private:
+ /**
+ * A helper function for calling APZCTreeManager::DispatchScroll().
+ * Guards against the case where the APZC is being concurrently destroyed
+ * (and thus mTreeManager is being nulled out).
+ */
+ void CallDispatchScroll(ParentLayerPoint& aStartPoint,
+ ParentLayerPoint& aEndPoint,
+ OverscrollHandoffState& aOverscrollHandoffState);
+
+ /**
+ * A helper function for overscrolling during panning. This is a wrapper
+ * around OverscrollBy() that also implements restrictions on entering
+ * overscroll based on the pan angle.
+ */
+ void OverscrollForPanning(ParentLayerPoint& aOverscroll,
+ const ScreenPoint& aPanDistance);
+
+ /**
+ * Try to overscroll by 'aOverscroll'.
+ * If we are pannable on a particular axis, that component of 'aOverscroll'
+ * is transferred to any existing overscroll.
+ */
+ void OverscrollBy(ParentLayerPoint& aOverscroll);
+
+
+ /* ===================================================================
+ * The functions and members in this section are used to maintain the
+ * area that this APZC instance is responsible for. This is used when
+ * hit-testing to see which APZC instance should handle touch events.
+ */
+public:
+ void SetAncestorTransform(const Matrix4x4& aTransformToLayer) {
+ mAncestorTransform = aTransformToLayer;
+ }
+
+ Matrix4x4 GetAncestorTransform() const {
+ return mAncestorTransform;
+ }
+
+ // Returns whether or not this apzc contains the given screen point within
+ // its composition bounds.
+ bool Contains(const ScreenIntPoint& aPoint) const;
+
+ bool IsOverscrolled() const {
+ return mX.IsOverscrolled() || mY.IsOverscrolled();
+ }
+
+ bool IsInPanningState() const;
+
+private:
+ /* This is the cumulative CSS transform for all the layers from (and including)
+ * the parent APZC down to (but excluding) this one, and excluding any
+ * perspective transforms. */
+ Matrix4x4 mAncestorTransform;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for sharing the
+ * FrameMetrics across processes for the progressive tiling code.
+ */
+private:
+ /* Unique id assigned to each APZC. Used with ViewID to uniquely identify
+ * shared FrameMeterics used in progressive tile painting. */
+ const uint32_t mAPZCId;
+
+ RefPtr<ipc::SharedMemoryBasic> mSharedFrameMetricsBuffer;
+ CrossProcessMutex* mSharedLock;
+ /**
+ * Called when ever mFrameMetrics is updated so that if it is being
+ * shared with the content process the shared FrameMetrics may be updated.
+ */
+ void UpdateSharedCompositorFrameMetrics();
+ /**
+ * Create a shared memory buffer for containing the FrameMetrics and
+ * a CrossProcessMutex that may be shared with the content process
+ * for use in progressive tiled update calculations.
+ */
+ void ShareCompositorFrameMetrics();
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for testing
+ * and assertion purposes only.
+ */
+public:
+ /**
+ * Set an extra offset for testing async scrolling.
+ */
+ void SetTestAsyncScrollOffset(const CSSPoint& aPoint)
+ {
+ mTestAsyncScrollOffset = aPoint;
+ }
+ /**
+ * Set an extra offset for testing async scrolling.
+ */
+ void SetTestAsyncZoom(const LayerToParentLayerScale& aZoom)
+ {
+ mTestAsyncZoom = aZoom;
+ }
+
+ void MarkAsyncTransformAppliedToContent()
+ {
+ mAsyncTransformAppliedToContent = true;
+ }
+
+ bool GetAsyncTransformAppliedToContent() const
+ {
+ return mAsyncTransformAppliedToContent;
+ }
+
+ uint64_t GetLayersId() const
+ {
+ return mLayersId;
+ }
+
+private:
+ // Extra offset to add to the async scroll position for testing
+ CSSPoint mTestAsyncScrollOffset;
+ // Extra zoom to include in the aync zoom for testing
+ LayerToParentLayerScale mTestAsyncZoom;
+ // Flag to track whether or not the APZ transform is not used. This
+ // flag is recomputed for every composition frame.
+ bool mAsyncTransformAppliedToContent;
+
+
+ /* ===================================================================
+ * The functions and members in this section are used for checkerboard
+ * recording.
+ */
+private:
+ // Helper function to update the in-progress checkerboard event, if any.
+ void UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+ uint32_t aMagnitude);
+
+ // Mutex protecting mCheckerboardEvent
+ Mutex mCheckerboardEventLock;
+ // This is created when this APZC instance is first included as part of a
+ // composite. If a checkerboard event takes place, this is destroyed at the
+ // end of the event, and a new one is created on the next composite.
+ UniquePtr<CheckerboardEvent> mCheckerboardEvent;
+ // This is used to track the total amount of time that we could reasonably
+ // be checkerboarding. Combined with other info, this allows us to meaningfully
+ // say how frequently users actually encounter checkerboarding.
+ PotentialCheckerboardDurationTracker mPotentialCheckerboardTracker;
+
+
+ /* ===================================================================
+ * The functions in this section are used for CSS scroll snapping.
+ */
+
+ // If |aEvent| should trigger scroll snapping, adjust |aDelta| to reflect
+ // the snapping (that is, make it a delta that will take us to the desired
+ // snap point). The delta is interpreted as being relative to
+ // |aStartPosition|, and if a target snap point is found, |aStartPosition|
+ // is also updated, to the value of the snap point.
+ // Returns true iff. a target snap point was found.
+ bool MaybeAdjustDeltaForScrollSnapping(const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition);
+
+ // Snap to a snap position nearby the current scroll position, if appropriate.
+ void ScrollSnap();
+
+ // Snap to a snap position nearby the destination predicted based on the
+ // current velocity, if appropriate.
+ void ScrollSnapToDestination();
+
+ // Snap to a snap position nearby the provided destination, if appropriate.
+ void ScrollSnapNear(const CSSPoint& aDestination);
+
+ // Find a snap point near |aDestination| that we should snap to.
+ // Returns the snap point if one was found, or an empty Maybe otherwise.
+ // |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
+ // GetSnapPointForDestination). It should generally be determined by the
+ // type of event that's triggering the scroll.
+ Maybe<CSSPoint> FindSnapPointNear(const CSSPoint& aDestination,
+ nsIScrollableFrame::ScrollUnit aUnit);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PanZoomController_h
diff --git a/gfx/layers/apz/src/Axis.cpp b/gfx/layers/apz/src/Axis.cpp
new file mode 100644
index 000000000..ddd660e0b
--- /dev/null
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Axis.h"
+#include <math.h> // for fabsf, pow, powf
+#include <algorithm> // for max
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/Attributes.h" // for final
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/gfx/Rect.h" // for RoundedIn
+#include "mozilla/mozalloc.h" // for operator new
+#include "mozilla/FloatingPoint.h" // for FuzzyEqualsAdditive
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "nsMathUtils.h" // for NS_lround
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc
+#include "nscore.h" // for NS_IMETHOD
+#include "gfxPrefs.h" // for the preferences
+
+#define AXIS_LOG(...)
+// #define AXIS_LOG(...) printf_stderr("AXIS: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+// When we compute the velocity we do so by taking two input events and
+// dividing the distance delta over the time delta. In some cases the time
+// delta can be really small, which can make the velocity computation very
+// volatile. To avoid this we impose a minimum time delta below which we do
+// not recompute the velocity.
+const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
+
+bool FuzzyEqualsCoordinate(float aValue1, float aValue2)
+{
+ return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON)
+ || FuzzyEqualsMultiplicative(aValue1, aValue2);
+}
+
+extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
+
+Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
+ : mPos(0),
+ mVelocitySampleTimeMs(0),
+ mVelocitySamplePos(0),
+ mVelocity(0.0f),
+ mAxisLocked(false),
+ mAsyncPanZoomController(aAsyncPanZoomController),
+ mOverscroll(0),
+ mFirstOverscrollAnimationSample(0),
+ mLastOverscrollPeak(0),
+ mOverscrollScale(1.0f)
+{
+}
+
+float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const {
+ ScreenPoint velocity = MakePoint(aVelocityInchesPerMs * APZCTreeManager::GetDPI());
+ // Use ToScreenCoordinates() to convert a point rather than a vector by
+ // treating the point as a vector, and using (0, 0) as the anchor.
+ ScreenPoint panStart = mAsyncPanZoomController->ToScreenCoordinates(
+ mAsyncPanZoomController->PanStart(),
+ ParentLayerPoint());
+ ParentLayerPoint localVelocity =
+ mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
+ return localVelocity.Length();
+}
+
+void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
+ // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
+ // We still update mPos so that the positioning is correct (and we don't run
+ // into problems like bug 1042734) but the velocity will remain where it was.
+ // In particular we don't update either mVelocitySampleTimeMs or
+ // mVelocitySamplePos so that eventually when we do get an event with the
+ // required time delta we use the corresponding distance delta as well.
+ AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
+ mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs));
+ mPos = aPos;
+ return;
+ }
+
+ float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
+
+ newVelocity = ApplyFlingCurveToVelocity(newVelocity);
+
+ AXIS_LOG("%p|%s updating velocity to %f with touch\n",
+ mAsyncPanZoomController, Name(), newVelocity);
+ mVelocity = newVelocity;
+ mPos = aPos;
+ mVelocitySampleTimeMs = aTimestampMs;
+ mVelocitySamplePos = aPos;
+
+ AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+float Axis::ApplyFlingCurveToVelocity(float aVelocity) const {
+ float newVelocity = aVelocity;
+ if (gfxPrefs::APZMaxVelocity() > 0.0f) {
+ bool velocityIsNegative = (newVelocity < 0);
+ newVelocity = fabs(newVelocity);
+
+ float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity());
+ newVelocity = std::min(newVelocity, maxVelocity);
+
+ if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
+ float curveThreshold = ToLocalVelocity(gfxPrefs::APZCurveThreshold());
+ if (newVelocity > curveThreshold) {
+ // here, 0 < curveThreshold < newVelocity <= maxVelocity, so we apply the curve
+ float scale = maxVelocity - curveThreshold;
+ float funcInput = (newVelocity - curveThreshold) / scale;
+ float funcOutput =
+ gVelocityCurveFunction->GetValue(funcInput,
+ ComputedTimingFunction::BeforeFlag::Unset);
+ float curvedVelocity = (funcOutput * scale) + curveThreshold;
+ AXIS_LOG("%p|%s curving up velocity from %f to %f\n",
+ mAsyncPanZoomController, Name(), newVelocity, curvedVelocity);
+ newVelocity = curvedVelocity;
+ }
+ }
+
+ if (velocityIsNegative) {
+ newVelocity = -newVelocity;
+ }
+ }
+
+ return newVelocity;
+}
+
+void Axis::AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity) {
+ mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity));
+ if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
+ mVelocityQueue.RemoveElementAt(0);
+ }
+}
+
+void Axis::HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ mVelocity = ApplyFlingCurveToVelocity(aSpeed);
+ mVelocitySampleTimeMs = aTimestampMs;
+
+ AddVelocityToQueue(aTimestampMs, mVelocity);
+}
+
+void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
+ mStartPos = aPos;
+ mPos = aPos;
+ mVelocitySampleTimeMs = aTimestampMs;
+ mVelocitySamplePos = aPos;
+ mAxisLocked = false;
+}
+
+bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
+ /* ParentLayerCoord */ float& aDisplacementOut,
+ /* ParentLayerCoord */ float& aOverscrollAmountOut,
+ bool aForceOverscroll /* = false */)
+{
+ if (mAxisLocked) {
+ aOverscrollAmountOut = 0;
+ aDisplacementOut = 0;
+ return false;
+ }
+ if (aForceOverscroll) {
+ aOverscrollAmountOut = aDisplacement;
+ aDisplacementOut = 0;
+ return false;
+ }
+
+ EndOverscrollAnimation();
+
+ ParentLayerCoord displacement = aDisplacement;
+
+ // First consume any overscroll in the opposite direction along this axis.
+ ParentLayerCoord consumedOverscroll = 0;
+ if (mOverscroll > 0 && aDisplacement < 0) {
+ consumedOverscroll = std::min(mOverscroll, -aDisplacement);
+ } else if (mOverscroll < 0 && aDisplacement > 0) {
+ consumedOverscroll = 0.f - std::min(-mOverscroll, aDisplacement);
+ }
+ mOverscroll -= consumedOverscroll;
+ displacement += consumedOverscroll;
+
+ // Split the requested displacement into an allowed displacement that does
+ // not overscroll, and an overscroll amount.
+ aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
+ if (aOverscrollAmountOut != 0.0f) {
+ // No need to have a velocity along this axis anymore; it won't take us
+ // anywhere, so we're just spinning needlessly.
+ AXIS_LOG("%p|%s has overscrolled, clearing velocity\n",
+ mAsyncPanZoomController, Name());
+ mVelocity = 0.0f;
+ displacement -= aOverscrollAmountOut;
+ }
+ aDisplacementOut = displacement;
+ return fabsf(consumedOverscroll) > EPSILON;
+}
+
+ParentLayerCoord Axis::ApplyResistance(ParentLayerCoord aRequestedOverscroll) const {
+ // 'resistanceFactor' is a value between 0 and 1, which:
+ // - tends to 1 as the existing overscroll tends to 0
+ // - tends to 0 as the existing overscroll tends to the composition length
+ // The actual overscroll is the requested overscroll multiplied by this
+ // factor; this should prevent overscrolling by more than the composition
+ // length.
+ float resistanceFactor = 1 - fabsf(GetOverscroll()) / GetCompositionLength();
+ return resistanceFactor < 0 ? ParentLayerCoord(0) : aRequestedOverscroll * resistanceFactor;
+}
+
+void Axis::OverscrollBy(ParentLayerCoord aOverscroll) {
+ MOZ_ASSERT(CanScroll());
+ // We can get some spurious calls to OverscrollBy() with near-zero values
+ // due to rounding error. Ignore those (they might trip the asserts below.)
+ if (FuzzyEqualsAdditive(aOverscroll.value, 0.0f, COORDINATE_EPSILON)) {
+ return;
+ }
+ EndOverscrollAnimation();
+ aOverscroll = ApplyResistance(aOverscroll);
+ if (aOverscroll > 0) {
+#ifdef DEBUG
+ if (!FuzzyEqualsCoordinate(GetCompositionEnd().value, GetPageEnd().value)) {
+ nsPrintfCString message("composition end (%f) is not equal (within error) to page end (%f)\n",
+ GetCompositionEnd().value, GetPageEnd().value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue > 0");
+ }
+#endif
+ MOZ_ASSERT(mOverscroll >= 0);
+ } else if (aOverscroll < 0) {
+#ifdef DEBUG
+ if (!FuzzyEqualsCoordinate(GetOrigin().value, GetPageStart().value)) {
+ nsPrintfCString message("composition origin (%f) is not equal (within error) to page origin (%f)\n",
+ GetOrigin().value, GetPageStart().value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue < 0");
+ }
+#endif
+ MOZ_ASSERT(mOverscroll <= 0);
+ }
+ mOverscroll += aOverscroll;
+}
+
+ParentLayerCoord Axis::GetOverscroll() const {
+ ParentLayerCoord result = (mOverscroll - mLastOverscrollPeak) / mOverscrollScale;
+
+ // Assert that we return overscroll in the correct direction
+#ifdef DEBUG
+ if ((result.value * mFirstOverscrollAnimationSample.value) < 0.0f) {
+ nsPrintfCString message("GetOverscroll() (%f) and first overscroll animation sample (%f) have different signs\n",
+ result.value, mFirstOverscrollAnimationSample.value);
+ NS_ASSERTION(false, message.get());
+ MOZ_CRASH("GFX: Overscroll issue");
+ }
+#endif
+
+ return result;
+}
+
+void Axis::StartOverscrollAnimation(float aVelocity) {
+ // Make sure any state from a previous animation has been cleared.
+ MOZ_ASSERT(mFirstOverscrollAnimationSample == 0 &&
+ mLastOverscrollPeak == 0 &&
+ mOverscrollScale == 1);
+
+ SetVelocity(aVelocity);
+}
+
+void Axis::EndOverscrollAnimation() {
+ ParentLayerCoord overscroll = GetOverscroll();
+ mFirstOverscrollAnimationSample = 0;
+ mLastOverscrollPeak = 0;
+ mOverscrollScale = 1.0f;
+ mOverscroll = overscroll;
+}
+
+void Axis::StepOverscrollAnimation(double aStepDurationMilliseconds) {
+ // Apply spring physics to the overscroll as time goes on.
+ // Note: this method of sampling isn't perfectly smooth, as it assumes
+ // a constant velocity over 'aDelta', instead of an accelerating velocity.
+ // (The way we applying friction to flings has the same issue.)
+ // Hooke's law with damping:
+ // F = -kx - bv
+ // where
+ // k is a constant related to the stiffness of the spring
+ // The larger the constant, the stiffer the spring.
+ // x is the displacement of the end of the spring from its equilibrium
+ // In our scenario, it's the amount of overscroll on the axis.
+ // b is a constant that provides damping (friction)
+ // v is the velocity of the point at the end of the spring
+ // See http://gafferongames.com/game-physics/spring-physics/
+ const float kSpringStiffness = gfxPrefs::APZOverscrollSpringStiffness();
+ const float kSpringFriction = gfxPrefs::APZOverscrollSpringFriction();
+
+ // Apply spring force.
+ float springForce = -1 * kSpringStiffness * mOverscroll;
+ // Assume unit mass, so force = acceleration.
+ float oldVelocity = mVelocity;
+ mVelocity += springForce * aStepDurationMilliseconds;
+
+ // Apply dampening.
+ mVelocity *= pow(double(1 - kSpringFriction), aStepDurationMilliseconds);
+ AXIS_LOG("%p|%s sampled overscroll animation, leaving velocity at %f\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+
+ // At the peak of each oscillation, record new offset and scaling factors for
+ // overscroll, to ensure that GetOverscroll always returns a value of the
+ // same sign, and that this value is correctly adjusted as the spring is
+ // dampened.
+ // To handle the case where one of the velocity samples is exaclty zero,
+ // consider a sign change to have occurred when the outgoing velocity is zero.
+ bool velocitySignChange = (oldVelocity * mVelocity) < 0 || mVelocity == 0;
+ if (mFirstOverscrollAnimationSample == 0.0f) {
+ mFirstOverscrollAnimationSample = mOverscroll;
+
+ // It's possible to start sampling overscroll with velocity == 0, or
+ // velocity in the opposite direction of overscroll, so make sure we
+ // correctly record the peak in this case.
+ if (mOverscroll != 0 && ((mOverscroll > 0 ? oldVelocity : -oldVelocity) <= 0.0f)) {
+ velocitySignChange = true;
+ }
+ }
+ if (velocitySignChange) {
+ bool oddOscillation = (mOverscroll.value * mFirstOverscrollAnimationSample.value) < 0.0f;
+ mLastOverscrollPeak = oddOscillation ? mOverscroll : -mOverscroll;
+ mOverscrollScale = 2.0f;
+ }
+
+ // Adjust the amount of overscroll based on the velocity.
+ // Note that we allow for oscillations.
+ mOverscroll += (mVelocity * aStepDurationMilliseconds);
+
+ // Our mechanism for translating a set of mOverscroll values that oscillate
+ // around zero to a set of GetOverscroll() values that have the same sign
+ // (so content is always stretched, never compressed) assumes that
+ // mOverscroll does not exceed mLastOverscrollPeak in magnitude. If our
+ // calculations were exact, this would be the case, as a dampened spring
+ // should never attain a displacement greater in magnitude than a previous
+ // peak. In our approximation calculations, however, this may not hold
+ // exactly. To ensure the assumption is not violated, we clamp the magnitude
+ // of mOverscroll.
+ if (mLastOverscrollPeak != 0 && fabs(mOverscroll) > fabs(mLastOverscrollPeak)) {
+ mOverscroll = (mOverscroll >= 0) ? fabs(mLastOverscrollPeak) : -fabs(mLastOverscrollPeak);
+ }
+}
+
+bool Axis::SampleOverscrollAnimation(const TimeDuration& aDelta) {
+ // Short-circuit early rather than running through all the sampling code.
+ if (mVelocity == 0.0f && mOverscroll == 0.0f) {
+ return false;
+ }
+
+ // We approximate the curve traced out by the velocity of the spring
+ // over time by breaking up the curve into small segments over which we
+ // consider the velocity to be constant. If the animation is sampled
+ // sufficiently often, then treating |aDelta| as a single segment of this
+ // sort would be fine, but the frequency at which the animation is sampled
+ // can be affected by external factors, and as the segment size grows larger,
+ // the approximation gets worse and the approximated curve can even diverge
+ // (i.e. oscillate forever, with displacements of increasing absolute value)!
+ // To avoid this, we break up |aDelta| into smaller segments of length 1 ms
+ // each, and a segment of any remaining fractional milliseconds.
+ double milliseconds = aDelta.ToMilliseconds();
+ int wholeMilliseconds = (int) aDelta.ToMilliseconds();
+ double fractionalMilliseconds = milliseconds - wholeMilliseconds;
+ for (int i = 0; i < wholeMilliseconds; ++i) {
+ StepOverscrollAnimation(1);
+ }
+ StepOverscrollAnimation(fractionalMilliseconds);
+
+ // If both the velocity and the displacement fall below a threshold, stop
+ // the animation so we don't continue doing tiny oscillations that aren't
+ // noticeable.
+ if (fabs(mOverscroll) < gfxPrefs::APZOverscrollStopDistanceThreshold() &&
+ fabs(mVelocity) < gfxPrefs::APZOverscrollStopVelocityThreshold()) {
+ // "Jump" to the at-rest state. The jump shouldn't be noticeable as the
+ // velocity and overscroll are already low.
+ AXIS_LOG("%p|%s oscillation dropped below threshold, going to rest\n",
+ mAsyncPanZoomController, Name());
+ ClearOverscroll();
+ mVelocity = 0;
+ return false;
+ }
+
+ // Otherwise, continue the animation.
+ return true;
+}
+
+bool Axis::IsOverscrolled() const {
+ return mOverscroll != 0.f;
+}
+
+void Axis::ClearOverscroll() {
+ EndOverscrollAnimation();
+ mOverscroll = 0;
+}
+
+ParentLayerCoord Axis::PanStart() const {
+ return mStartPos;
+}
+
+ParentLayerCoord Axis::PanDistance() const {
+ return fabs(mPos - mStartPos);
+}
+
+ParentLayerCoord Axis::PanDistance(ParentLayerCoord aPos) const {
+ return fabs(aPos - mStartPos);
+}
+
+void Axis::EndTouch(uint32_t aTimestampMs) {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ mAxisLocked = false;
+ mVelocity = 0;
+ int count = 0;
+ while (!mVelocityQueue.IsEmpty()) {
+ uint32_t timeDelta = (aTimestampMs - mVelocityQueue[0].first);
+ if (timeDelta < gfxPrefs::APZVelocityRelevanceTime()) {
+ count++;
+ mVelocity += mVelocityQueue[0].second;
+ }
+ mVelocityQueue.RemoveElementAt(0);
+ }
+ if (count > 1) {
+ mVelocity /= count;
+ }
+ AXIS_LOG("%p|%s ending touch, computed velocity %f\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+}
+
+void Axis::CancelGesture() {
+ // mVelocityQueue is controller-thread only
+ APZThreadUtils::AssertOnControllerThread();
+
+ AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n",
+ mAsyncPanZoomController, Name());
+ mVelocity = 0.0f;
+ while (!mVelocityQueue.IsEmpty()) {
+ mVelocityQueue.RemoveElementAt(0);
+ }
+}
+
+bool Axis::CanScroll() const {
+ return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
+}
+
+bool Axis::CanScroll(ParentLayerCoord aDelta) const
+{
+ if (!CanScroll() || mAxisLocked) {
+ return false;
+ }
+
+ return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) > COORDINATE_EPSILON;
+}
+
+CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const
+{
+ CSSToParentLayerScale zoom = GetScaleForAxis(GetFrameMetrics().GetZoom());
+ ParentLayerCoord origin = aOrigin * zoom;
+
+ ParentLayerCoord result;
+ if (origin < GetPageStart()) {
+ result = GetPageStart();
+ } else if (origin + GetCompositionLength() > GetPageEnd()) {
+ result = GetPageEnd() - GetCompositionLength();
+ } else {
+ return aOrigin;
+ }
+
+ return result / zoom;
+}
+
+bool Axis::CanScrollNow() const {
+ return !mAxisLocked && CanScroll();
+}
+
+bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta,
+ float aFriction,
+ float aThreshold) {
+ if (fabsf(mVelocity) <= aThreshold) {
+ // If the velocity is very low, just set it to 0 and stop the fling,
+ // otherwise we'll just asymptotically approach 0 and the user won't
+ // actually see any changes.
+ mVelocity = 0.0f;
+ return false;
+ } else {
+ mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
+ }
+ AXIS_LOG("%p|%s reduced velocity to %f due to friction\n",
+ mAsyncPanZoomController, Name(), mVelocity);
+ return true;
+}
+
+ParentLayerCoord Axis::DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const {
+ ParentLayerCoord newOrigin = GetOrigin() + aDisplacement;
+ ParentLayerCoord newCompositionEnd = GetCompositionEnd() + aDisplacement;
+ // If the current pan plus a displacement takes the window to the left of or
+ // above the current page rect.
+ bool minus = newOrigin < GetPageStart();
+ // If the current pan plus a displacement takes the window to the right of or
+ // below the current page rect.
+ bool plus = newCompositionEnd > GetPageEnd();
+ if (minus && plus) {
+ // Don't handle overscrolled in both directions; a displacement can't cause
+ // this, it must have already been zoomed out too far.
+ return 0;
+ }
+ if (minus) {
+ return newOrigin - GetPageStart();
+ }
+ if (plus) {
+ return newCompositionEnd - GetPageEnd();
+ }
+ return 0;
+}
+
+CSSCoord Axis::ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const {
+ // Internally, do computations in ParentLayer coordinates *before* the scale
+ // is applied.
+ CSSToParentLayerScale zoom = GetFrameMetrics().GetZoom().ToScaleFactor();
+ ParentLayerCoord focus = aFocus * zoom;
+ ParentLayerCoord originAfterScale = (GetOrigin() + focus) - (focus / aScale);
+
+ bool both = ScaleWillOverscrollBothSides(aScale);
+ bool minus = GetPageStart() - originAfterScale > COORDINATE_EPSILON;
+ bool plus = (originAfterScale + (GetCompositionLength() / aScale)) - GetPageEnd() > COORDINATE_EPSILON;
+
+ if ((minus && plus) || both) {
+ // If we ever reach here it's a bug in the client code.
+ MOZ_ASSERT(false, "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount");
+ return 0;
+ }
+ if (minus) {
+ return (originAfterScale - GetPageStart()) / zoom;
+ }
+ if (plus) {
+ return (originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd()) / zoom;
+ }
+ return 0;
+}
+
+bool Axis::IsAxisLocked() const {
+ return mAxisLocked;
+}
+
+float Axis::GetVelocity() const {
+ return mAxisLocked ? 0 : mVelocity;
+}
+
+void Axis::SetVelocity(float aVelocity) {
+ AXIS_LOG("%p|%s direct-setting velocity to %f\n",
+ mAsyncPanZoomController, Name(), aVelocity);
+ mVelocity = aVelocity;
+}
+
+ParentLayerCoord Axis::GetCompositionEnd() const {
+ return GetOrigin() + GetCompositionLength();
+}
+
+ParentLayerCoord Axis::GetPageEnd() const {
+ return GetPageStart() + GetPageLength();
+}
+
+ParentLayerCoord Axis::GetScrollRangeEnd() const {
+ return GetPageEnd() - GetCompositionLength();
+}
+
+ParentLayerCoord Axis::GetOrigin() const {
+ ParentLayerPoint origin = GetFrameMetrics().GetScrollOffset() * GetFrameMetrics().GetZoom();
+ return GetPointOffset(origin);
+}
+
+ParentLayerCoord Axis::GetCompositionLength() const {
+ return GetRectLength(GetFrameMetrics().GetCompositionBounds());
+}
+
+ParentLayerCoord Axis::GetPageStart() const {
+ ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom();
+ return GetRectOffset(pageRect);
+}
+
+ParentLayerCoord Axis::GetPageLength() const {
+ ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom();
+ return GetRectLength(pageRect);
+}
+
+bool Axis::ScaleWillOverscrollBothSides(float aScale) const {
+ const FrameMetrics& metrics = GetFrameMetrics();
+ ParentLayerRect screenCompositionBounds = metrics.GetCompositionBounds()
+ / ParentLayerToParentLayerScale(aScale);
+ return GetRectLength(screenCompositionBounds) - GetPageLength() > COORDINATE_EPSILON;
+}
+
+const FrameMetrics& Axis::GetFrameMetrics() const {
+ return mAsyncPanZoomController->GetFrameMetrics();
+}
+
+
+AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
+ : Axis(aAsyncPanZoomController)
+{
+
+}
+
+ParentLayerCoord AxisX::GetPointOffset(const ParentLayerPoint& aPoint) const
+{
+ return aPoint.x;
+}
+
+ParentLayerCoord AxisX::GetRectLength(const ParentLayerRect& aRect) const
+{
+ return aRect.width;
+}
+
+ParentLayerCoord AxisX::GetRectOffset(const ParentLayerRect& aRect) const
+{
+ return aRect.x;
+}
+
+CSSToParentLayerScale AxisX::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const
+{
+ return CSSToParentLayerScale(aScale.xScale);
+}
+
+ScreenPoint AxisX::MakePoint(ScreenCoord aCoord) const
+{
+ return ScreenPoint(aCoord, 0);
+}
+
+const char* AxisX::Name() const
+{
+ return "X";
+}
+
+AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
+ : Axis(aAsyncPanZoomController)
+{
+
+}
+
+ParentLayerCoord AxisY::GetPointOffset(const ParentLayerPoint& aPoint) const
+{
+ return aPoint.y;
+}
+
+ParentLayerCoord AxisY::GetRectLength(const ParentLayerRect& aRect) const
+{
+ return aRect.height;
+}
+
+ParentLayerCoord AxisY::GetRectOffset(const ParentLayerRect& aRect) const
+{
+ return aRect.y;
+}
+
+CSSToParentLayerScale AxisY::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const
+{
+ return CSSToParentLayerScale(aScale.yScale);
+}
+
+ScreenPoint AxisY::MakePoint(ScreenCoord aCoord) const
+{
+ return ScreenPoint(0, aCoord);
+}
+
+const char* AxisY::Name() const
+{
+ return "Y";
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/Axis.h b/gfx/layers/apz/src/Axis.h
new file mode 100644
index 000000000..e4c6b5644
--- /dev/null
+++ b/gfx/layers/apz/src/Axis.h
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Axis_h
+#define mozilla_layers_Axis_h
+
+#include <sys/types.h> // for int32_t
+#include "APZUtils.h"
+#include "Units.h"
+#include "mozilla/TimeStamp.h" // for TimeDuration
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+
+const float EPSILON = 0.0001f;
+
+/**
+ * Compare two coordinates for equality, accounting for rounding error.
+ * Use both FuzzyEqualsAdditive() with COORDINATE_EPISLON, which accounts for
+ * things like the error introduced by rounding during a round-trip to app
+ * units, and FuzzyEqualsMultiplicative(), which accounts for accumulated error
+ * due to floating-point operations (which can be larger than COORDINATE_EPISLON
+ * for sufficiently large coordinate values).
+ */
+bool FuzzyEqualsCoordinate(float aValue1, float aValue2);
+
+struct FrameMetrics;
+class AsyncPanZoomController;
+
+/**
+ * Helper class to maintain each axis of movement (X,Y) for panning and zooming.
+ * Note that everything here is specific to one axis; that is, the X axis knows
+ * nothing about the Y axis and vice versa.
+ */
+class Axis {
+public:
+ explicit Axis(AsyncPanZoomController* aAsyncPanZoomController);
+
+ /**
+ * Notify this Axis that a new touch has been received, including a timestamp
+ * for when the touch was received. This triggers a recalculation of velocity.
+ * This can also used for pan gesture events. For those events, the "touch"
+ * location is stationary and the scroll displacement is passed in as
+ * aAdditionalDelta.
+ */
+ void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs);
+
+protected:
+ float ApplyFlingCurveToVelocity(float aVelocity) const;
+ void AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity);
+
+public:
+ void HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed);
+
+ /**
+ * Notify this Axis that a touch has begun, i.e. the user has put their finger
+ * on the screen but has not yet tried to pan.
+ */
+ void StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs);
+
+ /**
+ * Notify this Axis that a touch has ended gracefully. This may perform
+ * recalculations of the axis velocity.
+ */
+ void EndTouch(uint32_t aTimestampMs);
+
+ /**
+ * Notify this Axis that the gesture has ended forcefully. Useful for stopping
+ * flings when a user puts their finger down in the middle of one (i.e. to
+ * stop a previous touch including its fling so that a new one can take its
+ * place).
+ */
+ void CancelGesture();
+
+ /**
+ * Takes a requested displacement to the position of this axis, and adjusts it
+ * to account for overscroll (which might decrease the displacement; this is
+ * to prevent the viewport from overscrolling the page rect), and axis locking
+ * (which might prevent any displacement from happening). If overscroll
+ * ocurred, its amount is written to |aOverscrollAmountOut|.
+ * The |aDisplacementOut| parameter is set to the adjusted
+ * displacement, and the function returns true iff internal overscroll amounts
+ * were changed.
+ */
+ bool AdjustDisplacement(ParentLayerCoord aDisplacement,
+ /* ParentLayerCoord */ float& aDisplacementOut,
+ /* ParentLayerCoord */ float& aOverscrollAmountOut,
+ bool aForceOverscroll = false);
+
+ /**
+ * Overscrolls this axis by the requested amount in the requested direction.
+ * The axis must be at the end of its scroll range in this direction.
+ */
+ void OverscrollBy(ParentLayerCoord aOverscroll);
+
+ /**
+ * Return the amount of overscroll on this axis, in ParentLayer pixels.
+ *
+ * If this amount is nonzero, the relevant component of
+ * mAsyncPanZoomController->mFrameMetrics.mScrollOffset must be at its
+ * extreme allowed value in the relevant direction (that is, it must be at
+ * its maximum value if we are overscrolled at our composition length, and
+ * at its minimum value if we are overscrolled at the origin).
+ */
+ ParentLayerCoord GetOverscroll() const;
+
+ /**
+ * Start an overscroll animation with the given initial velocity.
+ */
+ void StartOverscrollAnimation(float aVelocity);
+
+ /**
+ * Sample the snap-back animation to relieve overscroll.
+ * |aDelta| is the time since the last sample.
+ */
+ bool SampleOverscrollAnimation(const TimeDuration& aDelta);
+
+ /**
+ * Stop an overscroll animation.
+ */
+ void EndOverscrollAnimation();
+
+ /**
+ * Return whether this axis is overscrolled in either direction.
+ */
+ bool IsOverscrolled() const;
+
+ /**
+ * Clear any overscroll amount on this axis.
+ */
+ void ClearOverscroll();
+
+ /**
+ * Gets the starting position of the touch supplied in StartTouch().
+ */
+ ParentLayerCoord PanStart() const;
+
+ /**
+ * Gets the distance between the starting position of the touch supplied in
+ * StartTouch() and the current touch from the last
+ * UpdateWithTouchAtDevicePoint().
+ */
+ ParentLayerCoord PanDistance() const;
+
+ /**
+ * Gets the distance between the starting position of the touch supplied in
+ * StartTouch() and the supplied position.
+ */
+ ParentLayerCoord PanDistance(ParentLayerCoord aPos) const;
+
+ /**
+ * Applies friction during a fling, or cancels the fling if the velocity is
+ * too low. Returns true if the fling should continue to another frame, or
+ * false if it should end.
+ * |aDelta| is the amount of time that has passed since the last time
+ * friction was applied.
+ * |aFriction| is the amount of friction to apply.
+ * |aThreshold| is the velocity below which the fling is cancelled.
+ */
+ bool FlingApplyFrictionOrCancel(const TimeDuration& aDelta,
+ float aFriction,
+ float aThreshold);
+
+ /**
+ * Returns true if the page has room to be scrolled along this axis.
+ */
+ bool CanScroll() const;
+
+ /**
+ * Returns whether this axis can scroll any more in a particular direction.
+ */
+ bool CanScroll(ParentLayerCoord aDelta) const;
+
+ /**
+ * Returns true if the page has room to be scrolled along this axis
+ * and this axis is not scroll-locked.
+ */
+ bool CanScrollNow() const;
+
+ /**
+ * Clamp a point to the page's scrollable bounds. That is, a scroll
+ * destination to the returned point will not contain any overscroll.
+ */
+ CSSCoord ClampOriginToScrollableRect(CSSCoord aOrigin) const;
+
+ void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; }
+
+ /**
+ * Gets the raw velocity of this axis at this moment.
+ */
+ float GetVelocity() const;
+
+ /**
+ * Sets the raw velocity of this axis at this moment.
+ * Intended to be called only when the axis "takes over" a velocity from
+ * another APZC, in which case there are no touch points available to call
+ * UpdateWithTouchAtDevicePoint. In other circumstances,
+ * UpdateWithTouchAtDevicePoint should be used and the velocity calculated
+ * there.
+ */
+ void SetVelocity(float aVelocity);
+
+ /**
+ * If a displacement will overscroll the axis, this returns the amount and in
+ * what direction.
+ */
+ ParentLayerCoord DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const;
+
+ /**
+ * If a scale will overscroll the axis, this returns the amount and in what
+ * direction.
+ *
+ * |aFocus| is the point at which the scale is focused at. We will offset the
+ * scroll offset in such a way that it remains in the same place on the page
+ * relative.
+ *
+ * Note: Unlike most other functions in Axis, this functions operates in
+ * CSS coordinates so there is no confusion as to whether the ParentLayer
+ * coordinates it operates in are before or after the scale is applied.
+ */
+ CSSCoord ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const;
+
+ /**
+ * Checks if an axis will overscroll in both directions by computing the
+ * content rect and checking that its height/width (depending on the axis)
+ * does not overextend past the viewport.
+ *
+ * This gets called by ScaleWillOverscroll().
+ */
+ bool ScaleWillOverscrollBothSides(float aScale) const;
+
+ /**
+ * Returns true if movement on this axis is locked.
+ */
+ bool IsAxisLocked() const;
+
+ ParentLayerCoord GetOrigin() const;
+ ParentLayerCoord GetCompositionLength() const;
+ ParentLayerCoord GetPageStart() const;
+ ParentLayerCoord GetPageLength() const;
+ ParentLayerCoord GetCompositionEnd() const;
+ ParentLayerCoord GetPageEnd() const;
+ ParentLayerCoord GetScrollRangeEnd() const;
+
+ ParentLayerCoord GetPos() const { return mPos; }
+
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const = 0;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const = 0;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const = 0;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const = 0;
+
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
+
+ virtual const char* Name() const = 0;
+
+protected:
+ ParentLayerCoord mPos;
+
+ // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
+ // used in the last velocity sampling. They get updated when a new sample is
+ // taken (which may not happen on every input event, if the time delta is too
+ // small).
+ uint32_t mVelocitySampleTimeMs;
+ ParentLayerCoord mVelocitySamplePos;
+
+ ParentLayerCoord mStartPos;
+ float mVelocity; // Units: ParentLayerCoords per millisecond
+ bool mAxisLocked; // Whether movement on this axis is locked.
+ AsyncPanZoomController* mAsyncPanZoomController;
+
+ // mOverscroll is the displacement of an oscillating spring from its resting
+ // state. The resting state moves as the overscroll animation progresses.
+ ParentLayerCoord mOverscroll;
+ // Used to record the initial overscroll when we start sampling for animation.
+ ParentLayerCoord mFirstOverscrollAnimationSample;
+ // These two variables are used in combination to make sure that
+ // GetOverscroll() never changes sign during animation. This is necessary,
+ // as mOverscroll itself oscillates around zero during animation.
+ // If we're not sampling overscroll animation, mOverscrollScale will be 1.0
+ // and mLastOverscrollPeak will be zero.
+ // If we are animating, after the overscroll reaches its peak,
+ // mOverscrollScale will be 2.0 and mLastOverscrollPeak will store the amount
+ // of overscroll at the last peak of the oscillation. Together, these values
+ // guarantee that the result of GetOverscroll() never changes sign.
+ ParentLayerCoord mLastOverscrollPeak;
+ float mOverscrollScale;
+
+ // A queue of (timestamp, velocity) pairs; these are the historical
+ // velocities at the given timestamps. Timestamps are in milliseconds,
+ // velocities are in screen pixels per ms. This member can only be
+ // accessed on the controller/UI thread.
+ nsTArray<std::pair<uint32_t, float> > mVelocityQueue;
+
+ const FrameMetrics& GetFrameMetrics() const;
+
+ // Adjust a requested overscroll amount for resistance, yielding a smaller
+ // actual overscroll amount.
+ ParentLayerCoord ApplyResistance(ParentLayerCoord aOverscroll) const;
+
+ // Helper function for SampleOverscrollAnimation().
+ void StepOverscrollAnimation(double aStepDurationMilliseconds);
+
+ // Convert a velocity from global inches/ms into ParentLayerCoords/ms.
+ float ToLocalVelocity(float aVelocityInchesPerMs) const;
+};
+
+class AxisX : public Axis {
+public:
+ explicit AxisX(AsyncPanZoomController* mAsyncPanZoomController);
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const override;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const override;
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const override;
+ virtual const char* Name() const override;
+};
+
+class AxisY : public Axis {
+public:
+ explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController);
+ virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const override;
+ virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
+ virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
+ virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const override;
+ virtual ScreenPoint MakePoint(ScreenCoord aCoord) const override;
+ virtual const char* Name() const override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/apz/src/CheckerboardEvent.cpp b/gfx/layers/apz/src/CheckerboardEvent.cpp
new file mode 100644
index 000000000..ea40a5fa7
--- /dev/null
+++ b/gfx/layers/apz/src/CheckerboardEvent.cpp
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CheckerboardEvent.h"
+
+#include <algorithm> // for std::sort
+
+namespace mozilla {
+namespace layers {
+
+// Relatively arbitrary limit to prevent a perma-checkerboard event from
+// eating up gobs of memory. Ideally we shouldn't have perma-checkerboarding
+// but better to guard against it.
+#define LOG_LENGTH_LIMIT (50 * 1024)
+
+const char* CheckerboardEvent::sDescriptions[] = {
+ "page",
+ "painted critical displayport",
+ "painted displayport",
+ "requested displayport",
+ "viewport",
+};
+
+const char* CheckerboardEvent::sColors[] = {
+ "brown",
+ "darkgreen",
+ "lightgreen",
+ "yellow",
+ "red",
+};
+
+CheckerboardEvent::CheckerboardEvent(bool aRecordTrace)
+ : mRecordTrace(aRecordTrace)
+ , mOriginTime(TimeStamp::Now())
+ , mCheckerboardingActive(false)
+ , mLastSampleTime(mOriginTime)
+ , mFrameCount(0)
+ , mTotalPixelMs(0)
+ , mPeakPixels(0)
+ , mRendertraceLock("Rendertrace")
+{
+}
+
+uint32_t
+CheckerboardEvent::GetSeverity()
+{
+ // Scale the total into a 32-bit value
+ return (uint32_t)sqrt((double)mTotalPixelMs);
+}
+
+uint32_t
+CheckerboardEvent::GetPeak()
+{
+ return mPeakPixels;
+}
+
+TimeDuration
+CheckerboardEvent::GetDuration()
+{
+ return mEndTime - mStartTime;
+}
+
+std::string
+CheckerboardEvent::GetLog()
+{
+ MonitorAutoLock lock(mRendertraceLock);
+ return mRendertraceInfo.str();
+}
+
+bool
+CheckerboardEvent::IsRecordingTrace()
+{
+ return mRecordTrace;
+}
+
+void
+CheckerboardEvent::UpdateRendertraceProperty(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo)
+{
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ if (!mCheckerboardingActive) {
+ mBufferedProperties[aProperty].Update(aProperty, aRect, aExtraInfo, lock);
+ } else {
+ LogInfo(aProperty, TimeStamp::Now(), aRect, aExtraInfo, lock);
+ }
+}
+
+void
+CheckerboardEvent::LogInfo(RendertraceProperty aProperty,
+ const TimeStamp& aTimestamp,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock)
+{
+ MOZ_ASSERT(mRecordTrace);
+ if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) {
+ // The log is already long enough, don't put more things into it. We'll
+ // append a truncation message when this event ends.
+ return;
+ }
+ // The log is consumed by the page at http://people.mozilla.org/~kgupta/rendertrace.html
+ // and will move to about:checkerboard in bug 1238042. The format is not
+ // formally specced, but an informal description can be found at
+ // https://github.com/staktrace/rendertrace/blob/master/index.html#L30
+ mRendertraceInfo << "RENDERTRACE "
+ << (aTimestamp - mOriginTime).ToMilliseconds() << " rect "
+ << sColors[aProperty] << " "
+ << aRect.x << " "
+ << aRect.y << " "
+ << aRect.width << " "
+ << aRect.height << " "
+ << "// " << sDescriptions[aProperty]
+ << aExtraInfo << std::endl;
+}
+
+bool
+CheckerboardEvent::RecordFrameInfo(uint32_t aCssPixelsCheckerboarded)
+{
+ TimeStamp sampleTime = TimeStamp::Now();
+ bool eventEnding = false;
+ if (aCssPixelsCheckerboarded > 0) {
+ if (!mCheckerboardingActive) {
+ StartEvent();
+ }
+ MOZ_ASSERT(mCheckerboardingActive);
+ MOZ_ASSERT(sampleTime >= mLastSampleTime);
+ mTotalPixelMs += (uint64_t)((sampleTime - mLastSampleTime).ToMilliseconds() * aCssPixelsCheckerboarded);
+ if (aCssPixelsCheckerboarded > mPeakPixels) {
+ mPeakPixels = aCssPixelsCheckerboarded;
+ }
+ mFrameCount++;
+ } else {
+ if (mCheckerboardingActive) {
+ StopEvent();
+ eventEnding = true;
+ }
+ MOZ_ASSERT(!mCheckerboardingActive);
+ }
+ mLastSampleTime = sampleTime;
+ return eventEnding;
+}
+
+void
+CheckerboardEvent::StartEvent()
+{
+ MOZ_ASSERT(!mCheckerboardingActive);
+ mCheckerboardingActive = true;
+ mStartTime = TimeStamp::Now();
+
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ std::vector<PropertyValue> history;
+ for (int i = 0; i < MAX_RendertraceProperty; i++) {
+ mBufferedProperties[i].Flush(history, lock);
+ }
+ std::sort(history.begin(), history.end());
+ for (const PropertyValue& p : history) {
+ LogInfo(p.mProperty, p.mTimeStamp, p.mRect, p.mExtraInfo, lock);
+ }
+ mRendertraceInfo << " -- checkerboarding starts below --" << std::endl;
+}
+
+void
+CheckerboardEvent::StopEvent()
+{
+ mCheckerboardingActive = false;
+ mEndTime = TimeStamp::Now();
+
+ if (!mRecordTrace) {
+ return;
+ }
+ MonitorAutoLock lock(mRendertraceLock);
+ if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) {
+ mRendertraceInfo << "[logging aborted due to length limitations]\n";
+ }
+ mRendertraceInfo << "Checkerboarded for " << mFrameCount << " frames ("
+ << (mEndTime - mStartTime).ToMilliseconds() << " ms), "
+ << mPeakPixels << " peak, " << GetSeverity() << " severity." << std::endl;
+}
+
+bool
+CheckerboardEvent::PropertyValue::operator<(const PropertyValue& aOther) const
+{
+ if (mTimeStamp < aOther.mTimeStamp) {
+ return true;
+ } else if (mTimeStamp > aOther.mTimeStamp) {
+ return false;
+ }
+ return mProperty < aOther.mProperty;
+}
+
+CheckerboardEvent::PropertyBuffer::PropertyBuffer()
+ : mIndex(0)
+{
+}
+
+void
+CheckerboardEvent::PropertyBuffer::Update(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock)
+{
+ mValues[mIndex] = { aProperty, TimeStamp::Now(), aRect, aExtraInfo };
+ mIndex = (mIndex + 1) % BUFFER_SIZE;
+}
+
+void
+CheckerboardEvent::PropertyBuffer::Flush(std::vector<PropertyValue>& aOut,
+ const MonitorAutoLock& aProofOfLock)
+{
+ for (uint32_t i = 0; i < BUFFER_SIZE; i++) {
+ uint32_t ix = (mIndex + i) % BUFFER_SIZE;
+ if (!mValues[ix].mTimeStamp.IsNull()) {
+ aOut.push_back(mValues[ix]);
+ mValues[ix].mTimeStamp = TimeStamp();
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/CheckerboardEvent.h b/gfx/layers/apz/src/CheckerboardEvent.h
new file mode 100644
index 000000000..c71611d89
--- /dev/null
+++ b/gfx/layers/apz/src/CheckerboardEvent.h
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CheckerboardEvent_h
+#define mozilla_layers_CheckerboardEvent_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include <sstream>
+#include "Units.h"
+#include <vector>
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class records information relevant to one "checkerboard event", which is
+ * a contiguous set of frames where a given APZC was checkerboarding. The intent
+ * of this class is to record enough information that it can provide actionable
+ * steps to reduce the occurrence of checkerboarding. Furthermore, it records
+ * information about the severity of the checkerboarding so as to allow
+ * prioritizing the debugging of some checkerboarding events over others.
+ */
+class CheckerboardEvent {
+public:
+ enum RendertraceProperty {
+ Page,
+ PaintedCriticalDisplayPort,
+ PaintedDisplayPort,
+ RequestedDisplayPort,
+ UserVisible,
+
+ // sentinel final value
+ MAX_RendertraceProperty
+ };
+
+ static const char* sDescriptions[MAX_RendertraceProperty];
+ static const char* sColors[MAX_RendertraceProperty];
+
+public:
+ explicit CheckerboardEvent(bool aRecordTrace);
+
+ /**
+ * Gets the "severity" of the checkerboard event. This doesn't have units,
+ * it's just useful for comparing two checkerboard events to see which one
+ * is worse, for some implementation-specific definition of "worse".
+ */
+ uint32_t GetSeverity();
+
+ /**
+ * Gets the number of CSS pixels that were checkerboarded at the peak of the
+ * checkerboard event.
+ */
+ uint32_t GetPeak();
+
+ /**
+ * Gets the length of the checkerboard event.
+ */
+ TimeDuration GetDuration();
+
+ /**
+ * Gets the raw log of the checkerboard event. This can be called any time,
+ * although it really only makes sense to pull once the event is done, after
+ * RecordFrameInfo returns true.
+ */
+ std::string GetLog();
+
+ /**
+ * Returns true iff this event is recording a detailed trace of the event.
+ * This is the argument passed in to the constructor.
+ */
+ bool IsRecordingTrace();
+
+ /**
+ * Provide a new value for one of the rects that is tracked for
+ * checkerboard events.
+ */
+ void UpdateRendertraceProperty(RendertraceProperty aProperty,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo = std::string());
+
+ /**
+ * Provide the number of CSS pixels that are checkerboarded in a composite
+ * at the current time.
+ * @return true if the checkerboard event has completed. The caller should
+ * stop updating this object once this happens.
+ */
+ bool RecordFrameInfo(uint32_t aCssPixelsCheckerboarded);
+
+private:
+ /**
+ * Helper method to do stuff when checkeboarding starts.
+ */
+ void StartEvent();
+ /**
+ * Helper method to do stuff when checkerboarding stops.
+ */
+ void StopEvent();
+
+ /**
+ * Helper method to log a rendertrace property and its value to the
+ * rendertrace info buffer (mRendertraceInfo).
+ */
+ void LogInfo(RendertraceProperty aProperty,
+ const TimeStamp& aTimestamp,
+ const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock);
+
+ /**
+ * Helper struct that holds a single rendertrace property value.
+ */
+ struct PropertyValue
+ {
+ RendertraceProperty mProperty;
+ TimeStamp mTimeStamp;
+ CSSRect mRect;
+ std::string mExtraInfo;
+
+ bool operator<(const PropertyValue& aOther) const;
+ };
+
+ /**
+ * A circular buffer that stores the most recent BUFFER_SIZE values of a
+ * given property.
+ */
+ class PropertyBuffer
+ {
+ public:
+ PropertyBuffer();
+ /**
+ * Add a new value to the buffer, overwriting the oldest one if needed.
+ */
+ void Update(RendertraceProperty aProperty, const CSSRect& aRect,
+ const std::string& aExtraInfo,
+ const MonitorAutoLock& aProofOfLock);
+ /**
+ * Dump the recorded values, oldest to newest, to the given vector, and
+ * remove them from this buffer.
+ */
+ void Flush(std::vector<PropertyValue>& aOut,
+ const MonitorAutoLock& aProofOfLock);
+
+ private:
+ static const uint32_t BUFFER_SIZE = 5;
+
+ /**
+ * The index of the oldest value in the buffer. This is the next index
+ * that will be written to.
+ */
+ uint32_t mIndex;
+ PropertyValue mValues[BUFFER_SIZE];
+ };
+
+private:
+ /**
+ * If true, we should log the various properties during the checkerboard
+ * event. If false, we only need to record things we need for telemetry
+ * measures.
+ */
+ const bool mRecordTrace;
+ /**
+ * A base time so that the other timestamps can be turned into durations.
+ */
+ const TimeStamp mOriginTime;
+ /**
+ * Whether or not a checkerboard event is currently occurring.
+ */
+ bool mCheckerboardingActive;
+
+ /**
+ * The start time of the checkerboard event.
+ */
+ TimeStamp mStartTime;
+ /**
+ * The end time of the checkerboard event.
+ */
+ TimeStamp mEndTime;
+ /**
+ * The sample time of the last frame recorded.
+ */
+ TimeStamp mLastSampleTime;
+ /**
+ * The number of contiguous frames with checkerboard.
+ */
+ uint32_t mFrameCount;
+ /**
+ * The total number of pixel-milliseconds of checkerboarding visible to
+ * the user during the checkerboarding event.
+ */
+ uint64_t mTotalPixelMs;
+ /**
+ * The largest number of pixels of checkerboarding visible to the user
+ * during any one frame, during this checkerboarding event.
+ */
+ uint32_t mPeakPixels;
+
+ /**
+ * Monitor that needs to be acquired before touching mBufferedProperties
+ * or mRendertraceInfo.
+ */
+ mutable Monitor mRendertraceLock;
+ /**
+ * A circular buffer to store some properties. This is used before the
+ * checkerboarding actually starts, so that we have some data on what
+ * was happening before the checkerboarding started.
+ */
+ PropertyBuffer mBufferedProperties[MAX_RendertraceProperty];
+ /**
+ * The rendertrace info buffer that gives us info on what was happening
+ * during the checkerboard event.
+ */
+ std::ostringstream mRendertraceInfo;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CheckerboardEvent_h
diff --git a/gfx/layers/apz/src/DragTracker.cpp b/gfx/layers/apz/src/DragTracker.cpp
new file mode 100644
index 000000000..ecd3ff16f
--- /dev/null
+++ b/gfx/layers/apz/src/DragTracker.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DragTracker.h"
+
+#include "InputData.h"
+
+#define DRAG_LOG(...)
+// #define DRAG_LOG(...) printf_stderr("DRAG: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+DragTracker::DragTracker()
+ : mInDrag(false)
+{
+}
+
+/*static*/ bool
+DragTracker::StartsDrag(const MouseInput& aInput)
+{
+ return aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_DOWN;
+}
+
+/*static*/ bool
+DragTracker::EndsDrag(const MouseInput& aInput)
+{
+ // On Windows, we don't receive a MOUSE_UP at the end of a drag if an
+ // actual drag session took place. As a backup, we detect the end of the
+ // drag using the MOUSE_DRAG_END event, which normally is routed directly
+ // to content, but we're specially routing to APZ for this purpose. Bug
+ // 1265105 tracks a solution to this at the Windows widget layer; once
+ // that is implemented, this workaround can be removed.
+ return (aInput.IsLeftButton() && aInput.mType == MouseInput::MOUSE_UP)
+ || aInput.mType == MouseInput::MOUSE_DRAG_END;
+}
+
+void
+DragTracker::Update(const MouseInput& aInput)
+{
+ if (StartsDrag(aInput)) {
+ DRAG_LOG("Starting drag\n");
+ mInDrag = true;
+ } else if (EndsDrag(aInput)) {
+ DRAG_LOG("Ending drag\n");
+ mInDrag = false;
+ mOnScrollbar = Nothing();
+ }
+}
+
+bool
+DragTracker::InDrag() const
+{
+ return mInDrag;
+}
+
+bool
+DragTracker::IsOnScrollbar(bool aOnScrollbar)
+{
+ if (!mOnScrollbar) {
+ DRAG_LOG("Setting hitscrollbar %d\n", aOnScrollbar);
+ mOnScrollbar = Some(aOnScrollbar);
+ }
+ return mOnScrollbar.value();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/DragTracker.h b/gfx/layers/apz/src/DragTracker.h
new file mode 100644
index 000000000..9f7ff1222
--- /dev/null
+++ b/gfx/layers/apz/src/DragTracker.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DragTracker_h
+#define mozilla_layers_DragTracker_h
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class MouseInput;
+
+namespace layers {
+
+// DragTracker simply tracks a sequence of mouse inputs and allows us to tell
+// if we are in a drag or not (i.e. the left mouse button went down and hasn't
+// gone up yet).
+class DragTracker
+{
+public:
+ DragTracker();
+ static bool StartsDrag(const MouseInput& aInput);
+ static bool EndsDrag(const MouseInput& aInput);
+ void Update(const MouseInput& aInput);
+ bool InDrag() const;
+ bool IsOnScrollbar(bool aOnScrollbar);
+
+private:
+ Maybe<bool> mOnScrollbar;
+ bool mInDrag;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_DragTracker_h */
diff --git a/gfx/layers/apz/src/GenericFlingAnimation.h b/gfx/layers/apz/src/GenericFlingAnimation.h
new file mode 100644
index 000000000..deec37b47
--- /dev/null
+++ b/gfx/layers/apz/src/GenericFlingAnimation.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GenericFlingAnimation_h_
+#define mozilla_layers_GenericFlingAnimation_h_
+
+#include "APZUtils.h"
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "FrameMetrics.h"
+#include "Layers.h"
+#include "Units.h"
+#include "OverscrollHandoffState.h"
+#include "gfxPrefs.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+#define FLING_LOG(...)
+// #define FLING_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+class GenericFlingAnimation: public AsyncPanZoomAnimation {
+public:
+ GenericFlingAnimation(AsyncPanZoomController& aApzc,
+ PlatformSpecificStateBase* aPlatformSpecificState,
+ const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
+ bool aFlingIsHandedOff,
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+ : mApzc(aApzc)
+ , mOverscrollHandoffChain(aOverscrollHandoffChain)
+ , mScrolledApzc(aScrolledApzc)
+ {
+ MOZ_ASSERT(mOverscrollHandoffChain);
+ TimeStamp now = aApzc.GetFrameTime();
+
+ // Drop any velocity on axes where we don't have room to scroll anyways
+ // (in this APZC, or an APZC further in the handoff chain).
+ // This ensures that we don't take the 'overscroll' path in Sample()
+ // on account of one axis which can't scroll having a velocity.
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::HORIZONTAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mX.SetVelocity(0);
+ }
+ if (!mOverscrollHandoffChain->CanScrollInDirection(&mApzc, Layer::VERTICAL)) {
+ ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
+ mApzc.mY.SetVelocity(0);
+ }
+
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ // If the last fling was very recent and in the same direction as this one,
+ // boost the velocity to be the sum of the two. Check separate axes separately
+ // because we could have two vertical flings with small horizontal components
+ // on the opposite side of zero, and we still want the y-fling to get accelerated.
+ // Note that the acceleration code is only applied on the APZC that initiates
+ // the fling; the accelerated velocities are then handed off using the
+ // normal DispatchFling codepath.
+ // Acceleration is only applied in the APZC that originated the fling,
+ // not in APZCs further down the handoff chain during handoff.
+ bool applyAcceleration = !aFlingIsHandedOff;
+ if (applyAcceleration && !mApzc.mLastFlingTime.IsNull()
+ && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()
+ && velocity.Length() >= gfxPrefs::APZFlingAccelMinVelocity()) {
+ if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
+ velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
+ FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
+ mApzc.mX.SetVelocity(velocity.x);
+ }
+ if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
+ velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
+ FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
+ &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
+ mApzc.mY.SetVelocity(velocity.y);
+ }
+ }
+
+ mApzc.mLastFlingTime = now;
+ mApzc.mLastFlingVelocity = velocity;
+ }
+
+ /**
+ * Advances a fling by an interpolated amount based on the passed in |aDelta|.
+ * This should be called whenever sampling the content transform for this
+ * frame. Returns true if the fling animation should be advanced by one frame,
+ * or false if there is no fling or the fling has ended.
+ */
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ float friction = gfxPrefs::APZFlingFriction();
+ float threshold = gfxPrefs::APZFlingStoppedThreshold();
+
+ bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
+ shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
+ // If we shouldn't continue the fling, let's just stop and repaint.
+ if (!shouldContinueFlingX && !shouldContinueFlingY) {
+ FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
+ // This APZC or an APZC further down the handoff chain may be be overscrolled.
+ // Start a snap-back animation on the overscrolled APZC.
+ // Note:
+ // This needs to be a deferred task even though it can safely run
+ // while holding mMonitor, because otherwise, if the overscrolled APZC
+ // is this one, then the SetState(NOTHING) in UpdateAnimation will
+ // stomp on the SetState(SNAP_BACK) it does.
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<AsyncPanZoomController*>(mOverscrollHandoffChain.get(),
+ &OverscrollHandoffChain::SnapBackOverscrolledApzc,
+ &mApzc));
+ return false;
+ }
+
+ // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
+ // Since we need to hand off the velocity to the tree manager in such a case,
+ // we save it here. Would be ParentLayerVector instead of ParentLayerPoint
+ // if we had vector classes.
+ ParentLayerPoint velocity = mApzc.GetVelocityVector();
+
+ ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
+
+ // Ordinarily we might need to do a ScheduleComposite if either of
+ // the following AdjustDisplacement calls returns true, but this
+ // is already running as part of a FlingAnimation, so we'll be compositing
+ // per frame of animation anyway.
+ ParentLayerPoint overscroll;
+ ParentLayerPoint adjustedOffset;
+ mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
+
+ aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
+
+ // The fling may have caused us to reach the end of our scroll range.
+ if (!IsZero(overscroll)) {
+ // Hand off the fling to the next APZC in the overscroll handoff chain.
+
+ // We may have reached the end of the scroll range along one axis but
+ // not the other. In such a case we only want to hand off the relevant
+ // component of the fling.
+ if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ velocity.y = 0;
+ }
+
+ // To hand off the fling, we attempt to find a target APZC and start a new
+ // fling with the same velocity on that APZC. For simplicity, the actual
+ // overscroll of the current sample is discarded rather than being handed
+ // off. The compositor should sample animations sufficiently frequently
+ // that this is not noticeable. The target APZC is chosen by seeing if
+ // there is an APZC further in the handoff chain which is pannable; if
+ // there isn't, we take the new fling ourselves, entering an overscrolled
+ // state.
+ // Note: APZC is holding mMonitor, so directly calling
+ // HandleFlingOverscroll() (which acquires the tree lock) would violate
+ // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
+ // called after mMonitor is released.
+ FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", &mApzc, Stringify(velocity).c_str());
+ mDeferredTasks.AppendElement(
+ NewRunnableMethod<ParentLayerPoint,
+ RefPtr<const OverscrollHandoffChain>,
+ RefPtr<const AsyncPanZoomController>>(&mApzc,
+ &AsyncPanZoomController::HandleFlingOverscroll,
+ velocity,
+ mOverscrollHandoffChain,
+ mScrolledApzc));
+
+ // If there is a remaining velocity on this APZC, continue this fling
+ // as well. (This fling and the handed-off fling will run concurrently.)
+ // Note that AdjustDisplacement() will have zeroed out the velocity
+ // along the axes where we're overscrolled.
+ return !IsZero(mApzc.GetVelocityVector());
+ }
+
+ return true;
+ }
+
+private:
+ static bool SameDirection(float aVelocity1, float aVelocity2)
+ {
+ return (aVelocity1 == 0.0f)
+ || (aVelocity2 == 0.0f)
+ || (IsNegative(aVelocity1) == IsNegative(aVelocity2));
+ }
+
+ static float Accelerate(float aBase, float aSupplemental)
+ {
+ return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
+ + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
+ }
+
+ AsyncPanZoomController& mApzc;
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_GenericFlingAnimation_h_
diff --git a/gfx/layers/apz/src/GestureEventListener.cpp b/gfx/layers/apz/src/GestureEventListener.cpp
new file mode 100644
index 000000000..7fd07f3ff
--- /dev/null
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GestureEventListener.h"
+#include <math.h> // for fabsf
+#include <stddef.h> // for size_t
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "base/task.h" // for CancelableTask, etc
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
+#include "nsDebug.h" // for NS_WARNING
+#include "nsMathUtils.h" // for NS_hypot
+
+#define GEL_LOG(...)
+// #define GEL_LOG(...) printf_stderr("GEL: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Maximum time for a touch on the screen and corresponding lift of the finger
+ * to be considered a tap. This also applies to double taps, except that it is
+ * used twice.
+ */
+static const uint32_t MAX_TAP_TIME = 300;
+
+/**
+ * Amount of span or focus change needed to take us from the GESTURE_WAITING_PINCH
+ * state to the GESTURE_PINCH state. This is measured as either a change in distance
+ * between the fingers used to compute the span ratio, or the a change in
+ * position of the focus point between the two fingers.
+ */
+static const float PINCH_START_THRESHOLD = 35.0f;
+
+static bool sLongTapEnabled = true;
+
+ParentLayerPoint GetCurrentFocus(const MultiTouchInput& aEvent)
+{
+ const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
+ const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
+ return (firstTouch + secondTouch) / 2;
+}
+
+ParentLayerCoord GetCurrentSpan(const MultiTouchInput& aEvent)
+{
+ const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
+ const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
+ ParentLayerPoint delta = secondTouch - firstTouch;
+ return delta.Length();
+}
+
+TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch, TapGestureInput::TapGestureType aType)
+{
+ return TapGestureInput(aType,
+ aTouch.mTime,
+ aTouch.mTimeStamp,
+ aTouch.mTouches[0].mScreenPoint,
+ aTouch.modifiers);
+}
+
+GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
+ : mAsyncPanZoomController(aAsyncPanZoomController),
+ mState(GESTURE_NONE),
+ mSpanChange(0.0f),
+ mPreviousSpan(0.0f),
+ mFocusChange(0.0f),
+ mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLastTapInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLongTapTimeoutTask(nullptr),
+ mMaxTapTimeoutTask(nullptr)
+{
+}
+
+GestureEventListener::~GestureEventListener()
+{
+}
+
+nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
+{
+ GEL_LOG("Receiving event type %d with %" PRIuSIZE " touches in state %d\n", aEvent.mType, aEvent.mTouches.Length(), mState);
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ // Cache the current event since it may become the single or long tap that we
+ // send.
+ mLastTouchInput = aEvent;
+
+ switch (aEvent.mType) {
+ case MultiTouchInput::MULTITOUCH_START:
+ mTouches.Clear();
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ mTouches.AppendElement(aEvent.mTouches[i]);
+ }
+
+ if (aEvent.mTouches.Length() == 1) {
+ rv = HandleInputTouchSingleStart();
+ } else {
+ rv = HandleInputTouchMultiStart();
+ }
+ break;
+ case MultiTouchInput::MULTITOUCH_MOVE:
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches[j].mScreenPoint = aEvent.mTouches[i].mScreenPoint;
+ mTouches[j].mLocalScreenPoint = aEvent.mTouches[i].mLocalScreenPoint;
+ }
+ }
+ }
+ rv = HandleInputTouchMove();
+ break;
+ case MultiTouchInput::MULTITOUCH_END:
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches.RemoveElementAt(j);
+ break;
+ }
+ }
+ }
+
+ rv = HandleInputTouchEnd();
+ break;
+ case MultiTouchInput::MULTITOUCH_CANCEL:
+ mTouches.Clear();
+ rv = HandleInputTouchCancel();
+ break;
+ case MultiTouchInput::MULTITOUCH_SENTINEL:
+ MOZ_ASSERT_UNREACHABLE("Invalid MultTouchInput.");
+ break;
+ }
+
+ return rv;
+}
+
+int32_t GestureEventListener::GetLastTouchIdentifier() const
+{
+ if (mTouches.Length() != 1) {
+ NS_WARNING("GetLastTouchIdentifier() called when last touch event "
+ "did not have one touch");
+ }
+ return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
+}
+
+/* static */
+void GestureEventListener::SetLongTapEnabled(bool aLongTapEnabled)
+{
+ sLongTapEnabled = aLongTapEnabled;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchSingleStart()
+{
+ switch (mState) {
+ case GESTURE_NONE:
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_DOWN);
+ mTouchStartPosition = mLastTouchInput.mTouches[0].mLocalScreenPoint;
+
+ if (sLongTapEnabled) {
+ CreateLongTapTimeoutTask();
+ }
+ CreateMaxTapTimeoutTask();
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
+ break;
+ default:
+ NS_WARNING("Unhandled state upon single touch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
+{
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
+ // Cancel wait for double tap
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_LONG_TOUCH_DOWN:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_MULTI_TOUCH_DOWN:
+ case GESTURE_PINCH:
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ default:
+ NS_WARNING("Unhandled state upon multitouch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+bool GestureEventListener::MoveDistanceIsLarge()
+{
+ const ParentLayerPoint start = mLastTouchInput.mTouches[0].mLocalScreenPoint;
+ ParentLayerPoint delta = start - mTouchStartPosition;
+ ScreenPoint screenDelta = mAsyncPanZoomController->ToScreenCoordinates(delta, start);
+ return (screenDelta.Length() > AsyncPanZoomController::GetTouchStartTolerance());
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMove()
+{
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // Ignore this input signal as the corresponding events get handled by APZC
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN:
+ if (MoveDistanceIsLarge()) {
+ // So that we don't fire a long-tap-up if the user moves around after a
+ // long-tap
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ // If we move too much, bail out of the tap.
+ if (MoveDistanceIsLarge()) {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ }
+ break;
+ }
+
+ case GESTURE_MULTI_TOUCH_DOWN: {
+ if (mLastTouchInput.mTouches.Length() < 2) {
+ NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
+ break;
+ }
+
+ ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+ ParentLayerPoint currentFocus = GetCurrentFocus(mLastTouchInput);
+
+ mSpanChange += fabsf(currentSpan - mPreviousSpan);
+ mFocusChange += (currentFocus - mPreviousFocus).Length();
+ if (mSpanChange > PINCH_START_THRESHOLD ||
+ mFocusChange > PINCH_START_THRESHOLD) {
+ SetState(GESTURE_PINCH);
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ currentFocus,
+ currentSpan,
+ currentSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ } else {
+ // Prevent APZC::OnTouchMove from processing a move event when two
+ // touches are active
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+
+ mPreviousSpan = currentSpan;
+ mPreviousFocus = currentFocus;
+ break;
+ }
+
+ case GESTURE_PINCH: {
+ if (mLastTouchInput.mTouches.Length() < 2) {
+ NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state");
+ // Prevent APZC::OnTouchMove() from handling this wrong input
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ }
+
+ ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ GetCurrentFocus(mLastTouchInput),
+ currentSpan,
+ mPreviousSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ mPreviousSpan = currentSpan;
+
+ break;
+ }
+
+ default:
+ NS_WARNING("Unhandled state upon touch move");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchEnd()
+{
+ // We intentionally do not pass apzc return statuses up since
+ // it may cause apzc stay in the touching state even after
+ // gestures are completed (please see Bug 1013378 for reference).
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP));
+ mSingleTapSent = Some(tapupStatus != nsEventStatus_eIgnore);
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
+ CreateMaxTapTimeoutTask();
+ break;
+ }
+
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput,
+ mSingleTapSent.value() ? TapGestureInput::TAPGESTURE_SECOND
+ : TapGestureInput::TAPGESTURE_DOUBLE));
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_NONE);
+ TriggerSingleTapConfirmedEvent();
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN: {
+ SetState(GESTURE_NONE);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG_UP));
+ break;
+ }
+
+ case GESTURE_MULTI_TOUCH_DOWN:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_PINCH:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ ParentLayerPoint point(-1, -1);
+ if (mTouches.Length() == 1) {
+ // As user still keeps one finger down the event's focus point should
+ // contain meaningful data.
+ point = mTouches[0].mLocalScreenPoint;
+ }
+ PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
+ mLastTouchInput.mTime,
+ mLastTouchInput.mTimeStamp,
+ point,
+ 1.0f,
+ 1.0f,
+ mLastTouchInput.modifiers);
+ mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ }
+
+ rv = nsEventStatus_eConsumeNoDefault;
+
+ break;
+
+ default:
+ NS_WARNING("Unhandled state upon touch end");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchCancel()
+{
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ CancelMaxTapTimeoutTask();
+ CancelLongTapTimeoutTask();
+ return nsEventStatus_eIgnore;
+}
+
+void GestureEventListener::HandleInputTimeoutLongTap()
+{
+ GEL_LOG("Running long-tap timeout task in state %d\n", mState);
+
+ mLongTapTimeoutTask = nullptr;
+
+ switch (mState) {
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ // just in case MAX_TAP_TIME > ContextMenuDelay cancel MAX_TAP timer
+ // and fall through
+ CancelMaxTapTimeoutTask();
+ MOZ_FALLTHROUGH;
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
+ SetState(GESTURE_LONG_TOUCH_DOWN);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG));
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled state upon long tap timeout");
+ SetState(GESTURE_NONE);
+ break;
+ }
+}
+
+void GestureEventListener::HandleInputTimeoutMaxTap(bool aDuringFastFling)
+{
+ GEL_LOG("Running max-tap timeout task in state %d\n", mState);
+
+ mMaxTapTimeoutTask = nullptr;
+
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) {
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN);
+ } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP ||
+ mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!aDuringFastFling && !mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ } else {
+ NS_WARNING("Unhandled state upon MAX_TAP timeout");
+ SetState(GESTURE_NONE);
+ }
+}
+
+void GestureEventListener::TriggerSingleTapConfirmedEvent()
+{
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTapInput, TapGestureInput::TAPGESTURE_CONFIRMED));
+}
+
+void GestureEventListener::SetState(GestureState aState)
+{
+ mState = aState;
+
+ if (mState == GESTURE_NONE) {
+ mSpanChange = 0.0f;
+ mPreviousSpan = 0.0f;
+ mFocusChange = 0.0f;
+ } else if (mState == GESTURE_MULTI_TOUCH_DOWN) {
+ mPreviousSpan = GetCurrentSpan(mLastTouchInput);
+ mPreviousFocus = GetCurrentFocus(mLastTouchInput);
+ }
+}
+
+void GestureEventListener::CancelLongTapTimeoutTask()
+{
+ if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ // being in this state means the task has been canceled already
+ return;
+ }
+
+ if (mLongTapTimeoutTask) {
+ mLongTapTimeoutTask->Cancel();
+ mLongTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateLongTapTimeoutTask()
+{
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &GestureEventListener::HandleInputTimeoutLongTap);
+
+ mLongTapTimeoutTask = task;
+ mAsyncPanZoomController->PostDelayedTask(
+ task.forget(),
+ gfxPrefs::UiClickHoldContextMenusDelay());
+}
+
+void GestureEventListener::CancelMaxTapTimeoutTask()
+{
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) {
+ // being in this state means the timer has just been triggered
+ return;
+ }
+
+ if (mMaxTapTimeoutTask) {
+ mMaxTapTimeoutTask->Cancel();
+ mMaxTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateMaxTapTimeoutTask()
+{
+ mLastTapInput = mLastTouchInput;
+
+ TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
+ MOZ_ASSERT(block);
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<bool>(this,
+ &GestureEventListener::HandleInputTimeoutMaxTap,
+ block->IsDuringFastFling());
+
+ mMaxTapTimeoutTask = task;
+ mAsyncPanZoomController->PostDelayedTask(
+ task.forget(),
+ MAX_TAP_TIME);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/GestureEventListener.h b/gfx/layers/apz/src/GestureEventListener.h
new file mode 100644
index 000000000..d025ed0d1
--- /dev/null
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_GestureEventListener_h
+#define mozilla_layers_GestureEventListener_h
+
+#include "InputData.h" // for MultiTouchInput, etc
+#include "Units.h"
+#include "mozilla/EventForwards.h" // for nsEventStatus
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h"
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace layers {
+
+class AsyncPanZoomController;
+
+/**
+ * Platform-non-specific, generalized gesture event listener. This class
+ * intercepts all touches events on their way to AsyncPanZoomController and
+ * determines whether or not they are part of a gesture.
+ *
+ * For example, seeing that two fingers are on the screen means that the user
+ * wants to do a pinch gesture, so we don't forward the touches along to
+ * AsyncPanZoomController since it will think that they are just trying to pan
+ * the screen. Instead, we generate a PinchGestureInput and send that. If the
+ * touch event is not part of a gesture, we just return nsEventStatus_eIgnore
+ * and AsyncPanZoomController is expected to handle it.
+ *
+ * Android doesn't use this class because it has its own built-in gesture event
+ * listeners that should generally be preferred.
+ */
+class GestureEventListener final {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener)
+
+ explicit GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController);
+
+ // --------------------------------------------------------------------------
+ // These methods must only be called on the controller/UI thread.
+ //
+
+ /**
+ * General input handler for a touch event. If the touch event is not a part
+ * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
+ * it gets consumed here and never forwarded along.
+ */
+ nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
+
+ /**
+ * Returns the identifier of the touch in the last touch event processed by
+ * this GestureEventListener. This should only be called when the last touch
+ * event contained only one touch.
+ */
+ int32_t GetLastTouchIdentifier() const;
+
+ /**
+ * Function used to disable long tap gestures.
+ *
+ * On slow running tests, drags and touch events can be misinterpreted
+ * as a long tap. This allows tests to disable long tap gesture detection.
+ */
+ static void SetLongTapEnabled(bool aLongTapEnabled);
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~GestureEventListener();
+
+ /**
+ * States of GEL finite-state machine.
+ */
+ enum GestureState {
+ // This is the initial and final state of any gesture.
+ // In this state there's no gesture going on, and we don't think we're
+ // about to enter one.
+ // Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_NONE,
+
+ // A touch start with a single touch point has just happened.
+ // After having gotten into this state we start timers for MAX_TAP_TIME and
+ // gfxPrefs::UiClickHoldContextMenusDelay().
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_FIRST_SINGLE_TOUCH_UP, GESTURE_LONG_TOUCH_DOWN,
+ // GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_DOWN,
+
+ // While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got
+ // triggered. Now we'll trigger either a single tap if a user lifts her
+ // finger or a long tap if gfxPrefs::UiClickHoldContextMenusDelay() happens
+ // first.
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_LONG_TOUCH_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN,
+
+ // A user put her finger down and lifted it up quickly enough.
+ // After having gotten into this state we clear the timer for MAX_TAP_TIME.
+ // Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
+ // GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_FIRST_SINGLE_TOUCH_UP,
+
+ // A user put down her finger again right after a single tap thus the
+ // gesture can't be a single tap, but rather a double tap. But we're
+ // still not sure about that until the user lifts her finger again.
+ // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE.
+ GESTURE_SECOND_SINGLE_TOUCH_DOWN,
+
+ // A long touch has happened, but the user still keeps her finger down.
+ // We'll trigger a "long tap up" event when the finger is up.
+ // Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
+ GESTURE_LONG_TOUCH_DOWN,
+
+ // We have detected that two or more fingers are on the screen, but there
+ // hasn't been enough movement yet to make us start actually zooming the
+ // screen.
+ // Allowed next states: GESTURE_PINCH, GESTURE_NONE
+ GESTURE_MULTI_TOUCH_DOWN,
+
+ // There are two or more fingers on the screen, and the user has already
+ // pinched enough for us to start zooming the screen.
+ // Allowed next states: GESTURE_NONE
+ GESTURE_PINCH
+ };
+
+ /**
+ * These HandleInput* functions comprise input alphabet of the GEL
+ * finite-state machine triggering state transitions.
+ */
+ nsEventStatus HandleInputTouchSingleStart();
+ nsEventStatus HandleInputTouchMultiStart();
+ nsEventStatus HandleInputTouchEnd();
+ nsEventStatus HandleInputTouchMove();
+ nsEventStatus HandleInputTouchCancel();
+ void HandleInputTimeoutLongTap();
+ void HandleInputTimeoutMaxTap(bool aDuringFastFling);
+
+ void TriggerSingleTapConfirmedEvent();
+
+ bool MoveDistanceIsLarge();
+
+ /**
+ * Do actual state transition and reset substates.
+ */
+ void SetState(GestureState aState);
+
+ RefPtr<AsyncPanZoomController> mAsyncPanZoomController;
+
+ /**
+ * Array containing all active touches. When a touch happens it, gets added to
+ * this array, even if we choose not to handle it. When it ends, we remove it.
+ * We need to maintain this array in order to detect the end of the
+ * "multitouch" states because touch start events contain all current touches,
+ * but touch end events contain only those touches that have gone.
+ */
+ nsTArray<SingleTouchData> mTouches;
+
+ /**
+ * Current state we're dealing with.
+ */
+ GestureState mState;
+
+ /**
+ * Total change in span since we detected a pinch gesture. Only used when we
+ * are in the |GESTURE_WAITING_PINCH| state and need to know how far zoomed
+ * out we are compared to our original pinch span. Note that this does _not_
+ * continue to be updated once we jump into the |GESTURE_PINCH| state.
+ */
+ ParentLayerCoord mSpanChange;
+
+ /**
+ * Previous span calculated for the purposes of setting inside a
+ * PinchGestureInput.
+ */
+ ParentLayerCoord mPreviousSpan;
+
+ /* Properties similar to mSpanChange and mPreviousSpan, but for the focus */
+ ParentLayerCoord mFocusChange;
+ ParentLayerPoint mPreviousFocus;
+
+ /**
+ * Cached copy of the last touch input.
+ */
+ MultiTouchInput mLastTouchInput;
+
+ /**
+ * Cached copy of the last tap gesture input.
+ * In the situation when we have a tap followed by a pinch we lose info
+ * about tap since we keep only last input and to dispatch it correctly
+ * we save last tap copy into this variable.
+ * For more info see bug 947892.
+ */
+ MultiTouchInput mLastTapInput;
+
+ /**
+ * Position of the last touch starting. This is only valid during an attempt
+ * to determine if a touch is a tap. If a touch point moves away from
+ * mTouchStartPosition to the distance greater than
+ * AsyncPanZoomController::GetTouchStartTolerance() while in
+ * GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN
+ * or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is
+ * not tap.
+ */
+ ParentLayerPoint mTouchStartPosition;
+
+ /**
+ * Task used to timeout a long tap. This gets posted to the UI thread such
+ * that it runs a time when a single tap happens. We cache it so that
+ * we can cancel it if any other touch event happens.
+ *
+ * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN
+ * and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states.
+ *
+ * CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set
+ * it to null.
+ */
+ RefPtr<CancelableRunnable> mLongTapTimeoutTask;
+ void CancelLongTapTimeoutTask();
+ void CreateLongTapTimeoutTask();
+
+ /**
+ * Task used to timeout a single tap or a double tap.
+ *
+ * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN,
+ * GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
+ *
+ * CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
+ * it to null.
+ */
+ RefPtr<CancelableRunnable> mMaxTapTimeoutTask;
+ void CancelMaxTapTimeoutTask();
+ void CreateMaxTapTimeoutTask();
+
+ /**
+ * Tracks whether the single-tap event was already sent to content. This is
+ * needed because it affects how the double-tap gesture, if detected, is
+ * handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP and
+ * GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is
+ * stored in a Maybe which is set to Nothing() at all other times.
+ */
+ Maybe<bool> mSingleTapSent;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp
new file mode 100644
index 000000000..acedcde5d
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HitTestingTreeNode.h"
+
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "LayersLogging.h" // for Stringify
+#include "mozilla/gfx/Point.h" // for Point4D
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform::operator Matrix4x4()
+#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "UnitTransforms.h" // for ViewAs
+
+namespace mozilla {
+namespace layers {
+
+HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
+ bool aIsPrimaryHolder,
+ uint64_t aLayersId)
+ : mApzc(aApzc)
+ , mIsPrimaryApzcHolder(aIsPrimaryHolder)
+ , mLayersId(aLayersId)
+ , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
+ , mScrollDir(Layer::NONE)
+ , mScrollSize(0)
+ , mIsScrollbarContainer(false)
+ , mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
+ , mOverride(EventRegionsOverride::NoOverride)
+{
+if (mIsPrimaryApzcHolder) {
+ MOZ_ASSERT(mApzc);
+ }
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+}
+
+void
+HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc,
+ uint64_t aLayersId)
+{
+ MOZ_ASSERT(!mIsPrimaryApzcHolder);
+ Destroy(); // clear out tree pointers
+ mApzc = aApzc;
+ mLayersId = aLayersId;
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+ // The caller is expected to call SetHitTestData to repopulate the hit-test
+ // fields.
+}
+
+HitTestingTreeNode::~HitTestingTreeNode()
+{
+}
+
+void
+HitTestingTreeNode::Destroy()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ mPrevSibling = nullptr;
+ mLastChild = nullptr;
+ mParent = nullptr;
+
+ if (mApzc) {
+ if (mIsPrimaryApzcHolder) {
+ mApzc->Destroy();
+ }
+ mApzc = nullptr;
+ }
+
+ mLayersId = 0;
+}
+
+void
+HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
+{
+ mLastChild = aChild;
+ if (aChild) {
+ aChild->mParent = this;
+
+ if (aChild->GetApzc()) {
+ AsyncPanZoomController* parent = GetNearestContainingApzc();
+ // We assume that HitTestingTreeNodes with an ancestor/descendant
+ // relationship cannot both point to the same APZC instance. This
+ // assertion only covers a subset of cases in which that might occur,
+ // but it's better than nothing.
+ MOZ_ASSERT(aChild->GetApzc() != parent);
+ aChild->SetApzcParent(parent);
+ }
+ }
+}
+
+void
+HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+ Layer::ScrollDirection aDir,
+ int32_t aScrollSize,
+ bool aIsScrollContainer)
+{
+ mScrollViewId = aScrollViewId;
+ mScrollDir = aDir;
+ mScrollSize = aScrollSize;;
+ mIsScrollbarContainer = aIsScrollContainer;
+}
+
+bool
+HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
+{
+ return ((mScrollDir == Layer::HORIZONTAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) ||
+ (mScrollDir == Layer::VERTICAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::VERTICAL)) &&
+ mScrollViewId == aDragMetrics.mViewId;
+}
+
+int32_t
+HitTestingTreeNode::GetScrollSize() const
+{
+ return mScrollSize;
+}
+
+bool
+HitTestingTreeNode::IsScrollbarNode() const
+{
+ return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
+}
+
+void
+HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
+{
+ mFixedPosTarget = aFixedPosTarget;
+}
+
+FrameMetrics::ViewID
+HitTestingTreeNode::GetFixedPosTarget() const
+{
+ return mFixedPosTarget;
+}
+
+void
+HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
+{
+ mPrevSibling = aSibling;
+ if (aSibling) {
+ aSibling->mParent = mParent;
+
+ if (aSibling->GetApzc()) {
+ AsyncPanZoomController* parent = mParent ? mParent->GetNearestContainingApzc() : nullptr;
+ aSibling->SetApzcParent(parent);
+ }
+ }
+}
+
+void
+HitTestingTreeNode::MakeRoot()
+{
+ mParent = nullptr;
+
+ if (GetApzc()) {
+ SetApzcParent(nullptr);
+ }
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetFirstChild() const
+{
+ HitTestingTreeNode* child = GetLastChild();
+ while (child && child->GetPrevSibling()) {
+ child = child->GetPrevSibling();
+ }
+ return child;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetLastChild() const
+{
+ return mLastChild;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetPrevSibling() const
+{
+ return mPrevSibling;
+}
+
+HitTestingTreeNode*
+HitTestingTreeNode::GetParent() const
+{
+ return mParent;
+}
+
+AsyncPanZoomController*
+HitTestingTreeNode::GetApzc() const
+{
+ return mApzc;
+}
+
+AsyncPanZoomController*
+HitTestingTreeNode::GetNearestContainingApzc() const
+{
+ for (const HitTestingTreeNode* n = this; n; n = n->GetParent()) {
+ if (n->GetApzc()) {
+ return n->GetApzc();
+ }
+ }
+ return nullptr;
+}
+
+bool
+HitTestingTreeNode::IsPrimaryHolder() const
+{
+ return mIsPrimaryApzcHolder;
+}
+
+uint64_t
+HitTestingTreeNode::GetLayersId() const
+{
+ return mLayersId;
+}
+
+void
+HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
+ const CSSTransformMatrix& aTransform,
+ const Maybe<ParentLayerIntRegion>& aClipRegion,
+ const EventRegionsOverride& aOverride)
+{
+ mEventRegions = aRegions;
+ mTransform = aTransform;
+ mClipRegion = aClipRegion;
+ mOverride = aOverride;
+}
+
+bool
+HitTestingTreeNode::IsOutsideClip(const ParentLayerPoint& aPoint) const
+{
+ // test against clip rect in ParentLayer coordinate space
+ return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
+}
+
+Maybe<LayerPoint>
+HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
+{
+ // convert into Layer coordinate space
+ LayerToParentLayerMatrix4x4 transform = mTransform *
+ CompleteAsyncTransform(
+ mApzc
+ ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
+ : AsyncTransformComponentMatrix());
+ return UntransformBy(transform.Inverse(), aPoint);
+}
+
+HitTestResult
+HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
+{
+ // This should only ever get called if the point is inside the clip region
+ // for this node.
+ MOZ_ASSERT(!IsOutsideClip(aPoint));
+
+ if (mOverride & EventRegionsOverride::ForceEmptyHitRegion) {
+ return HitTestResult::HitNothing;
+ }
+
+ // convert into Layer coordinate space
+ Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
+ if (!pointInLayerPixels) {
+ return HitTestResult::HitNothing;
+ }
+ auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
+
+ // test against event regions in Layer coordinate space
+ if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitNothing;
+ }
+ if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
+ mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
+ {
+ return HitTestResult::HitDispatchToContentRegion;
+ }
+ if (gfxPrefs::TouchActionEnabled()) {
+ if (mEventRegions.mNoActionRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitLayerTouchActionNone;
+ }
+ bool panX = mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y);
+ bool panY = mEventRegions.mVerticalPanRegion.Contains(point.x, point.y);
+ if (panX && panY) {
+ return HitTestResult::HitLayerTouchActionPanXY;
+ } else if (panX) {
+ return HitTestResult::HitLayerTouchActionPanX;
+ } else if (panY) {
+ return HitTestResult::HitLayerTouchActionPanY;
+ }
+ }
+ return HitTestResult::HitLayer;
+}
+
+EventRegionsOverride
+HitTestingTreeNode::GetEventRegionsOverride() const
+{
+ return mOverride;
+}
+
+void
+HitTestingTreeNode::Dump(const char* aPrefix) const
+{
+ if (mPrevSibling) {
+ mPrevSibling->Dump(aPrefix);
+ }
+ printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%sr=(%s) t=(%s) c=(%s)\n",
+ aPrefix, this, mApzc.get(),
+ mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=%" PRIu64, mLayersId).get(),
+ (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "",
+ (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
+ (mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() : "",
+ Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
+ mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
+ if (mLastChild) {
+ mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
+ }
+}
+
+void
+HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent)
+{
+ // precondition: GetApzc() is non-null
+ MOZ_ASSERT(GetApzc() != nullptr);
+ if (IsPrimaryHolder()) {
+ GetApzc()->SetParent(aParent);
+ } else {
+ MOZ_ASSERT(GetApzc()->GetParent() == aParent);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.h b/gfx/layers/apz/src/HitTestingTreeNode.h
new file mode 100644
index 000000000..442751a8d
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_HitTestingTreeNode_h
+#define mozilla_layers_HitTestingTreeNode_h
+
+#include "APZUtils.h" // for HitTestResult
+#include "FrameMetrics.h" // for ScrollableLayerGuid
+#include "Layers.h"
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/LayersTypes.h" // for EventRegions
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h" // for nsRefPtr
+
+namespace mozilla {
+namespace layers {
+
+class AsyncDragMetrics;
+class AsyncPanZoomController;
+
+/**
+ * This class represents a node in a tree that is used by the APZCTreeManager
+ * to do hit testing. The tree is roughly a copy of the layer tree, but will
+ * contain multiple nodes in cases where the layer has multiple FrameMetrics.
+ * In other words, the structure of this tree should be identical to the
+ * LayerMetrics tree (see documentation in LayerMetricsWrapper.h).
+ *
+ * Not all HitTestingTreeNode instances will have an APZC associated with them;
+ * only HitTestingTreeNodes that correspond to layers with scrollable metrics
+ * have APZCs.
+ * Multiple HitTestingTreeNode instances may share the same underlying APZC
+ * instance if the layers they represent share the same scrollable metrics (i.e.
+ * are part of the same animated geometry root). If this happens, exactly one of
+ * the HitTestingTreeNode instances will be designated as the "primary holder"
+ * of the APZC. When this primary holder is destroyed, it will destroy the APZC
+ * along with it; in contrast, destroying non-primary-holder nodes will not
+ * destroy the APZC.
+ * Code should not make assumptions about which of the nodes will be the
+ * primary holder, only that that there will be exactly one for each APZC in
+ * the tree.
+ *
+ * The reason this tree exists at all is so that we can do hit-testing on the
+ * thread that we receive input on (referred to the as the controller thread in
+ * APZ terminology), which may be different from the compositor thread.
+ * Accessing the compositor layer tree can only be done on the compositor
+ * thread, and so it is simpler to make a copy of the hit-testing related
+ * properties into a separate tree.
+ */
+class HitTestingTreeNode {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
+
+private:
+ ~HitTestingTreeNode();
+public:
+ HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder,
+ uint64_t aLayersId);
+ void RecycleWith(AsyncPanZoomController* aApzc, uint64_t aLayersId);
+ void Destroy();
+
+ /* Tree construction methods */
+
+ void SetLastChild(HitTestingTreeNode* aChild);
+ void SetPrevSibling(HitTestingTreeNode* aSibling);
+ void MakeRoot();
+
+ /* Tree walking methods. GetFirstChild is O(n) in the number of children. The
+ * other tree walking methods are all O(1). */
+
+ HitTestingTreeNode* GetFirstChild() const;
+ HitTestingTreeNode* GetLastChild() const;
+ HitTestingTreeNode* GetPrevSibling() const;
+ HitTestingTreeNode* GetParent() const;
+
+ /* APZC related methods */
+
+ AsyncPanZoomController* GetApzc() const;
+ AsyncPanZoomController* GetNearestContainingApzc() const;
+ bool IsPrimaryHolder() const;
+ uint64_t GetLayersId() const;
+
+ /* Hit test related methods */
+
+ void SetHitTestData(const EventRegions& aRegions,
+ const CSSTransformMatrix& aTransform,
+ const Maybe<ParentLayerIntRegion>& aClipRegion,
+ const EventRegionsOverride& aOverride);
+ bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
+
+ /* Scrollbar info */
+
+ void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+ Layer::ScrollDirection aDir,
+ int32_t aScrollSize,
+ bool aIsScrollContainer);
+ bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
+ int32_t GetScrollSize() const;
+ bool IsScrollbarNode() const;
+
+ /* Fixed pos info */
+
+ void SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget);
+ FrameMetrics::ViewID GetFixedPosTarget() const;
+
+ /* Convert aPoint into the LayerPixel space for the layer corresponding to
+ * this node. */
+ Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
+ /* Assuming aPoint is inside the clip region for this node, check which of the
+ * event region spaces it falls inside. */
+ HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
+ /* Returns the mOverride flag. */
+ EventRegionsOverride GetEventRegionsOverride() const;
+
+ /* Debug helpers */
+ void Dump(const char* aPrefix = "") const;
+
+private:
+ void SetApzcParent(AsyncPanZoomController* aApzc);
+
+ RefPtr<HitTestingTreeNode> mLastChild;
+ RefPtr<HitTestingTreeNode> mPrevSibling;
+ RefPtr<HitTestingTreeNode> mParent;
+
+ RefPtr<AsyncPanZoomController> mApzc;
+ bool mIsPrimaryApzcHolder;
+
+ uint64_t mLayersId;
+
+ FrameMetrics::ViewID mScrollViewId;
+ Layer::ScrollDirection mScrollDir;
+ int32_t mScrollSize;
+ bool mIsScrollbarContainer;
+
+ FrameMetrics::ViewID mFixedPosTarget;
+
+ /* Let {L,M} be the {layer, scrollable metrics} pair that this node
+ * corresponds to in the layer tree. mEventRegions contains the event regions
+ * from L, in the case where event-regions are enabled. If event-regions are
+ * disabled, it will contain the visible region of L, which we use as an
+ * approximation to the hit region for the purposes of obscuring other layers.
+ * This value is in L's LayerPixels.
+ */
+ EventRegions mEventRegions;
+
+ /* This is the transform from layer L. This does NOT include any async
+ * transforms. */
+ CSSTransformMatrix mTransform;
+
+ /* This is clip rect for L that we wish to use for hit-testing purposes. Note
+ * that this may not be exactly the same as the clip rect on layer L because
+ * of the touch-sensitive region provided by the GeckoContentController, or
+ * because we may use the composition bounds of the layer if the clip is not
+ * present. This value is in L's ParentLayerPixels. */
+ Maybe<ParentLayerIntRegion> mClipRegion;
+
+ /* Indicates whether or not the event regions on this node need to be
+ * overridden in a certain way. */
+ EventRegionsOverride mOverride;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_HitTestingTreeNode_h
diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp
new file mode 100644
index 000000000..f1310c031
--- /dev/null
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -0,0 +1,868 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputBlockState.h"
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "AsyncScrollBase.h" // for kScrollSeriesTimeoutMs
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/MouseEvents.h"
+#include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
+#include "mozilla/Telemetry.h" // for Telemetry
+#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
+#include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
+
+#define TBS_LOG(...)
+// #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
+
+InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed)
+ : mTargetApzc(aTargetApzc)
+ , mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed
+ : TargetConfirmationState::eUnconfirmed)
+ , mBlockId(sBlockCounter++)
+ , mTransformToApzc(aTargetApzc->GetTransformToThis())
+{
+ // We should never be constructed with a nullptr target.
+ MOZ_ASSERT(mTargetApzc);
+ mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
+}
+
+bool
+InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
+ || aState == TargetConfirmationState::eTimedOut);
+
+ if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
+ aState == TargetConfirmationState::eConfirmed) {
+ // The main thread finally responded. We had already timed out the
+ // confirmation, but we want to update the state internally so that we
+ // can record the time for telemetry purposes.
+ mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded;
+ }
+ if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) {
+ return false;
+ }
+ mTargetConfirmed = aState;
+
+ TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
+ if (mTargetApzc == aTargetApzc) {
+ // The confirmed target is the same as the tentative one, so we're done.
+ return true;
+ }
+
+ TBS_LOG("%p replacing unconfirmed target %p with real target %p\n",
+ this, mTargetApzc.get(), aTargetApzc.get());
+
+ UpdateTargetApzc(aTargetApzc);
+ return true;
+}
+
+void
+InputBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
+{
+ // note that aTargetApzc MAY be null here.
+ mTargetApzc = aTargetApzc;
+ mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : ScreenToParentLayerMatrix4x4();
+ mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr);
+}
+
+const RefPtr<AsyncPanZoomController>&
+InputBlockState::GetTargetApzc() const
+{
+ return mTargetApzc;
+}
+
+const RefPtr<const OverscrollHandoffChain>&
+InputBlockState::GetOverscrollHandoffChain() const
+{
+ return mOverscrollHandoffChain;
+}
+
+uint64_t
+InputBlockState::GetBlockId() const
+{
+ return mBlockId;
+}
+
+bool
+InputBlockState::IsTargetConfirmed() const
+{
+ return mTargetConfirmed != TargetConfirmationState::eUnconfirmed;
+}
+
+bool
+InputBlockState::HasReceivedRealConfirmedTarget() const
+{
+ return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
+ mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
+}
+
+bool
+InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const
+{
+ if (aA == aB) {
+ return true;
+ }
+
+ bool seenA = false;
+ for (size_t i = 0; i < mOverscrollHandoffChain->Length(); ++i) {
+ AsyncPanZoomController* apzc = mOverscrollHandoffChain->GetApzcAtIndex(i);
+ if (apzc == aB) {
+ return seenA;
+ }
+ if (apzc == aA) {
+ seenA = true;
+ }
+ }
+ return false;
+}
+
+
+void
+InputBlockState::SetScrolledApzc(AsyncPanZoomController* aApzc)
+{
+ // An input block should only have one scrolled APZC.
+ MOZ_ASSERT(!mScrolledApzc || (gfxPrefs::APZAllowImmediateHandoff() ? IsDownchainOf(mScrolledApzc, aApzc) : mScrolledApzc == aApzc));
+
+ mScrolledApzc = aApzc;
+}
+
+AsyncPanZoomController*
+InputBlockState::GetScrolledApzc() const
+{
+ return mScrolledApzc;
+}
+
+bool
+InputBlockState::IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const
+{
+ MOZ_ASSERT(aApzc && mScrolledApzc);
+
+ return IsDownchainOf(mScrolledApzc, aApzc);
+}
+
+CancelableBlockState::CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed)
+ : InputBlockState(aTargetApzc, aTargetConfirmed)
+ , mPreventDefault(false)
+ , mContentResponded(false)
+ , mContentResponseTimerExpired(false)
+{
+}
+
+bool
+CancelableBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (mContentResponded) {
+ return false;
+ }
+ TBS_LOG("%p got content response %d with timer expired %d\n",
+ this, aPreventDefault, mContentResponseTimerExpired);
+ mPreventDefault = aPreventDefault;
+ mContentResponded = true;
+ return true;
+}
+
+void
+CancelableBlockState::StartContentResponseTimer()
+{
+ MOZ_ASSERT(mContentResponseTimer.IsNull());
+ mContentResponseTimer = TimeStamp::Now();
+}
+
+bool
+CancelableBlockState::TimeoutContentResponse()
+{
+ if (mContentResponseTimerExpired) {
+ return false;
+ }
+ TBS_LOG("%p got content timer expired with response received %d\n",
+ this, mContentResponded);
+ if (!mContentResponded) {
+ mPreventDefault = false;
+ }
+ mContentResponseTimerExpired = true;
+ return true;
+}
+
+bool
+CancelableBlockState::IsContentResponseTimerExpired() const
+{
+ return mContentResponseTimerExpired;
+}
+
+bool
+CancelableBlockState::IsDefaultPrevented() const
+{
+ MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
+ return mPreventDefault;
+}
+
+bool
+CancelableBlockState::HasReceivedAllContentNotifications() const
+{
+ return HasReceivedRealConfirmedTarget() && mContentResponded;
+}
+
+bool
+CancelableBlockState::IsReadyForHandling() const
+{
+ if (!IsTargetConfirmed()) {
+ return false;
+ }
+ return mContentResponded || mContentResponseTimerExpired;
+}
+
+void
+CancelableBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
+}
+
+void
+CancelableBlockState::RecordContentResponseTime()
+{
+ if (!mContentResponseTimer) {
+ // We might get responses from content even though we didn't wait for them.
+ // In that case, ignore the time on them, because they're not relevant for
+ // tuning our timeout value. Also this function might get called multiple
+ // times on the same input block, so we should only record the time from the
+ // first successful call.
+ return;
+ }
+ if (!HasReceivedAllContentNotifications()) {
+ // Not done yet, we'll get called again
+ return;
+ }
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::CONTENT_RESPONSE_DURATION,
+ (uint32_t)(TimeStamp::Now() - mContentResponseTimer).ToMilliseconds());
+ mContentResponseTimer = TimeStamp();
+}
+
+DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const MouseInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mReceivedMouseUp(false)
+{
+}
+
+bool
+DragBlockState::HasReceivedMouseUp()
+{
+ return mReceivedMouseUp;
+}
+
+void
+DragBlockState::MarkMouseUpReceived()
+{
+ mReceivedMouseUp = true;
+}
+
+void
+DragBlockState::SetDragMetrics(const AsyncDragMetrics& aDragMetrics)
+{
+ mDragMetrics = aDragMetrics;
+}
+
+void
+DragBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ MouseInput mouseInput = aEvent.AsMouseInput();
+ if (!mouseInput.TransformToLocal(mTransformToApzc)) {
+ return;
+ }
+
+ GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics);
+}
+
+bool
+DragBlockState::MustStayActive()
+{
+ return !mReceivedMouseUp;
+}
+
+const char*
+DragBlockState::Type()
+{
+ return "drag";
+}
+// This is used to track the current wheel transaction.
+static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
+
+WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mScrollSeriesCounter(0)
+ , mTransactionEnded(false)
+{
+ sLastWheelBlockId = GetBlockId();
+
+ if (aTargetConfirmed) {
+ // Find the nearest APZC in the overscroll handoff chain that is scrollable.
+ // If we get a content confirmation later that the apzc is different, then
+ // content should have found a scrollable apzc, so we don't need to handle
+ // that case.
+ RefPtr<AsyncPanZoomController> apzc =
+ mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
+
+ // If nothing is scrollable, we don't consider this block as starting a
+ // transaction.
+ if (!apzc) {
+ EndTransaction();
+ return;
+ }
+
+ if (apzc != GetTargetApzc()) {
+ UpdateTargetApzc(apzc);
+ }
+ }
+}
+
+bool
+WheelBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (aPreventDefault) {
+ EndTransaction();
+ }
+ return CancelableBlockState::SetContentResponse(aPreventDefault);
+}
+
+bool
+WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ // The APZC that we find via APZCCallbackHelpers may not be the same APZC
+ // ESM or OverscrollHandoff would have computed. Make sure we get the right
+ // one by looking for the first apzc the next pending event can scroll.
+ RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
+ if (apzc && aFirstInput) {
+ apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
+ }
+
+ InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
+ return true;
+}
+
+void
+WheelBlockState::Update(ScrollWheelInput& aEvent)
+{
+ // We might not be in a transaction if the block never started in a
+ // transaction - for example, if nothing was scrollable.
+ if (!InTransaction()) {
+ return;
+ }
+
+ // The current "scroll series" is a like a sub-transaction. It has a separate
+ // timeout of 80ms. Since we need to compute wheel deltas at different phases
+ // of a transaction (for example, when it is updated, and later when the
+ // event action is taken), we affix the scroll series counter to the event.
+ // This makes GetScrollWheelDelta() consistent.
+ if (!mLastEventTime.IsNull() &&
+ (aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs)
+ {
+ mScrollSeriesCounter = 0;
+ }
+ aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter;
+
+ // If we can't scroll in the direction of the wheel event, we don't update
+ // the last move time. This allows us to timeout a transaction even if the
+ // mouse isn't moving.
+ //
+ // We skip this check if the target is not yet confirmed, so that when it is
+ // confirmed, we don't timeout the transaction.
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
+ return;
+ }
+
+ // Update the time of the last known good event, and reset the mouse move
+ // time to null. This will reset the delays on both the general transaction
+ // timeout and the mouse-move-in-frame timeout.
+ mLastEventTime = aEvent.mTimeStamp;
+ mLastMouseMove = TimeStamp();
+}
+
+bool
+WheelBlockState::MustStayActive()
+{
+ return !mTransactionEnded;
+}
+
+const char*
+WheelBlockState::Type()
+{
+ return "scroll wheel";
+}
+
+bool
+WheelBlockState::ShouldAcceptNewEvent() const
+{
+ if (!InTransaction()) {
+ // If we're not in a transaction, start a new one.
+ return false;
+ }
+
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ if (apzc->IsDestroyed()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+WheelBlockState::MaybeTimeout(const ScrollWheelInput& aEvent)
+{
+ MOZ_ASSERT(InTransaction());
+
+ if (MaybeTimeout(aEvent.mTimeStamp)) {
+ return true;
+ }
+
+ if (!mLastMouseMove.IsNull()) {
+ // If there's a recent mouse movement, we can time out the transaction early.
+ TimeDuration duration = TimeStamp::Now() - mLastMouseMove;
+ if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
+ TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
+ EndTransaction();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+WheelBlockState::MaybeTimeout(const TimeStamp& aTimeStamp)
+{
+ MOZ_ASSERT(InTransaction());
+
+ // End the transaction if the event occurred > 1.5s after the most recently
+ // seen wheel event.
+ TimeDuration duration = aTimeStamp - mLastEventTime;
+ if (duration.ToMilliseconds() < gfxPrefs::MouseWheelTransactionTimeoutMs()) {
+ return false;
+ }
+
+ TBS_LOG("%p wheel transaction timed out\n", this);
+
+ if (gfxPrefs::MouseScrollTestingEnabled()) {
+ RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
+ apzc->NotifyMozMouseScrollEvent(NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"));
+ }
+
+ EndTransaction();
+ return true;
+}
+
+void
+WheelBlockState::OnMouseMove(const ScreenIntPoint& aPoint)
+{
+ MOZ_ASSERT(InTransaction());
+
+ if (!GetTargetApzc()->Contains(aPoint)) {
+ EndTransaction();
+ return;
+ }
+
+ if (mLastMouseMove.IsNull()) {
+ // If the cursor is moving inside the frame, and it is more than the
+ // ignoremovedelay time since the last scroll operation, we record
+ // this as the most recent mouse movement.
+ TimeStamp now = TimeStamp::Now();
+ TimeDuration duration = now - mLastEventTime;
+ if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
+ mLastMouseMove = now;
+ }
+ }
+}
+
+void
+WheelBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
+{
+ InputBlockState::UpdateTargetApzc(aTargetApzc);
+
+ // If we found there was no target apzc, then we end the transaction.
+ if (!GetTargetApzc()) {
+ EndTransaction();
+ }
+}
+
+bool
+WheelBlockState::InTransaction() const
+{
+ // We consider a wheel block to be in a transaction if it has a confirmed
+ // target and is the most recent wheel input block to be created.
+ if (GetBlockId() != sLastWheelBlockId) {
+ return false;
+ }
+
+ if (mTransactionEnded) {
+ return false;
+ }
+
+ MOZ_ASSERT(GetTargetApzc());
+ return true;
+}
+
+bool
+WheelBlockState::AllowScrollHandoff() const
+{
+ // If we're in a wheel transaction, we do not allow overscroll handoff until
+ // a new event ends the wheel transaction.
+ return !IsTargetConfirmed() || !InTransaction();
+}
+
+void
+WheelBlockState::EndTransaction()
+{
+ TBS_LOG("%p ending wheel transaction\n", this);
+ mTransactionEnded = true;
+}
+
+PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const PanGestureInput& aInitialEvent)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mInterrupted(false)
+ , mWaitingForContentResponse(false)
+{
+ if (aTargetConfirmed) {
+ // Find the nearest APZC in the overscroll handoff chain that is scrollable.
+ // If we get a content confirmation later that the apzc is different, then
+ // content should have found a scrollable apzc, so we don't need to handle
+ // that case.
+ RefPtr<AsyncPanZoomController> apzc =
+ mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
+
+ if (apzc && apzc != GetTargetApzc()) {
+ UpdateTargetApzc(apzc);
+ }
+ }
+}
+
+bool
+PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput)
+{
+ // The APZC that we find via APZCCallbackHelpers may not be the same APZC
+ // ESM or OverscrollHandoff would have computed. Make sure we get the right
+ // one by looking for the first apzc the next pending event can scroll.
+ RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
+ if (apzc && aFirstInput) {
+ RefPtr<AsyncPanZoomController> scrollableApzc =
+ apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
+ if (scrollableApzc) {
+ apzc = scrollableApzc;
+ }
+ }
+
+ InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
+ return true;
+}
+
+bool
+PanGestureBlockState::MustStayActive()
+{
+ return !mInterrupted;
+}
+
+const char*
+PanGestureBlockState::Type()
+{
+ return "pan gesture";
+}
+
+bool
+PanGestureBlockState::SetContentResponse(bool aPreventDefault)
+{
+ if (aPreventDefault) {
+ TBS_LOG("%p setting interrupted flag\n", this);
+ mInterrupted = true;
+ }
+ bool stateChanged = CancelableBlockState::SetContentResponse(aPreventDefault);
+ if (mWaitingForContentResponse) {
+ mWaitingForContentResponse = false;
+ stateChanged = true;
+ }
+ return stateChanged;
+}
+
+bool
+PanGestureBlockState::HasReceivedAllContentNotifications() const
+{
+ return CancelableBlockState::HasReceivedAllContentNotifications()
+ && !mWaitingForContentResponse;
+}
+
+bool
+PanGestureBlockState::IsReadyForHandling() const
+{
+ if (!CancelableBlockState::IsReadyForHandling()) {
+ return false;
+ }
+ return !mWaitingForContentResponse ||
+ IsContentResponseTimerExpired();
+}
+
+bool
+PanGestureBlockState::AllowScrollHandoff() const
+{
+ return false;
+}
+
+void
+PanGestureBlockState::SetNeedsToWaitForContentResponse(bool aWaitForContentResponse)
+{
+ mWaitingForContentResponse = aWaitForContentResponse;
+}
+
+TouchBlockState::TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed, TouchCounter& aCounter)
+ : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+ , mAllowedTouchBehaviorSet(false)
+ , mDuringFastFling(false)
+ , mSingleTapOccurred(false)
+ , mInSlop(false)
+ , mTouchCounter(aCounter)
+{
+ TBS_LOG("Creating %p\n", this);
+ if (!gfxPrefs::TouchActionEnabled()) {
+ mAllowedTouchBehaviorSet = true;
+ }
+}
+
+bool
+TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
+{
+ if (mAllowedTouchBehaviorSet) {
+ return false;
+ }
+ TBS_LOG("%p got allowed touch behaviours for %" PRIuSIZE " points\n", this, aBehaviors.Length());
+ mAllowedTouchBehaviors.AppendElements(aBehaviors);
+ mAllowedTouchBehaviorSet = true;
+ return true;
+}
+
+bool
+TouchBlockState::GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const
+{
+ if (!mAllowedTouchBehaviorSet) {
+ return false;
+ }
+ aOutBehaviors.AppendElements(mAllowedTouchBehaviors);
+ return true;
+}
+
+void
+TouchBlockState::CopyPropertiesFrom(const TouchBlockState& aOther)
+{
+ TBS_LOG("%p copying properties from %p\n", this, &aOther);
+ if (gfxPrefs::TouchActionEnabled()) {
+ MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet || aOther.IsContentResponseTimerExpired());
+ SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
+ }
+ mTransformToApzc = aOther.mTransformToApzc;
+}
+
+bool
+TouchBlockState::HasReceivedAllContentNotifications() const
+{
+ return CancelableBlockState::HasReceivedAllContentNotifications()
+ // See comment in TouchBlockState::IsReadyforHandling()
+ && (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
+}
+
+bool
+TouchBlockState::IsReadyForHandling() const
+{
+ if (!CancelableBlockState::IsReadyForHandling()) {
+ return false;
+ }
+
+ if (!gfxPrefs::TouchActionEnabled()) {
+ // If TouchActionEnabled() was false when this block was created, then
+ // mAllowedTouchBehaviorSet is guaranteed to the true. However, the pref
+ // may have been flipped to false after the block was created. In that case,
+ // we should eventually get the touch-behaviour notification, or expire the
+ // content response timeout, but we don't really need to wait for those,
+ // since we don't care about the touch-behaviour values any more.
+ return true;
+ }
+
+ return mAllowedTouchBehaviorSet || IsContentResponseTimerExpired();
+}
+
+void
+TouchBlockState::SetDuringFastFling()
+{
+ TBS_LOG("%p setting fast-motion flag\n", this);
+ mDuringFastFling = true;
+}
+
+bool
+TouchBlockState::IsDuringFastFling() const
+{
+ return mDuringFastFling;
+}
+
+void
+TouchBlockState::SetSingleTapOccurred()
+{
+ TBS_LOG("%p setting single-tap-occurred flag\n", this);
+ mSingleTapOccurred = true;
+}
+
+bool
+TouchBlockState::SingleTapOccurred() const
+{
+ return mSingleTapOccurred;
+}
+
+bool
+TouchBlockState::MustStayActive()
+{
+ return true;
+}
+
+const char*
+TouchBlockState::Type()
+{
+ return "touch";
+}
+
+void
+TouchBlockState::DispatchEvent(const InputData& aEvent) const
+{
+ MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
+ mTouchCounter.Update(aEvent.AsMultiTouchInput());
+ CancelableBlockState::DispatchEvent(aEvent);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPinchZoom() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ // Pointer events specification requires that all touch points allow zoom.
+ for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+ if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsDoubleTapZoom() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+ if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningX() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningY() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningXY() const
+{
+ if (!gfxPrefs::TouchActionEnabled()) {
+ return true;
+ }
+ if (mAllowedTouchBehaviors.IsEmpty()) {
+ // Default to allowed
+ return true;
+ }
+ TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+ return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
+ && (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+bool
+TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
+ bool aApzcCanConsumeEvents)
+{
+ if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
+ // this is by definition the first event in this block. If it's the first
+ // touch, then we enter a slop state.
+ mInSlop = (aInput.mTouches.Length() == 1);
+ if (mInSlop) {
+ mSlopOrigin = aInput.mTouches[0].mScreenPoint;
+ TBS_LOG("%p entering slop with origin %s\n", this, Stringify(mSlopOrigin).c_str());
+ }
+ return false;
+ }
+ if (mInSlop) {
+ ScreenCoord threshold = aApzcCanConsumeEvents
+ ? AsyncPanZoomController::GetTouchStartTolerance()
+ : ScreenCoord(gfxPrefs::APZTouchMoveTolerance() * APZCTreeManager::GetDPI());
+ bool stayInSlop = (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) &&
+ (aInput.mTouches.Length() == 1) &&
+ ((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() < threshold);
+ if (!stayInSlop) {
+ // we're out of the slop zone, and will stay out for the remainder of
+ // this block
+ TBS_LOG("%p exiting slop\n", this);
+ mInSlop = false;
+ }
+ }
+ return mInSlop;
+}
+
+uint32_t
+TouchBlockState::GetActiveTouchCount() const
+{
+ return mTouchCounter.GetActiveTouchCount();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h
new file mode 100644
index 000000000..86fb0d03f
--- /dev/null
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputBlockState_h
+#define mozilla_layers_InputBlockState_h
+
+#include "InputData.h" // for MultiTouchInput
+#include "mozilla/RefCounted.h" // for RefCounted
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/APZUtils.h" // for TouchBehaviorFlags
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsTArray.h" // for nsTArray
+#include "TouchCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class OverscrollHandoffChain;
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+
+/**
+ * A base class that stores state common to various input blocks.
+ * Note that the InputBlockState constructor acquires the tree lock, so callers
+ * from inside AsyncPanZoomController should ensure that the APZC lock is not
+ * held.
+ */
+class InputBlockState : public RefCounted<InputBlockState>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(InputBlockState)
+
+ static const uint64_t NO_BLOCK_ID = 0;
+
+ enum class TargetConfirmationState {
+ eUnconfirmed,
+ eTimedOut,
+ eTimedOutAndMainThreadResponded,
+ eConfirmed
+ };
+
+ explicit InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed);
+ virtual ~InputBlockState()
+ {}
+
+ virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput);
+ const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
+ const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
+ uint64_t GetBlockId() const;
+
+ bool IsTargetConfirmed() const;
+ bool HasReceivedRealConfirmedTarget() const;
+
+ void SetScrolledApzc(AsyncPanZoomController* aApzc);
+ AsyncPanZoomController* GetScrolledApzc() const;
+ bool IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const;
+
+protected:
+ virtual void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc);
+
+private:
+ // Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in
+ // |mOverscrollHandoffChain|.
+ bool IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const;
+
+private:
+ RefPtr<AsyncPanZoomController> mTargetApzc;
+ TargetConfirmationState mTargetConfirmed;
+ const uint64_t mBlockId;
+
+ // The APZC that was actually scrolled by events in this input block.
+ // This is used in configurations where a single input block is only
+ // allowed to scroll a single APZC (configurations where gfxPrefs::
+ // APZAllowImmediateHandoff() is false).
+ // Set the first time an input event in this block scrolls an APZC.
+ RefPtr<AsyncPanZoomController> mScrolledApzc;
+protected:
+ RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+
+ // Used to transform events from global screen space to |mTargetApzc|'s
+ // screen space. It's cached at the beginning of the input block so that
+ // all events in the block are in the same coordinate space.
+ ScreenToParentLayerMatrix4x4 mTransformToApzc;
+};
+
+/**
+ * This class represents a set of events that can be cancelled by web content
+ * via event listeners.
+ *
+ * Each cancelable input block can be cancelled by web content, and
+ * this information is stored in the mPreventDefault flag. Because web
+ * content runs on the Gecko main thread, we cannot always wait for web content's
+ * response. Instead, there is a timeout that sets this flag in the case
+ * where web content doesn't respond in time. The mContentResponded
+ * and mContentResponseTimerExpired flags indicate which of these scenarios
+ * occurred.
+ */
+class CancelableBlockState : public InputBlockState
+{
+public:
+ CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed);
+
+ virtual TouchBlockState *AsTouchBlock() {
+ return nullptr;
+ }
+ virtual WheelBlockState *AsWheelBlock() {
+ return nullptr;
+ }
+ virtual DragBlockState *AsDragBlock() {
+ return nullptr;
+ }
+ virtual PanGestureBlockState *AsPanGestureBlock() {
+ return nullptr;
+ }
+
+ /**
+ * Record whether or not content cancelled this block of events.
+ * @param aPreventDefault true iff the block is cancelled.
+ * @return false if this block has already received a response from
+ * web content, true if not.
+ */
+ virtual bool SetContentResponse(bool aPreventDefault);
+
+ /**
+ * This should be called when this block is starting to wait for the
+ * necessary content response notifications. It is used to gather data
+ * on how long the content response notifications take.
+ */
+ void StartContentResponseTimer();
+
+ /**
+ * This should be called when a content response notification has been
+ * delivered to this block. If all the notifications have arrived, this
+ * will report the total time take to telemetry.
+ */
+ void RecordContentResponseTime();
+
+ /**
+ * Record that content didn't respond in time.
+ * @return false if this block already timed out, true if not.
+ */
+ bool TimeoutContentResponse();
+
+ /**
+ * Checks if the content response timer has already expired.
+ */
+ bool IsContentResponseTimerExpired() const;
+
+ /**
+ * @return true iff web content cancelled this block of events.
+ */
+ bool IsDefaultPrevented() const;
+
+ /**
+ * Dispatch the event to the target APZC. Mostly this is a hook for
+ * subclasses to do any per-event processing they need to.
+ */
+ virtual void DispatchEvent(const InputData& aEvent) const;
+
+ /**
+ * @return true iff this block has received all the information it could
+ * have gotten from the content thread.
+ */
+ virtual bool HasReceivedAllContentNotifications() const;
+
+ /**
+ * @return true iff this block has received all the information needed
+ * to properly dispatch the events in the block.
+ */
+ virtual bool IsReadyForHandling() const;
+
+ /**
+ * Return true if this input block must stay active if it would otherwise
+ * be removed as the last item in the pending queue.
+ */
+ virtual bool MustStayActive() = 0;
+
+ /**
+ * Return a descriptive name for the block kind.
+ */
+ virtual const char* Type() = 0;
+
+private:
+ TimeStamp mContentResponseTimer;
+ bool mPreventDefault;
+ bool mContentResponded;
+ bool mContentResponseTimerExpired;
+};
+
+/**
+ * A single block of wheel events.
+ */
+class WheelBlockState : public CancelableBlockState
+{
+public:
+ WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent);
+
+ bool SetContentResponse(bool aPreventDefault) override;
+ bool MustStayActive() override;
+ const char* Type() override;
+ bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput) override;
+
+ WheelBlockState *AsWheelBlock() override {
+ return this;
+ }
+
+ /**
+ * Determine whether this wheel block is accepting new events.
+ */
+ bool ShouldAcceptNewEvent() const;
+
+ /**
+ * Call to check whether a wheel event will cause the current transaction to
+ * timeout.
+ */
+ bool MaybeTimeout(const ScrollWheelInput& aEvent);
+
+ /**
+ * Called from APZCTM when a mouse move or drag+drop event occurs, before
+ * the event has been processed.
+ */
+ void OnMouseMove(const ScreenIntPoint& aPoint);
+
+ /**
+ * Returns whether or not the block is participating in a wheel transaction.
+ * This means that the block is the most recent input block to be created,
+ * and no events have occurred that would require scrolling a different
+ * frame.
+ *
+ * @return True if in a transaction, false otherwise.
+ */
+ bool InTransaction() const;
+
+ /**
+ * Mark the block as no longer participating in a wheel transaction. This
+ * will force future wheel events to begin a new input block.
+ */
+ void EndTransaction();
+
+ /**
+ * @return Whether or not overscrolling is prevented for this wheel block.
+ */
+ bool AllowScrollHandoff() const;
+
+ /**
+ * Called to check and possibly end the transaction due to a timeout.
+ *
+ * @return True if the transaction ended, false otherwise.
+ */
+ bool MaybeTimeout(const TimeStamp& aTimeStamp);
+
+ /**
+ * Update the wheel transaction state for a new event.
+ */
+ void Update(ScrollWheelInput& aEvent);
+
+protected:
+ void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
+
+private:
+ TimeStamp mLastEventTime;
+ TimeStamp mLastMouseMove;
+ uint32_t mScrollSeriesCounter;
+ bool mTransactionEnded;
+};
+
+/**
+ * A block of mouse events that are part of a drag
+ */
+class DragBlockState : public CancelableBlockState
+{
+public:
+ DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent);
+
+ bool MustStayActive() override;
+ const char* Type() override;
+
+ bool HasReceivedMouseUp();
+ void MarkMouseUpReceived();
+
+ DragBlockState *AsDragBlock() override {
+ return this;
+ }
+
+ void SetDragMetrics(const AsyncDragMetrics& aDragMetrics);
+
+ void DispatchEvent(const InputData& aEvent) const override;
+private:
+ AsyncDragMetrics mDragMetrics;
+ bool mReceivedMouseUp;
+};
+
+/**
+ * A single block of pan gesture events.
+ */
+class PanGestureBlockState : public CancelableBlockState
+{
+public:
+ PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent);
+
+ bool SetContentResponse(bool aPreventDefault) override;
+ bool HasReceivedAllContentNotifications() const override;
+ bool IsReadyForHandling() const override;
+ bool MustStayActive() override;
+ const char* Type() override;
+ bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ TargetConfirmationState aState,
+ InputData* aFirstInput) override;
+
+ PanGestureBlockState *AsPanGestureBlock() override {
+ return this;
+ }
+
+ /**
+ * @return Whether or not overscrolling is prevented for this block.
+ */
+ bool AllowScrollHandoff() const;
+
+ bool WasInterrupted() const { return mInterrupted; }
+
+ void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
+
+private:
+ bool mInterrupted;
+ bool mWaitingForContentResponse;
+};
+
+/**
+ * This class represents a single touch block. A touch block is
+ * a set of touch events that can be cancelled by web content via
+ * touch event listeners.
+ *
+ * Every touch-start event creates a new touch block. In this case, the
+ * touch block consists of the touch-start, followed by all touch events
+ * up to but not including the next touch-start (except in the case where
+ * a long-tap happens, see below). Note that in particular we cannot know
+ * when a touch block ends until the next one is started. Most touch
+ * blocks are created by receipt of a touch-start event.
+ *
+ * Every long-tap event also creates a new touch block, since it can also
+ * be consumed by web content. In this case, when the long-tap event is
+ * dispatched to web content, a new touch block is started to hold the remaining
+ * touch events, up to but not including the next touch start (or long-tap).
+ *
+ * Additionally, if touch-action is enabled, each touch block should
+ * have a set of allowed touch behavior flags; one for each touch point.
+ * This also requires running code on the Gecko main thread, and so may
+ * be populated with some latency. The mAllowedTouchBehaviorSet and
+ * mAllowedTouchBehaviors variables track this information.
+ */
+class TouchBlockState : public CancelableBlockState
+{
+public:
+ explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ bool aTargetConfirmed, TouchCounter& aTouchCounter);
+
+ TouchBlockState *AsTouchBlock() override {
+ return this;
+ }
+
+ /**
+ * Set the allowed touch behavior flags for this block.
+ * @return false if this block already has these flags set, true if not.
+ */
+ bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+ /**
+ * If the allowed touch behaviors have been set, populate them into
+ * |aOutBehaviors| and return true. Else, return false.
+ */
+ bool GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const;
+
+ /**
+ * Copy various properties from another block.
+ */
+ void CopyPropertiesFrom(const TouchBlockState& aOther);
+
+ /*
+ * @return true iff this block has received all the information it could
+ * have gotten from the content thread.
+ */
+ bool HasReceivedAllContentNotifications() const override;
+
+ /**
+ * @return true iff this block has received all the information needed
+ * to properly dispatch the events in the block.
+ */
+ bool IsReadyForHandling() const override;
+
+ /**
+ * Sets a flag that indicates this input block occurred while the APZ was
+ * in a state of fast flinging. This affects gestures that may be produced
+ * from input events in this block.
+ */
+ void SetDuringFastFling();
+ /**
+ * @return true iff SetDuringFastFling was called on this block.
+ */
+ bool IsDuringFastFling() const;
+ /**
+ * Set the single-tap-occurred flag that indicates that this touch block
+ * triggered a single tap event.
+ */
+ void SetSingleTapOccurred();
+ /**
+ * @return true iff the single-tap-occurred flag is set on this block.
+ */
+ bool SingleTapOccurred() const;
+
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * this touch block do not allow pinch-zooming.
+ */
+ bool TouchActionAllowsPinchZoom() const;
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * this touch block do not allow double-tap zooming.
+ */
+ bool TouchActionAllowsDoubleTapZoom() const;
+ /**
+ * @return false iff touch-action is enabled and the allowed touch behaviors for
+ * the first touch point do not allow panning in the specified direction(s).
+ */
+ bool TouchActionAllowsPanningX() const;
+ bool TouchActionAllowsPanningY() const;
+ bool TouchActionAllowsPanningXY() const;
+
+ /**
+ * Notifies the input block of an incoming touch event so that the block can
+ * update its internal slop state. "Slop" refers to the area around the
+ * initial touchstart where we drop touchmove events so that content doesn't
+ * see them. The |aApzcCanConsumeEvents| parameter is factored into how large
+ * the slop area is - if this is true the slop area is larger.
+ * @return true iff the provided event is a touchmove in the slop area and
+ * so should not be sent to content.
+ */
+ bool UpdateSlopState(const MultiTouchInput& aInput,
+ bool aApzcCanConsumeEvents);
+
+ /**
+ * Returns the number of touch points currently active.
+ */
+ uint32_t GetActiveTouchCount() const;
+
+ void DispatchEvent(const InputData& aEvent) const override;
+ bool MustStayActive() override;
+ const char* Type() override;
+
+private:
+ nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
+ bool mAllowedTouchBehaviorSet;
+ bool mDuringFastFling;
+ bool mSingleTapOccurred;
+ bool mInSlop;
+ ScreenIntPoint mSlopOrigin;
+ // A reference to the InputQueue's touch counter
+ TouchCounter& mTouchCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_InputBlockState_h
diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp
new file mode 100644
index 000000000..820526d52
--- /dev/null
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -0,0 +1,731 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputQueue.h"
+
+#include "AsyncPanZoomController.h"
+#include "gfxPrefs.h"
+#include "InputBlockState.h"
+#include "LayersLogging.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "OverscrollHandoffState.h"
+#include "QueuedInput.h"
+
+#define INPQ_LOG(...)
+// #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+InputQueue::InputQueue()
+{
+}
+
+InputQueue::~InputQueue() {
+ mQueuedInputs.Clear();
+}
+
+nsEventStatus
+InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const InputData& aEvent,
+ uint64_t* aOutInputBlockId) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ const MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ return ReceiveTouchInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case SCROLLWHEEL_INPUT: {
+ const ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ return ReceiveScrollWheelInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case PANGESTURE_INPUT: {
+ const PanGestureInput& event = aEvent.AsPanGestureInput();
+ return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ case MOUSE_INPUT: {
+ const MouseInput& event = aEvent.AsMouseInput();
+ return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
+ }
+
+ default:
+ // The return value for non-touch input is only used by tests, so just pass
+ // through the return value for now. This can be changed later if needed.
+ // TODO (bug 1098430): we will eventually need to have smarter handling for
+ // non-touch events as well.
+ return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
+ }
+}
+
+nsEventStatus
+InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MultiTouchInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ TouchBlockState* block = nullptr;
+ if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) {
+ nsTArray<TouchBehaviorFlags> currentBehaviors;
+ bool haveBehaviors = false;
+ if (!gfxPrefs::TouchActionEnabled()) {
+ haveBehaviors = true;
+ } else if (mActiveTouchBlock) {
+ haveBehaviors = mActiveTouchBlock->GetAllowedTouchBehaviors(currentBehaviors);
+ // If the behaviours aren't set, but the main-thread response timer on
+ // the block is expired we still treat it as though it has behaviors,
+ // because in that case we still want to interrupt the fast-fling and
+ // use the default behaviours.
+ haveBehaviors |= mActiveTouchBlock->IsContentResponseTimerExpired();
+ }
+
+ block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
+ INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ // XXX using the chain from |block| here may be wrong in cases where the
+ // target isn't confirmed and the real target turns out to be something
+ // else. For now assume this is rare enough that it's not an issue.
+ if (mQueuedInputs.IsEmpty() &&
+ aEvent.mTouches.Length() == 1 &&
+ block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
+ haveBehaviors) {
+ // If we're already in a fast fling, and a single finger goes down, then
+ // we want special handling for the touch event, because it shouldn't get
+ // delivered to content. Note that we don't set this flag when going
+ // from a fast fling to a pinch state (i.e. second finger goes down while
+ // the first finger is moving).
+ block->SetDuringFastFling();
+ block->SetConfirmedTargetApzc(aTarget,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ nullptr /* the block was just created so it has no events */);
+ if (gfxPrefs::TouchActionEnabled()) {
+ block->SetAllowedTouchBehaviors(currentBehaviors);
+ }
+ INPQ_LOG("block %p tagged as fast-motion\n", block);
+ }
+
+ CancelAnimationsForNewBlock(block);
+
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ block = mActiveTouchBlock.get();
+ if (!block) {
+ NS_WARNING("Received a non-start touch event while no touch blocks active!");
+ return nsEventStatus_eIgnore;
+ }
+
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block.
+ RefPtr<AsyncPanZoomController> target = block->GetTargetApzc();
+
+ nsEventStatus result = nsEventStatus_eIgnore;
+
+ // XXX calling ArePointerEventsConsumable on |target| may be wrong here if
+ // the target isn't confirmed and the real target turns out to be something
+ // else. For now assume this is rare enough that it's not an issue.
+ if (block->IsDuringFastFling()) {
+ INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ } else if (target && target->ArePointerEventsConsumable(block, aEvent.mTouches.Length())) {
+ if (block->UpdateSlopState(aEvent, true)) {
+ INPQ_LOG("dropping event due to block %p being in slop\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ } else {
+ result = nsEventStatus_eConsumeDoDefault;
+ }
+ } else if (block->UpdateSlopState(aEvent, false)) {
+ INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
+ result = nsEventStatus_eConsumeNoDefault;
+ }
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+ ProcessQueue();
+ return result;
+}
+
+nsEventStatus
+InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ // On a new mouse down we can have a new target so we must force a new block
+ // with a new target.
+ bool newBlock = DragTracker::StartsDrag(aEvent);
+
+ DragBlockState* block = newBlock ? nullptr : mActiveDragBlock.get();
+ if (block && block->HasReceivedMouseUp()) {
+ block = nullptr;
+ }
+
+ if (!block && mDragTracker.InDrag()) {
+ // If there's no current drag block, but we're getting a move with a button
+ // down, we need to start a new drag block because we're obviously already
+ // in the middle of a drag (it probably got interrupted by something else).
+ INPQ_LOG("got a drag event outside a drag block, need to create a block to hold it\n");
+ newBlock = true;
+ }
+
+ mDragTracker.Update(aEvent);
+
+ if (!newBlock && !block) {
+ // This input event is not in a drag block, so we're not doing anything
+ // with it, return eIgnore.
+ return nsEventStatus_eIgnore;
+ }
+
+ if (!block) {
+ MOZ_ASSERT(newBlock);
+ block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
+
+ INPQ_LOG("started new drag block %p id %" PRIu64 " for %sconfirmed target %p\n",
+ block, block->GetBlockId(), aTargetConfirmed ? "" : "un", aTarget.get());
+
+ mActiveDragBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+ ProcessQueue();
+
+ if (DragTracker::EndsDrag(aEvent)) {
+ block->MarkMouseUpReceived();
+ }
+
+ // The event is part of a drag block and could potentially cause
+ // scrolling, so return DoDefault.
+ return nsEventStatus_eConsumeDoDefault;
+}
+
+nsEventStatus
+InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ WheelBlockState* block = mActiveWheelBlock.get();
+ // If the block is not accepting new events we'll create a new input block
+ // (and therefore a new wheel transaction).
+ if (block &&
+ (!block->ShouldAcceptNewEvent() ||
+ block->MaybeTimeout(aEvent)))
+ {
+ block = nullptr;
+ }
+
+ MOZ_ASSERT(!block || block->InTransaction());
+
+ if (!block) {
+ block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
+ INPQ_LOG("started new scroll wheel block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ mActiveWheelBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block, which is what
+ // ProcessQueue() does.
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
+
+ // The WheelBlockState needs to affix a counter to the event before we process
+ // it. Note that the counter is affixed to the copy in the queue rather than
+ // |aEvent|.
+ block->Update(mQueuedInputs.LastElement()->Input()->AsScrollWheelInput());
+
+ ProcessQueue();
+
+ return nsEventStatus_eConsumeDoDefault;
+}
+
+static bool
+CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
+ PanGestureBlockState* aBlock)
+{
+ PanGestureInput horizontalComponent = aInitialEvent;
+ horizontalComponent.mPanDisplacement.y = 0;
+ RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
+ aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent);
+ return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc();
+}
+
+nsEventStatus
+InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent,
+ uint64_t* aOutInputBlockId) {
+ if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
+ aEvent.mType == PanGestureInput::PANGESTURE_CANCELLED) {
+ // Ignore these events for now.
+ return nsEventStatus_eConsumeDoDefault;
+ }
+
+ PanGestureBlockState* block = nullptr;
+ if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
+ block = mActivePanGestureBlock.get();
+ }
+
+ PanGestureInput event = aEvent;
+ nsEventStatus result = nsEventStatus_eConsumeDoDefault;
+
+ if (!block || block->WasInterrupted()) {
+ if (event.mType != PanGestureInput::PANGESTURE_START) {
+ // Only PANGESTURE_START events are allowed to start a new pan gesture
+ // block, but we really want to start a new block here, so we magically
+ // turn this input into a PANGESTURE_START.
+ INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n",
+ event.mType);
+ event.mType = PanGestureInput::PANGESTURE_START;
+ }
+ block = new PanGestureBlockState(aTarget, aTargetConfirmed, event);
+ INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n",
+ block, block->GetBlockId(), aTarget.get());
+
+ if (aTargetConfirmed &&
+ event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
+ !CanScrollTargetHorizontally(event, block)) {
+ // This event may trigger a swipe gesture, depending on what our caller
+ // wants to do it. We need to suspend handling of this block until we get
+ // a content response which will tell us whether to proceed or abort the
+ // block.
+ block->SetNeedsToWaitForContentResponse(true);
+
+ // Inform our caller that we haven't scrolled in response to the event
+ // and that a swipe can be started from this event if desired.
+ result = nsEventStatus_eIgnore;
+ }
+
+ mActivePanGestureBlock = block;
+
+ CancelAnimationsForNewBlock(block);
+ MaybeRequestContentResponse(aTarget, block);
+ } else {
+ INPQ_LOG("received new event in block %p\n", block);
+ }
+
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = block->GetBlockId();
+ }
+
+ // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
+ // target set on the block. In this case the confirmed target (which may be
+ // null) should take priority. This is equivalent to just always using the
+ // target (confirmed or not) from the block, which is what
+ // ProcessQueue() does.
+ mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(event, *block));
+ ProcessQueue();
+
+ return result;
+}
+
+void
+InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
+{
+ // We want to cancel animations here as soon as possible (i.e. without waiting for
+ // content responses) because a finger has gone down and we don't want to keep moving
+ // the content under the finger. However, to prevent "future" touchstart events from
+ // interfering with "past" animations (i.e. from a previous touch block that is still
+ // being processed) we only do this animation-cancellation if there are no older
+ // touch blocks still in the queue.
+ if (mQueuedInputs.IsEmpty()) {
+ aBlock->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll | ScrollSnap);
+ }
+}
+
+void
+InputQueue::MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock)
+{
+ bool waitForMainThread = false;
+ if (aBlock->IsTargetConfirmed()) {
+ // Content won't prevent-default this, so we can just set the flag directly.
+ INPQ_LOG("not waiting for content response on block %p\n", aBlock);
+ aBlock->SetContentResponse(false);
+ } else {
+ waitForMainThread = true;
+ }
+ if (aBlock->AsTouchBlock() && gfxPrefs::TouchActionEnabled()) {
+ // waitForMainThread is set to true unconditionally here, but if the APZCTM
+ // has the touch-action behaviours for this block, it will set it
+ // immediately after we unwind out of this ReceiveInputEvent call. So even
+ // though we are scheduling the main-thread timeout, we might end up not
+ // waiting.
+ INPQ_LOG("waiting for main thread touch-action info on block %p\n", aBlock);
+ waitForMainThread = true;
+ }
+ if (waitForMainThread) {
+ // We either don't know for sure if aTarget is the right APZC, or we may
+ // need to wait to give content the opportunity to prevent-default the
+ // touch events. Either way we schedule a timeout so the main thread stuff
+ // can run.
+ ScheduleMainThreadTimeout(aTarget, aBlock);
+ }
+}
+
+uint64_t
+InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
+{
+ TouchBlockState* block = StartNewTouchBlock(aTarget,
+ /* aTargetConfirmed = */ true,
+ /* aCopyPropertiesFromCurrent = */ true);
+ INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
+ block, block->GetBlockId(), aTarget);
+ ScheduleMainThreadTimeout(aTarget, block);
+ return block->GetBlockId();
+}
+
+TouchBlockState*
+InputQueue::StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ bool aCopyPropertiesFromCurrent)
+{
+ TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
+ mTouchCounter);
+ if (aCopyPropertiesFromCurrent) {
+ // We should never enter here without a current touch block, because this
+ // codepath is invoked from the OnLongPress handler in
+ // AsyncPanZoomController, which should bail out if there is no current
+ // touch block.
+ MOZ_ASSERT(GetCurrentTouchBlock());
+ newBlock->CopyPropertiesFrom(*GetCurrentTouchBlock());
+ }
+
+ mActiveTouchBlock = newBlock;
+ return newBlock;
+}
+
+CancelableBlockState*
+InputQueue::GetCurrentBlock() const
+{
+ APZThreadUtils::AssertOnControllerThread();
+ return mQueuedInputs.IsEmpty() ? nullptr : mQueuedInputs[0]->Block();
+}
+
+TouchBlockState*
+InputQueue::GetCurrentTouchBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsTouchBlock() : mActiveTouchBlock.get();
+}
+
+WheelBlockState*
+InputQueue::GetCurrentWheelBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsWheelBlock() : mActiveWheelBlock.get();
+}
+
+DragBlockState*
+InputQueue::GetCurrentDragBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsDragBlock() : mActiveDragBlock.get();
+}
+
+PanGestureBlockState*
+InputQueue::GetCurrentPanGestureBlock() const
+{
+ CancelableBlockState* block = GetCurrentBlock();
+ return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
+}
+
+WheelBlockState*
+InputQueue::GetActiveWheelTransaction() const
+{
+ WheelBlockState* block = mActiveWheelBlock.get();
+ if (!block || !block->InTransaction()) {
+ return nullptr;
+ }
+ return block;
+}
+
+bool
+InputQueue::HasReadyTouchBlock() const
+{
+ return !mQueuedInputs.IsEmpty() &&
+ mQueuedInputs[0]->Block()->AsTouchBlock() &&
+ mQueuedInputs[0]->Block()->IsReadyForHandling();
+}
+
+bool
+InputQueue::AllowScrollHandoff() const
+{
+ if (GetCurrentWheelBlock()) {
+ return GetCurrentWheelBlock()->AllowScrollHandoff();
+ }
+ if (GetCurrentPanGestureBlock()) {
+ return GetCurrentPanGestureBlock()->AllowScrollHandoff();
+ }
+ return true;
+}
+
+bool
+InputQueue::IsDragOnScrollbar(bool aHitScrollbar)
+{
+ if (!mDragTracker.InDrag()) {
+ return false;
+ }
+ // Now that we know we are in a drag, get the info from the drag tracker.
+ // We keep it in the tracker rather than the block because the block can get
+ // interrupted by something else (like a wheel event) and then a new block
+ // will get created without the info we want. The tracker will persist though.
+ return mDragTracker.IsOnScrollbar(aHitScrollbar);
+}
+
+void
+InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock) {
+ INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
+ aBlock->StartContentResponseTimer();
+ aTarget->PostDelayedTask(NewRunnableMethod<uint64_t>(this,
+ &InputQueue::MainThreadTimeout,
+ aBlock->GetBlockId()),
+ gfxPrefs::APZContentResponseTimeout());
+}
+
+CancelableBlockState*
+InputQueue::FindBlockForId(uint64_t aInputBlockId,
+ InputData** aOutFirstInput)
+{
+ for (const auto& queuedInput : mQueuedInputs) {
+ if (queuedInput->Block()->GetBlockId() == aInputBlockId) {
+ if (aOutFirstInput) {
+ *aOutFirstInput = queuedInput->Input();
+ }
+ return queuedInput->Block();
+ }
+ }
+
+ CancelableBlockState* block = nullptr;
+ if (mActiveTouchBlock && mActiveTouchBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveTouchBlock.get();
+ } else if (mActiveWheelBlock && mActiveWheelBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveWheelBlock.get();
+ } else if (mActiveDragBlock && mActiveDragBlock->GetBlockId() == aInputBlockId) {
+ block = mActiveDragBlock.get();
+ } else if (mActivePanGestureBlock && mActivePanGestureBlock->GetBlockId() == aInputBlockId) {
+ block = mActivePanGestureBlock.get();
+ }
+ // Since we didn't encounter this block while iterating through mQueuedInputs,
+ // it must have no events associated with it at the moment.
+ if (aOutFirstInput) {
+ *aOutFirstInput = nullptr;
+ }
+ return block;
+}
+
+void
+InputQueue::MainThreadTimeout(uint64_t aInputBlockId) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block) {
+ // time out the touch-listener response and also confirm the existing
+ // target apzc in the case where the main thread doesn't get back to us
+ // fast enough.
+ success = block->TimeoutContentResponse();
+ success |= block->SetConfirmedTargetApzc(
+ block->GetTargetApzc(),
+ InputBlockState::TargetConfirmationState::eTimedOut,
+ firstInput);
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+ if (block) {
+ success = block->SetContentResponse(aPreventDefault);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
+ aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block) {
+ success = block->SetConfirmedTargetApzc(aTargetApzc,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ firstInput);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
+ aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
+ bool success = false;
+ InputData* firstInput = nullptr;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, &firstInput);
+ if (block && block->AsDragBlock()) {
+ block->AsDragBlock()->SetDragMetrics(aDragMetrics);
+ success = block->SetConfirmedTargetApzc(aTargetApzc,
+ InputBlockState::TargetConfirmationState::eConfirmed,
+ firstInput);
+ block->RecordContentResponseTime();
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
+ bool success = false;
+ CancelableBlockState* block = FindBlockForId(aInputBlockId, nullptr);
+ if (block && block->AsTouchBlock()) {
+ success = block->AsTouchBlock()->SetAllowedTouchBehaviors(aBehaviors);
+ block->RecordContentResponseTime();
+ } else if (block) {
+ NS_WARNING("input block is not a touch block");
+ }
+ if (success) {
+ ProcessQueue();
+ }
+}
+
+void
+InputQueue::ProcessQueue() {
+ APZThreadUtils::AssertOnControllerThread();
+
+ while (!mQueuedInputs.IsEmpty()) {
+ CancelableBlockState* curBlock = mQueuedInputs[0]->Block();
+ if (!curBlock->IsReadyForHandling()) {
+ break;
+ }
+
+ INPQ_LOG("processing input from block %p; preventDefault %d target %p\n",
+ curBlock, curBlock->IsDefaultPrevented(),
+ curBlock->GetTargetApzc().get());
+ RefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
+ // target may be null here if the initial target was unconfirmed and then
+ // we later got a confirmed null target. in that case drop the events.
+ if (target) {
+ if (curBlock->IsDefaultPrevented()) {
+ if (curBlock->AsTouchBlock()) {
+ target->ResetTouchInputState();
+ }
+ } else {
+ UpdateActiveApzc(target);
+ curBlock->DispatchEvent(*(mQueuedInputs[0]->Input()));
+ }
+ }
+ mQueuedInputs.RemoveElementAt(0);
+ }
+
+ if (CanDiscardBlock(mActiveTouchBlock)) {
+ mActiveTouchBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActiveWheelBlock)) {
+ mActiveWheelBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActiveDragBlock)) {
+ mActiveDragBlock = nullptr;
+ }
+ if (CanDiscardBlock(mActivePanGestureBlock)) {
+ mActivePanGestureBlock = nullptr;
+ }
+}
+
+bool
+InputQueue::CanDiscardBlock(CancelableBlockState* aBlock)
+{
+ if (!aBlock ||
+ !aBlock->IsReadyForHandling() ||
+ aBlock->MustStayActive()) {
+ return false;
+ }
+ InputData* firstInput = nullptr;
+ FindBlockForId(aBlock->GetBlockId(), &firstInput);
+ if (firstInput) {
+ // The block has at least one input event still in the queue, so it's
+ // not depleted
+ return false;
+ }
+ return true;
+}
+
+void
+InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
+ if (mLastActiveApzc && mLastActiveApzc != aNewActive
+ && mTouchCounter.GetActiveTouchCount() > 0) {
+ mLastActiveApzc->ResetTouchInputState();
+ }
+ mLastActiveApzc = aNewActive;
+}
+
+void
+InputQueue::Clear()
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ mQueuedInputs.Clear();
+ mActiveTouchBlock = nullptr;
+ mActiveWheelBlock = nullptr;
+ mActiveDragBlock = nullptr;
+ mActivePanGestureBlock = nullptr;
+ mLastActiveApzc = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/InputQueue.h b/gfx/layers/apz/src/InputQueue.h
new file mode 100644
index 000000000..eaf9b20bc
--- /dev/null
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputQueue_h
+#define mozilla_layers_InputQueue_h
+
+#include "APZUtils.h"
+#include "DragTracker.h"
+#include "InputData.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+#include "TouchCounter.h"
+
+namespace mozilla {
+
+class InputData;
+class MultiTouchInput;
+class ScrollWheelInput;
+
+namespace layers {
+
+class AsyncPanZoomController;
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+class AsyncDragMetrics;
+class QueuedInput;
+
+/**
+ * This class stores incoming input events, associated with "input blocks", until
+ * they are ready for handling.
+ */
+class InputQueue {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
+
+public:
+ InputQueue();
+
+ /**
+ * Notifies the InputQueue of a new incoming input event. The APZC that the
+ * input event was targeted to should be provided in the |aTarget| parameter.
+ * See the documentation on APZCTreeManager::ReceiveInputEvent for info on
+ * return values from this function, including |aOutInputBlockId|.
+ */
+ nsEventStatus ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const InputData& aEvent,
+ uint64_t* aOutInputBlockId);
+ /**
+ * This function should be invoked to notify the InputQueue when web content
+ * decides whether or not it wants to cancel a block of events. The block
+ * id to which this applies should be provided in |aInputBlockId|.
+ */
+ void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault);
+ /**
+ * This function should be invoked to notify the InputQueue once the target
+ * APZC to handle an input block has been confirmed. In practice this should
+ * generally be decidable upon receipt of the input event, but in some cases
+ * we may need to query the layout engine to know for sure. The input block
+ * this applies to should be specified via the |aInputBlockId| parameter.
+ */
+ void SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc);
+ /**
+ * This function is invoked to confirm that the drag block should be handled
+ * by the APZ.
+ */
+ void ConfirmDragBlock(uint64_t aInputBlockId,
+ const RefPtr<AsyncPanZoomController>& aTargetApzc,
+ const AsyncDragMetrics& aDragMetrics);
+ /**
+ * This function should be invoked to notify the InputQueue of the touch-
+ * action properties for the different touch points in an input block. The
+ * input block this applies to should be specified by the |aInputBlockId|
+ * parameter. If touch-action is not enabled on the platform, this function
+ * does nothing and need not be called.
+ */
+ void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
+ /**
+ * Adds a new touch block at the end of the input queue that has the same
+ * allowed touch behaviour flags as the the touch block currently being
+ * processed. This should only be called when processing of a touch block
+ * triggers the creation of a new touch block. Returns the input block id
+ * of the the newly-created block.
+ */
+ uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
+ /**
+ * Returns the pending input block at the head of the queue, if there is one.
+ * This may return null if there all input events have been processed.
+ */
+ CancelableBlockState* GetCurrentBlock() const;
+ /*
+ * Returns the current pending input block as a specific kind of block. If
+ * GetCurrentBlock() returns null, these functions additionally check the
+ * mActiveXXXBlock field of the corresponding input type to see if there is
+ * a depleted but still active input block, and returns that if found. These
+ * functions may return null if no block is found.
+ */
+ TouchBlockState* GetCurrentTouchBlock() const;
+ WheelBlockState* GetCurrentWheelBlock() const;
+ DragBlockState* GetCurrentDragBlock() const;
+ PanGestureBlockState* GetCurrentPanGestureBlock() const;
+ /**
+ * Returns true iff the pending block at the head of the queue is a touch
+ * block and is ready for handling.
+ */
+ bool HasReadyTouchBlock() const;
+ /**
+ * If there is an active wheel transaction, returns the WheelBlockState
+ * representing the transaction. Otherwise, returns null. "Active" in this
+ * function name is the same kind of "active" as in mActiveWheelBlock - that
+ * is, new incoming wheel events will go into the "active" block.
+ */
+ WheelBlockState* GetActiveWheelTransaction() const;
+ /**
+ * Remove all input blocks from the input queue.
+ */
+ void Clear();
+ /**
+ * Whether the current pending block allows scroll handoff.
+ */
+ bool AllowScrollHandoff() const;
+ /**
+ * If there is currently a drag in progress, return whether or not it was
+ * targeted at a scrollbar. If the drag was newly-created and doesn't know,
+ * use the provided |aOnScrollbar| to populate that information.
+ */
+ bool IsDragOnScrollbar(bool aOnScrollbar);
+
+private:
+ ~InputQueue();
+
+ TouchBlockState* StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ bool aCopyPropertiesFromCurrent);
+
+ /**
+ * If animations are present for the current pending input block, cancel
+ * them as soon as possible.
+ */
+ void CancelAnimationsForNewBlock(CancelableBlockState* aBlock);
+
+ /**
+ * If we need to wait for a content response, schedule that now.
+ */
+ void MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock);
+
+ nsEventStatus ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MultiTouchInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const MouseInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const ScrollWheelInput& aEvent,
+ uint64_t* aOutInputBlockId);
+ nsEventStatus ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
+ bool aTargetConfirmed,
+ const PanGestureInput& aEvent,
+ uint64_t* aOutInputBlockId);
+
+ /**
+ * Helper function that searches mQueuedInputs for the first block matching
+ * the given id, and returns it. If |aOutFirstInput| is non-null, it is
+ * populated with a pointer to the first input in mQueuedInputs that
+ * corresponds to the block, or null if no such input was found. Note that
+ * even if there are no inputs in mQueuedInputs, this function can return
+ * non-null if the block id provided matches one of the depleted-but-still-
+ * active blocks (mActiveTouchBlock, mActiveWheelBlock, etc.).
+ */
+ CancelableBlockState* FindBlockForId(uint64_t aInputBlockId,
+ InputData** aOutFirstInput);
+ void ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget,
+ CancelableBlockState* aBlock);
+ void MainThreadTimeout(uint64_t aInputBlockId);
+ void ProcessQueue();
+ bool CanDiscardBlock(CancelableBlockState* aBlock);
+ void UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive);
+
+private:
+ // The queue of input events that have not yet been fully processed.
+ // This member must only be accessed on the controller/UI thread.
+ nsTArray<UniquePtr<QueuedInput>> mQueuedInputs;
+
+ // These are the most recently created blocks of each input type. They are
+ // "active" in the sense that new inputs of that type are associated with
+ // them. Note that these pointers may be null if no inputs of the type have
+ // arrived, or if the inputs for the type formed a complete block that was
+ // then discarded.
+ RefPtr<TouchBlockState> mActiveTouchBlock;
+ RefPtr<WheelBlockState> mActiveWheelBlock;
+ RefPtr<DragBlockState> mActiveDragBlock;
+ RefPtr<PanGestureBlockState> mActivePanGestureBlock;
+
+ // The APZC to which the last event was delivered
+ RefPtr<AsyncPanZoomController> mLastActiveApzc;
+
+ // Track touches so we know when to clear mLastActiveApzc
+ TouchCounter mTouchCounter;
+
+ // Track mouse inputs so we know if we're in a drag or not
+ DragTracker mDragTracker;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_InputQueue_h
diff --git a/gfx/layers/apz/src/Overscroll.h b/gfx/layers/apz/src/Overscroll.h
new file mode 100644
index 000000000..586f104cc
--- /dev/null
+++ b/gfx/layers/apz/src/Overscroll.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Overscroll_h
+#define mozilla_layers_Overscroll_h
+
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncPanZoomController.h"
+#include "FrameMetrics.h"
+#include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+// Animation used by GenericOverscrollEffect.
+class OverscrollAnimation: public AsyncPanZoomAnimation {
+public:
+ explicit OverscrollAnimation(AsyncPanZoomController& aApzc, const ParentLayerPoint& aVelocity)
+ : mApzc(aApzc)
+ {
+ mApzc.mX.StartOverscrollAnimation(aVelocity.x);
+ mApzc.mY.StartOverscrollAnimation(aVelocity.y);
+ }
+ ~OverscrollAnimation()
+ {
+ mApzc.mX.EndOverscrollAnimation();
+ mApzc.mY.EndOverscrollAnimation();
+ }
+
+ virtual bool DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) override
+ {
+ // Can't inline these variables due to short-circuit evaluation.
+ bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
+ bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
+ if (!continueX && !continueY) {
+ // If we got into overscroll from a fling, that fling did not request a
+ // fling snap to avoid a resulting scrollTo from cancelling the overscroll
+ // animation too early. We do still want to request a fling snap, though,
+ // in case the end of the axis at which we're overscrolled is not a valid
+ // snap point, so we request one now. If there are no snap points, this will
+ // do nothing. If there are snap points, we'll get a scrollTo that snaps us
+ // back to the nearest valid snap point.
+ // The scroll snapping is done in a deferred task, otherwise the state
+ // change to NOTHING caused by the overscroll animation ending would
+ // clobber a possible state change to SMOOTH_SCROLL in ScrollSnap().
+ mDeferredTasks.AppendElement(NewRunnableMethod(&mApzc, &AsyncPanZoomController::ScrollSnap));
+ return false;
+ }
+ return true;
+ }
+
+ virtual bool WantsRepaints() override
+ {
+ return false;
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+// Base class for different overscroll effects;
+class OverscrollEffectBase {
+public:
+ virtual ~OverscrollEffectBase() {}
+ virtual void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) = 0;
+ virtual void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) = 0;
+};
+
+// A generic overscroll effect, implemented by AsyncPanZoomController itself.
+class GenericOverscrollEffect : public OverscrollEffectBase {
+public:
+ explicit GenericOverscrollEffect(AsyncPanZoomController& aApzc) : mApzc(aApzc) {}
+
+ void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) override {
+ if (aShouldOverscrollX) {
+ mApzc.mX.OverscrollBy(aOverscroll.x);
+ aOverscroll.x = 0;
+ }
+
+ if (aShouldOverscrollY) {
+ mApzc.mY.OverscrollBy(aOverscroll.y);
+ aOverscroll.y = 0;
+ }
+
+ if (aShouldOverscrollX || aShouldOverscrollY) {
+ mApzc.ScheduleComposite();
+ }
+ }
+
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) override {
+ mApzc.StartOverscrollAnimation(aVelocity);
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+// A widget-specific overscroll effect, implemented by the widget via
+// GeckoContentController.
+class WidgetOverscrollEffect : public OverscrollEffectBase {
+public:
+ explicit WidgetOverscrollEffect(AsyncPanZoomController& aApzc) : mApzc(aApzc) {}
+
+ void ConsumeOverscroll(ParentLayerPoint& aOverscroll,
+ bool aShouldOverscrollX,
+ bool aShouldOverscrollY) override {
+ RefPtr<GeckoContentController> controller = mApzc.GetGeckoContentController();
+ if (controller && (aShouldOverscrollX || aShouldOverscrollY)) {
+ controller->UpdateOverscrollOffset(aOverscroll.x, aOverscroll.y, mApzc.IsRootContent());
+ aOverscroll = ParentLayerPoint();
+ }
+ }
+
+ void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) override {
+ RefPtr<GeckoContentController> controller = mApzc.GetGeckoContentController();
+ if (controller) {
+ controller->UpdateOverscrollVelocity(aVelocity.x, aVelocity.y, mApzc.IsRootContent());
+ }
+ }
+
+private:
+ AsyncPanZoomController& mApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_Overscroll_h
diff --git a/gfx/layers/apz/src/OverscrollHandoffState.cpp b/gfx/layers/apz/src/OverscrollHandoffState.cpp
new file mode 100644
index 000000000..577303fdd
--- /dev/null
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OverscrollHandoffState.h"
+
+#include <algorithm> // for std::stable_sort
+#include "mozilla/Assertions.h"
+#include "AsyncPanZoomController.h"
+
+namespace mozilla {
+namespace layers {
+
+OverscrollHandoffChain::~OverscrollHandoffChain() {}
+
+void
+OverscrollHandoffChain::Add(AsyncPanZoomController* aApzc)
+{
+ mChain.push_back(aApzc);
+}
+
+struct CompareByScrollPriority
+{
+ bool operator()(const RefPtr<AsyncPanZoomController>& a,
+ const RefPtr<AsyncPanZoomController>& b) const
+ {
+ return a->HasScrollgrab() && !b->HasScrollgrab();
+ }
+};
+
+void
+OverscrollHandoffChain::SortByScrollPriority()
+{
+ // The sorting being stable ensures that the relative order between
+ // non-scrollgrabbing APZCs remains child -> parent.
+ // (The relative order between scrollgrabbing APZCs will also remain
+ // child -> parent, though that's just an artefact of the implementation
+ // and users of 'scrollgrab' should not rely on this.)
+ std::stable_sort(mChain.begin(), mChain.end(), CompareByScrollPriority());
+}
+
+const RefPtr<AsyncPanZoomController>&
+OverscrollHandoffChain::GetApzcAtIndex(uint32_t aIndex) const
+{
+ MOZ_ASSERT(aIndex < Length());
+ return mChain[aIndex];
+}
+
+uint32_t
+OverscrollHandoffChain::IndexOf(const AsyncPanZoomController* aApzc) const
+{
+ uint32_t i;
+ for (i = 0; i < Length(); ++i) {
+ if (mChain[i] == aApzc) {
+ break;
+ }
+ }
+ return i;
+}
+
+void
+OverscrollHandoffChain::ForEachApzc(APZCMethod aMethod) const
+{
+ for (uint32_t i = 0; i < Length(); ++i) {
+ (mChain[i]->*aMethod)();
+ }
+}
+
+bool
+OverscrollHandoffChain::AnyApzc(APZCPredicate aPredicate) const
+{
+ MOZ_ASSERT(Length() > 0);
+ for (uint32_t i = 0; i < Length(); ++i) {
+ if ((mChain[i]->*aPredicate)()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+OverscrollHandoffChain::FlushRepaints() const
+{
+ ForEachApzc(&AsyncPanZoomController::FlushRepaintForOverscrollHandoff);
+}
+
+void
+OverscrollHandoffChain::CancelAnimations(CancelAnimationFlags aFlags) const
+{
+ MOZ_ASSERT(Length() > 0);
+ for (uint32_t i = 0; i < Length(); ++i) {
+ mChain[i]->CancelAnimation(aFlags);
+ }
+}
+
+void
+OverscrollHandoffChain::ClearOverscroll() const
+{
+ ForEachApzc(&AsyncPanZoomController::ClearOverscroll);
+}
+
+void
+OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const
+{
+ uint32_t i = IndexOf(aStart);
+ for (; i < Length(); ++i) {
+ AsyncPanZoomController* apzc = mChain[i];
+ if (!apzc->IsDestroyed()) {
+ apzc->SnapBackIfOverscrolled();
+ }
+ }
+}
+
+bool
+OverscrollHandoffChain::CanBePanned(const AsyncPanZoomController* aApzc) const
+{
+ // Find |aApzc| in the handoff chain.
+ uint32_t i = IndexOf(aApzc);
+
+ // See whether any APZC in the handoff chain starting from |aApzc|
+ // has room to be panned.
+ for (uint32_t j = i; j < Length(); ++j) {
+ if (mChain[j]->IsPannable()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+OverscrollHandoffChain::CanScrollInDirection(const AsyncPanZoomController* aApzc,
+ Layer::ScrollDirection aDirection) const
+{
+ // Find |aApzc| in the handoff chain.
+ uint32_t i = IndexOf(aApzc);
+
+ // See whether any APZC in the handoff chain starting from |aApzc|
+ // has room to scroll in the given direction.
+ for (uint32_t j = i; j < Length(); ++j) {
+ if (mChain[j]->CanScroll(aDirection)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+OverscrollHandoffChain::HasOverscrolledApzc() const
+{
+ return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
+}
+
+bool
+OverscrollHandoffChain::HasFastFlungApzc() const
+{
+ return AnyApzc(&AsyncPanZoomController::IsFlingingFast);
+}
+
+RefPtr<AsyncPanZoomController>
+OverscrollHandoffChain::FindFirstScrollable(const InputData& aInput) const
+{
+ for (size_t i = 0; i < Length(); i++) {
+ if (mChain[i]->CanScroll(aInput)) {
+ return mChain[i];
+ }
+ }
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h
new file mode 100644
index 000000000..173d6bddd
--- /dev/null
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_OverscrollHandoffChain_h
+#define mozilla_layers_OverscrollHandoffChain_h
+
+#include <vector>
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
+#include "APZUtils.h" // for CancelAnimationFlags
+#include "Layers.h" // for Layer::ScrollDirection
+#include "Units.h" // for ScreenPoint
+
+namespace mozilla {
+
+class InputData;
+
+namespace layers {
+
+class AsyncPanZoomController;
+
+/**
+ * This class represents the chain of APZCs along which overscroll is handed off.
+ * It is created by APZCTreeManager by starting from an initial APZC which is
+ * the target for input events, and following the scroll parent ID links (often
+ * but not always corresponding to parent pointers in the APZC tree), then
+ * adjusting for scrollgrab.
+ */
+class OverscrollHandoffChain
+{
+protected:
+ // Reference-counted classes cannot have public destructors.
+ ~OverscrollHandoffChain();
+public:
+ // Threadsafe so that the controller and compositor threads can both maintain
+ // nsRefPtrs to the same handoff chain.
+ // Mutable so that we can pass around the class by
+ // RefPtr<const OverscrollHandoffChain> and thus enforce that, once built,
+ // the chain is not modified.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OverscrollHandoffChain)
+
+ /*
+ * Methods for building the handoff chain.
+ * These should be used only by AsyncPanZoomController::BuildOverscrollHandoffChain().
+ */
+ void Add(AsyncPanZoomController* aApzc);
+ void SortByScrollPriority();
+
+ /*
+ * Methods for accessing the handoff chain.
+ */
+ uint32_t Length() const { return mChain.size(); }
+ const RefPtr<AsyncPanZoomController>& GetApzcAtIndex(uint32_t aIndex) const;
+ // Returns Length() if |aApzc| is not on this chain.
+ uint32_t IndexOf(const AsyncPanZoomController* aApzc) const;
+
+ /*
+ * Convenience methods for performing operations on APZCs in the chain.
+ */
+
+ // Flush repaints all the way up the chain.
+ void FlushRepaints() const;
+
+ // Cancel animations all the way up the chain.
+ void CancelAnimations(CancelAnimationFlags aFlags = Default) const;
+
+ // Clear overscroll all the way up the chain.
+ void ClearOverscroll() const;
+
+ // Snap back the APZC that is overscrolled on the subset of the chain from
+ // |aStart| onwards, if any.
+ void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
+
+ // Determine whether the given APZC, or any APZC further in the chain,
+ // has room to be panned.
+ bool CanBePanned(const AsyncPanZoomController* aApzc) const;
+
+ // Determine whether the given APZC, or any APZC further in the chain,
+ // can scroll in the given direction.
+ bool CanScrollInDirection(const AsyncPanZoomController* aApzc,
+ Layer::ScrollDirection aDirection) const;
+
+ // Determine whether any APZC along this handoff chain is overscrolled.
+ bool HasOverscrolledApzc() const;
+
+ // Determine whether any APZC along this handoff chain has been flung fast.
+ bool HasFastFlungApzc() const;
+
+ RefPtr<AsyncPanZoomController> FindFirstScrollable(const InputData& aInput) const;
+
+private:
+ std::vector<RefPtr<AsyncPanZoomController>> mChain;
+
+ typedef void (AsyncPanZoomController::*APZCMethod)();
+ typedef bool (AsyncPanZoomController::*APZCPredicate)() const;
+ void ForEachApzc(APZCMethod aMethod) const;
+ bool AnyApzc(APZCPredicate aPredicate) const;
+};
+
+/**
+ * This class groups the state maintained during overscroll handoff.
+ */
+struct OverscrollHandoffState {
+ OverscrollHandoffState(const OverscrollHandoffChain& aChain,
+ const ScreenPoint& aPanDistance,
+ ScrollSource aScrollSource)
+ : mChain(aChain),
+ mChainIndex(0),
+ mPanDistance(aPanDistance),
+ mScrollSource(aScrollSource)
+ {}
+
+ // The chain of APZCs along which we hand off scroll.
+ // This is const to indicate that the chain does not change over the
+ // course of handoff.
+ const OverscrollHandoffChain& mChain;
+
+ // The index of the APZC in the chain that we are currently giving scroll to.
+ // This is non-const to indicate that this changes over the course of handoff.
+ uint32_t mChainIndex;
+
+ // The total distance since touch-start of the pan that triggered the
+ // handoff. This is const to indicate that it does not change over the
+ // course of handoff.
+ // The x/y components of this are non-negative.
+ const ScreenPoint mPanDistance;
+
+ ScrollSource mScrollSource;
+};
+
+/*
+ * This class groups the state maintained during fling handoff.
+ */
+struct FlingHandoffState {
+ // The velocity of the fling being handed off.
+ ParentLayerPoint mVelocity;
+
+ // The chain of APZCs along which we hand off the fling.
+ // Unlike in OverscrollHandoffState, this is stored by RefPtr because
+ // otherwise it may not stay alive for the entire handoff.
+ RefPtr<const OverscrollHandoffChain> mChain;
+
+ // Whether handoff has happened by this point, or we're still process
+ // the original fling.
+ bool mIsHandoff;
+
+ // The single APZC that was scrolled by the pan that started this fling.
+ // The fling is only allowed to scroll this APZC, too.
+ // Used only if immediate scroll handoff is disallowed.
+ RefPtr<const AsyncPanZoomController> mScrolledApzc;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_OverscrollHandoffChain_h */
diff --git a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp
new file mode 100644
index 000000000..c83b9f45c
--- /dev/null
+++ b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PotentialCheckerboardDurationTracker.h"
+
+#include "mozilla/Telemetry.h" // for Telemetry
+
+namespace mozilla {
+namespace layers {
+
+PotentialCheckerboardDurationTracker::PotentialCheckerboardDurationTracker()
+ : mInCheckerboard(false)
+ , mInTransform(false)
+{
+}
+
+void
+PotentialCheckerboardDurationTracker::CheckerboardSeen()
+{
+ // This might get called while mInCheckerboard is already true
+ if (!Tracking()) {
+ mCurrentPeriodStart = TimeStamp::Now();
+ }
+ mInCheckerboard = true;
+}
+
+void
+PotentialCheckerboardDurationTracker::CheckerboardDone()
+{
+ MOZ_ASSERT(Tracking());
+ mInCheckerboard = false;
+ if (!Tracking()) {
+ mozilla::Telemetry::AccumulateTimeDelta(
+ mozilla::Telemetry::CHECKERBOARD_POTENTIAL_DURATION,
+ mCurrentPeriodStart);
+ }
+}
+
+void
+PotentialCheckerboardDurationTracker::InTransform(bool aInTransform)
+{
+ if (aInTransform == mInTransform) {
+ // no-op
+ return;
+ }
+
+ if (!Tracking()) {
+ // Because !Tracking(), mInTransform must be false, and so aInTransform
+ // must be true (or we would have early-exited this function already).
+ // Therefore, we are starting a potential checkerboard period.
+ mInTransform = aInTransform;
+ mCurrentPeriodStart = TimeStamp::Now();
+ return;
+ }
+
+ mInTransform = aInTransform;
+
+ if (!Tracking()) {
+ // Tracking() must have been true at the start of this function, or we
+ // would have taken the other !Tracking branch above. If it's false now,
+ // it means we just stopped tracking, so we are ending a potential
+ // checkerboard period.
+ mozilla::Telemetry::AccumulateTimeDelta(
+ mozilla::Telemetry::CHECKERBOARD_POTENTIAL_DURATION,
+ mCurrentPeriodStart);
+ }
+}
+
+bool
+PotentialCheckerboardDurationTracker::Tracking() const
+{
+ return mInTransform || mInCheckerboard;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h
new file mode 100644
index 000000000..6154003ad
--- /dev/null
+++ b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PotentialCheckerboardDurationTracker_h
+#define mozilla_layers_PotentialCheckerboardDurationTracker_h
+
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class allows the owner to track the duration of time considered
+ * "potentially checkerboarding". This is the union of two possibly-intersecting
+ * sets of time periods. The first set is that in which checkerboarding was
+ * actually happening, since by definition it could potentially be happening.
+ * The second set is that in which the APZC is actively transforming content
+ * in the compositor, since it could potentially transform it so as to display
+ * checkerboarding to the user.
+ * The caller of this class calls the appropriate methods to indicate the start
+ * and stop of these two sets, and this class manages accumulating the union
+ * of the various durations.
+ */
+class PotentialCheckerboardDurationTracker {
+public:
+ PotentialCheckerboardDurationTracker();
+
+ /**
+ * This should be called if checkerboarding is encountered. It can be called
+ * multiple times during a checkerboard event.
+ */
+ void CheckerboardSeen();
+ /**
+ * This should be called when checkerboarding is done. It must have been
+ * preceded by one or more calls to CheckerboardSeen().
+ */
+ void CheckerboardDone();
+
+ /**
+ * This should be called at composition time, to indicate if the APZC is in
+ * a transforming state or not.
+ */
+ void InTransform(bool aInTransform);
+
+private:
+ bool Tracking() const;
+
+private:
+ bool mInCheckerboard;
+ bool mInTransform;
+
+ TimeStamp mCurrentPeriodStart;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_PotentialCheckerboardDurationTracker_h
diff --git a/gfx/layers/apz/src/QueuedInput.cpp b/gfx/layers/apz/src/QueuedInput.cpp
new file mode 100644
index 000000000..21dd8e1b4
--- /dev/null
+++ b/gfx/layers/apz/src/QueuedInput.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "QueuedInput.h"
+
+#include "AsyncPanZoomController.h"
+#include "InputBlockState.h"
+#include "InputData.h"
+#include "OverscrollHandoffState.h"
+
+namespace mozilla {
+namespace layers {
+
+QueuedInput::QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock)
+ : mInput(MakeUnique<MultiTouchInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock)
+ : mInput(MakeUnique<ScrollWheelInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const MouseInput& aInput, DragBlockState& aBlock)
+ : mInput(MakeUnique<MouseInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock)
+ : mInput(MakeUnique<PanGestureInput>(aInput))
+ , mBlock(&aBlock)
+{
+}
+
+InputData*
+QueuedInput::Input()
+{
+ return mInput.get();
+}
+
+CancelableBlockState*
+QueuedInput::Block()
+{
+ return mBlock.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/QueuedInput.h b/gfx/layers/apz/src/QueuedInput.h
new file mode 100644
index 000000000..68dfbc3c5
--- /dev/null
+++ b/gfx/layers/apz/src/QueuedInput.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_QueuedInput_h
+#define mozilla_layers_QueuedInput_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+class InputData;
+class MultiTouchInput;
+class ScrollWheelInput;
+class MouseInput;
+class PanGestureInput;
+
+namespace layers {
+
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+class DragBlockState;
+class PanGestureBlockState;
+
+/**
+ * This lightweight class holds a pointer to an input event that has not yet
+ * been completely processed, along with the input block that the input event
+ * is associated with.
+ */
+class QueuedInput
+{
+public:
+ QueuedInput(const MultiTouchInput& aInput, TouchBlockState& aBlock);
+ QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock);
+ QueuedInput(const MouseInput& aInput, DragBlockState& aBlock);
+ QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock);
+
+ InputData* Input();
+ CancelableBlockState* Block();
+
+private:
+ // A copy of the input event that is provided to the constructor. This must
+ // be non-null, and is owned by this QueuedInput instance (hence the
+ // UniquePtr).
+ UniquePtr<InputData> mInput;
+ // A pointer to the block that the input event is associated with. This must
+ // be non-null.
+ RefPtr<CancelableBlockState> mBlock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_QueuedInput_h
diff --git a/gfx/layers/apz/src/TouchCounter.cpp b/gfx/layers/apz/src/TouchCounter.cpp
new file mode 100644
index 000000000..96dc35dc7
--- /dev/null
+++ b/gfx/layers/apz/src/TouchCounter.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TouchCounter.h"
+
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+TouchCounter::TouchCounter()
+ : mActiveTouchCount(0)
+{
+}
+
+void
+TouchCounter::Update(const MultiTouchInput& aInput)
+{
+ switch (aInput.mType) {
+ case MultiTouchInput::MULTITOUCH_START:
+ // touch-start event contains all active touches of the current session
+ mActiveTouchCount = aInput.mTouches.Length();
+ break;
+ case MultiTouchInput::MULTITOUCH_END:
+ if (mActiveTouchCount >= aInput.mTouches.Length()) {
+ // touch-end event contains only released touches
+ mActiveTouchCount -= aInput.mTouches.Length();
+ } else {
+ NS_WARNING("Got an unexpected touchend/touchcancel");
+ mActiveTouchCount = 0;
+ }
+ break;
+ case MultiTouchInput::MULTITOUCH_CANCEL:
+ mActiveTouchCount = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+uint32_t
+TouchCounter::GetActiveTouchCount() const
+{
+ return mActiveTouchCount;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/TouchCounter.h b/gfx/layers/apz/src/TouchCounter.h
new file mode 100644
index 000000000..f2c45486e
--- /dev/null
+++ b/gfx/layers/apz/src/TouchCounter.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_TouchCounter_h
+#define mozilla_layers_TouchCounter_h
+
+#include "mozilla/EventForwards.h"
+
+namespace mozilla {
+
+class MultiTouchInput;
+
+namespace layers {
+
+// TouchCounter simply tracks the number of active touch points. Feed it
+// your input events to update the internal state.
+class TouchCounter
+{
+public:
+ TouchCounter();
+ void Update(const MultiTouchInput& aInput);
+ uint32_t GetActiveTouchCount() const;
+
+private:
+ uint32_t mActiveTouchCount;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_TouchCounter_h */
diff --git a/gfx/layers/apz/src/WheelScrollAnimation.cpp b/gfx/layers/apz/src/WheelScrollAnimation.cpp
new file mode 100644
index 000000000..d7cb338e6
--- /dev/null
+++ b/gfx/layers/apz/src/WheelScrollAnimation.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WheelScrollAnimation.h"
+
+#include "AsyncPanZoomController.h"
+#include "gfxPrefs.h"
+#include "nsPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+WheelScrollAnimation::WheelScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint& aInitialPosition,
+ ScrollWheelInput::ScrollDeltaType aDeltaType)
+ : AsyncScrollBase(aInitialPosition)
+ , mApzc(aApzc)
+ , mFinalDestination(aInitialPosition)
+ , mDeltaType(aDeltaType)
+{
+}
+
+void
+WheelScrollAnimation::Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
+{
+ InitPreferences(aTime);
+
+ mFinalDestination += aDelta;
+
+ // Clamp the final destination to the scrollable area.
+ CSSPoint clamped = CSSPoint::FromAppUnits(mFinalDestination);
+ clamped.x = mApzc.mX.ClampOriginToScrollableRect(clamped.x);
+ clamped.y = mApzc.mY.ClampOriginToScrollableRect(clamped.y);
+ mFinalDestination = CSSPoint::ToAppUnits(clamped);
+
+ AsyncScrollBase::Update(aTime, mFinalDestination, aCurrentVelocity);
+}
+
+bool
+WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta)
+{
+ TimeStamp now = mApzc.GetFrameTime();
+ CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
+
+ // If the animation is finished, make sure the final position is correct by
+ // using one last displacement. Otherwise, compute the delta via the timing
+ // function as normal.
+ bool finished = IsFinished(now);
+ nsPoint sampledDest = finished
+ ? mDestination
+ : PositionAt(now);
+ ParentLayerPoint displacement =
+ (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom;
+
+ if (finished) {
+ mApzc.mX.SetVelocity(0);
+ mApzc.mY.SetVelocity(0);
+ } else if (!IsZero(displacement)) {
+ // Velocity is measured in ParentLayerCoords / Milliseconds
+ float xVelocity = displacement.x / aDelta.ToMilliseconds();
+ float yVelocity = displacement.y / aDelta.ToMilliseconds();
+ mApzc.mX.SetVelocity(xVelocity);
+ mApzc.mY.SetVelocity(yVelocity);
+ }
+
+ // Note: we ignore overscroll for wheel animations.
+ ParentLayerPoint adjustedOffset, overscroll;
+ mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
+ mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
+ !mApzc.mScrollMetadata.AllowVerticalScrollWithWheel());
+
+ // If we expected to scroll, but there's no more scroll range on either axis,
+ // then end the animation early. Note that the initial displacement could be 0
+ // if the compositor ran very quickly (<1ms) after the animation was created.
+ // When that happens we want to make sure the animation continues.
+ if (!IsZero(displacement) && IsZero(adjustedOffset)) {
+ // Nothing more to do - end the animation.
+ return false;
+ }
+
+ aFrameMetrics.ScrollBy(adjustedOffset / zoom);
+ return !finished;
+}
+
+void
+WheelScrollAnimation::InitPreferences(TimeStamp aTime)
+{
+ if (!mIsFirstIteration) {
+ return;
+ }
+
+ switch (mDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_PAGE:
+ mOriginMaxMS = clamped(gfxPrefs::PageSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::PageSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ case ScrollWheelInput::SCROLLDELTA_PIXEL:
+ mOriginMaxMS = clamped(gfxPrefs::PixelSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::PixelSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ case ScrollWheelInput::SCROLLDELTA_LINE:
+ default:
+ mOriginMaxMS = clamped(gfxPrefs::WheelSmoothScrollMaxDurationMs(), 0, 10000);
+ mOriginMinMS = clamped(gfxPrefs::WheelSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
+ break;
+ }
+
+ // The pref is 100-based int percentage, while mIntervalRatio is 1-based ratio
+ mIntervalRatio = ((double)gfxPrefs::SmoothScrollDurationToIntervalRatio()) / 100.0;
+ mIntervalRatio = std::max(1.0, mIntervalRatio);
+
+ InitializeHistory(aTime);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/src/WheelScrollAnimation.h b/gfx/layers/apz/src/WheelScrollAnimation.h
new file mode 100644
index 000000000..79466c445
--- /dev/null
+++ b/gfx/layers/apz/src/WheelScrollAnimation.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_WheelScrollAnimation_h_
+#define mozilla_layers_WheelScrollAnimation_h_
+
+#include "AsyncPanZoomAnimation.h"
+#include "AsyncScrollBase.h"
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+
+class WheelScrollAnimation
+ : public AsyncPanZoomAnimation,
+ public AsyncScrollBase
+{
+public:
+ WheelScrollAnimation(AsyncPanZoomController& aApzc,
+ const nsPoint& aInitialPosition,
+ ScrollWheelInput::ScrollDeltaType aDeltaType);
+
+ bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
+ void Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
+
+ WheelScrollAnimation* AsWheelScrollAnimation() override {
+ return this;
+ }
+
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(mFinalDestination);
+ }
+
+private:
+ void InitPreferences(TimeStamp aTime);
+
+private:
+ AsyncPanZoomController& mApzc;
+ nsPoint mFinalDestination;
+ ScrollWheelInput::ScrollDeltaType mDeltaType;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_WheelScrollAnimation_h_
diff --git a/gfx/layers/apz/test/gtest/APZCBasicTester.h b/gfx/layers/apz/test/gtest/APZCBasicTester.h
new file mode 100644
index 000000000..79a69301f
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCBasicTester_h
+#define mozilla_layers_APZCBasicTester_h
+
+/**
+ * Defines a test fixture used for testing a single APZC.
+ */
+
+#include "APZTestCommon.h"
+
+class APZCBasicTester : public APZCTesterBase {
+public:
+ explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
+ : mGestureBehavior(aGestureBehavior)
+ {
+ }
+
+protected:
+ virtual void SetUp()
+ {
+ gfxPrefs::GetSingleton();
+ APZThreadUtils::SetThreadAssertionsEnabled(false);
+ APZThreadUtils::SetControllerThread(MessageLoop::current());
+
+ tm = new TestAPZCTreeManager(mcc);
+ apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
+ apzc->SetFrameMetrics(TestFrameMetrics());
+ apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
+ }
+
+ /**
+ * Get the APZC's scroll range in CSS pixels.
+ */
+ CSSRect GetScrollRange() const
+ {
+ const FrameMetrics& metrics = apzc->GetFrameMetrics();
+ return CSSRect(
+ metrics.GetScrollableRect().TopLeft(),
+ metrics.GetScrollableRect().Size() - metrics.CalculateCompositedSizeInCssPixels());
+ }
+
+ virtual void TearDown()
+ {
+ while (mcc->RunThroughDelayedTasks());
+ apzc->Destroy();
+ tm->ClearTree();
+ tm->ClearContentController();
+ }
+
+ void MakeApzcWaitForMainThread()
+ {
+ apzc->SetWaitForMainThread();
+ }
+
+ void MakeApzcZoomable()
+ {
+ apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f)));
+ }
+
+ void MakeApzcUnzoomable()
+ {
+ apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToParentLayerScale(1.0f), CSSToParentLayerScale(1.0f)));
+ }
+
+ void PanIntoOverscroll();
+
+ /**
+ * Sample animations once, 1 ms later than the last sample.
+ */
+ void SampleAnimationOnce()
+ {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ mcc->AdvanceBy(increment);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ }
+
+ /**
+ * Sample animations until we recover from overscroll.
+ * @param aExpectedScrollOffset the expected reported scroll offset
+ * throughout the animation
+ */
+ void SampleAnimationUntilRecoveredFromOverscroll(const ParentLayerPoint& aExpectedScrollOffset)
+ {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ bool recoveredFromOverscroll = false;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
+ // The reported scroll offset should be the same throughout.
+ EXPECT_EQ(aExpectedScrollOffset, pointOut);
+
+ // Trigger computation of the overscroll tranform, to make sure
+ // no assetions fire during the calculation.
+ apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL);
+
+ if (!apzc->IsOverscrolled()) {
+ recoveredFromOverscroll = true;
+ }
+
+ mcc->AdvanceBy(increment);
+ }
+ EXPECT_TRUE(recoveredFromOverscroll);
+ apzc->AssertStateIsReset();
+ }
+
+ void TestOverscroll();
+
+ AsyncPanZoomController::GestureBehavior mGestureBehavior;
+ RefPtr<TestAPZCTreeManager> tm;
+ RefPtr<TestAsyncPanZoomController> apzc;
+};
+
+#endif // mozilla_layers_APZCBasicTester_h
diff --git a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
new file mode 100644
index 000000000..4eeed1e7e
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerTester_h
+#define mozilla_layers_APZCTreeManagerTester_h
+
+/**
+ * Defines a test fixture used for testing multiple APZCs interacting in
+ * an APZCTreeManager.
+ */
+
+#include "APZTestCommon.h"
+#include "gfxPlatform.h"
+
+class APZCTreeManagerTester : public APZCTesterBase {
+protected:
+ virtual void SetUp() {
+ gfxPrefs::GetSingleton();
+ gfxPlatform::GetPlatform();
+ APZThreadUtils::SetThreadAssertionsEnabled(false);
+ APZThreadUtils::SetControllerThread(MessageLoop::current());
+
+ manager = new TestAPZCTreeManager(mcc);
+ }
+
+ virtual void TearDown() {
+ while (mcc->RunThroughDelayedTasks());
+ manager->ClearTree();
+ manager->ClearContentController();
+ }
+
+ /**
+ * Sample animations once for all APZCs, 1 ms later than the last sample.
+ */
+ void SampleAnimationsOnce() {
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ mcc->AdvanceBy(increment);
+
+ for (const RefPtr<Layer>& layer : layers) {
+ if (TestAsyncPanZoomController* apzc = ApzcOf(layer)) {
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ }
+ }
+ }
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root;
+
+ RefPtr<TestAPZCTreeManager> manager;
+
+protected:
+ static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,
+ const CSSRect& aScrollableRect,
+ const ParentLayerRect& aCompositionBounds)
+ {
+ ScrollMetadata metadata;
+ FrameMetrics& metrics = metadata.GetMetrics();
+ metrics.SetScrollId(aScrollId);
+ // By convention in this test file, START_SCROLL_ID is the root, so mark it as such.
+ if (aScrollId == FrameMetrics::START_SCROLL_ID) {
+ metadata.SetIsLayersIdRoot(true);
+ }
+ metrics.SetCompositionBounds(aCompositionBounds);
+ metrics.SetScrollableRect(aScrollableRect);
+ metrics.SetScrollOffset(CSSPoint(0, 0));
+ metadata.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
+ metadata.SetLineScrollAmount(LayoutDeviceIntSize(5, 10));
+ metadata.SetAllowVerticalScrollWithWheel(true);
+ return metadata;
+ }
+
+ static void SetEventRegionsBasedOnBottommostMetrics(Layer* aLayer)
+ {
+ const FrameMetrics& metrics = aLayer->GetScrollMetadata(0).GetMetrics();
+ CSSRect scrollableRect = metrics.GetScrollableRect();
+ if (!scrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
+ // The purpose of this is to roughly mimic what layout would do in the
+ // case of a scrollable frame with the event regions and clip. This lets
+ // us exercise the hit-testing code in APZCTreeManager
+ EventRegions er = aLayer->GetEventRegions();
+ IntRect scrollRect = RoundedToInt(
+ scrollableRect * metrics.LayersPixelsPerCSSPixel()).ToUnknownRect();
+ er.mHitRegion = nsIntRegion(IntRect(
+ RoundedToInt(metrics.GetCompositionBounds().TopLeft().ToUnknownPoint()),
+ scrollRect.Size()));
+ aLayer->SetEventRegions(er);
+ }
+ }
+
+ static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
+ CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
+ ParentLayerIntRect compositionBounds = ViewAs<ParentLayerPixel>(
+ aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+ ScrollMetadata metadata = BuildScrollMetadata(aScrollId, aScrollableRect,
+ ParentLayerRect(compositionBounds));
+ aLayer->SetScrollMetadata(metadata);
+ aLayer->SetClipRect(Some(compositionBounds));
+ SetEventRegionsBasedOnBottommostMetrics(aLayer);
+ }
+
+ void SetScrollHandoff(Layer* aChild, Layer* aParent) {
+ ScrollMetadata metadata = aChild->GetScrollMetadata(0);
+ metadata.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId());
+ aChild->SetScrollMetadata(metadata);
+ }
+
+ static TestAsyncPanZoomController* ApzcOf(Layer* aLayer) {
+ EXPECT_EQ(1u, aLayer->GetScrollMetadataCount());
+ return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(0);
+ }
+
+ static TestAsyncPanZoomController* ApzcOf(Layer* aLayer, uint32_t aIndex) {
+ EXPECT_LT(aIndex, aLayer->GetScrollMetadataCount());
+ return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(aIndex);
+ }
+
+ void CreateSimpleScrollingLayer() {
+ const char* layerTreeSyntax = "t";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
+ }
+
+ void CreateSimpleDTCScrollingLayer() {
+ const char* layerTreeSyntax = "t";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
+
+ EventRegions regions;
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 200));
+ regions.mDispatchToContentHitRegion = regions.mHitRegion;
+ layers[0]->SetEventRegions(regions);
+ }
+
+ void CreateSimpleMultiLayerTree() {
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,50)),
+ nsIntRegion(IntRect(0,50,100,50)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ }
+
+ void CreatePotentiallyLeakingTree() {
+ const char* layerTreeSyntax = "c(c(c(t))c(c(t)))";
+ // LayerID 0 1 2 3 4 5 6
+ root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
+ }
+
+ void CreateBug1194876Tree() {
+ const char* layerTreeSyntax = "c(t)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollHandoff(layers[1], layers[0]);
+
+ // Make layers[1] the root content
+ ScrollMetadata childMetadata = layers[1]->GetScrollMetadata(0);
+ childMetadata.GetMetrics().SetIsRootContent(true);
+ layers[1]->SetScrollMetadata(childMetadata);
+
+ // Both layers are fully dispatch-to-content
+ EventRegions regions;
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ regions.mDispatchToContentHitRegion = regions.mHitRegion;
+ layers[0]->SetEventRegions(regions);
+ layers[1]->SetEventRegions(regions);
+ }
+};
+
+#endif // mozilla_layers_APZCTreeManagerTester_h
diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h
new file mode 100644
index 000000000..6e259ab60
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZTestCommon_h
+#define mozilla_layers_APZTestCommon_h
+
+/**
+ * Defines a set of mock classes and utility functions/classes for
+ * writing APZ gtests.
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "apz/src/AsyncPanZoomController.h"
+#include "apz/src/HitTestingTreeNode.h"
+#include "base/task.h"
+#include "Layers.h"
+#include "TestLayers.h"
+#include "UnitTransforms.h"
+#include "gfxPrefs.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::MockFunction;
+using ::testing::InSequence;
+typedef mozilla::layers::GeckoContentController::TapType TapType;
+
+template<class T>
+class ScopedGfxPref {
+public:
+ ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
+ : mSetPrefFunc(aSetPrefFunc)
+ {
+ mOldVal = aGetPrefFunc();
+ aSetPrefFunc(aVal);
+ }
+
+ ~ScopedGfxPref() {
+ mSetPrefFunc(mOldVal);
+ }
+
+private:
+ void (*mSetPrefFunc)(T);
+ T mOldVal;
+};
+
+#define SCOPED_GFX_PREF(prefBase, prefType, prefValue) \
+ ScopedGfxPref<prefType> pref_##prefBase( \
+ &(gfxPrefs::prefBase), \
+ &(gfxPrefs::Set##prefBase), \
+ prefValue)
+
+static TimeStamp GetStartupTime() {
+ static TimeStamp sStartupTime = TimeStamp::Now();
+ return sStartupTime;
+}
+
+class MockContentController : public GeckoContentController {
+public:
+ MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
+ MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
+ MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
+ MOCK_METHOD5(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
+ MOCK_METHOD4(NotifyPinchGesture, void(PinchGestureInput::PinchGestureType, const ScrollableLayerGuid&, LayoutDeviceCoord, Modifiers));
+ // Can't use the macros with already_AddRefed :(
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ RefPtr<Runnable> task = aTask;
+ }
+ bool IsRepaintThread() {
+ return NS_IsMainThread();
+ }
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
+ NS_DispatchToMainThread(Move(aTask));
+ }
+ MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
+ MOCK_METHOD0(NotifyFlushComplete, void());
+};
+
+class MockContentControllerDelayed : public MockContentController {
+public:
+ MockContentControllerDelayed()
+ : mTime(GetStartupTime())
+ {
+ }
+
+ const TimeStamp& Time() {
+ return mTime;
+ }
+
+ void AdvanceByMillis(int aMillis) {
+ AdvanceBy(TimeDuration::FromMilliseconds(aMillis));
+ }
+
+ void AdvanceBy(const TimeDuration& aIncrement) {
+ TimeStamp target = mTime + aIncrement;
+ while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) {
+ RunNextDelayedTask();
+ }
+ mTime = target;
+ }
+
+ void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
+ RefPtr<Runnable> task = aTask;
+ TimeStamp runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs);
+ int insIndex = mTaskQueue.Length();
+ while (insIndex > 0) {
+ if (mTaskQueue[insIndex - 1].second <= runAtTime) {
+ break;
+ }
+ insIndex--;
+ }
+ mTaskQueue.InsertElementAt(insIndex, std::make_pair(task, runAtTime));
+ }
+
+ // Run all the tasks in the queue, returning the number of tasks
+ // run. Note that if a task queues another task while running, that
+ // new task will not be run. Therefore, there may be still be tasks
+ // in the queue after this function is called. Only when the return
+ // value is 0 is the queue guaranteed to be empty.
+ int RunThroughDelayedTasks() {
+ nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> runQueue;
+ runQueue.SwapElements(mTaskQueue);
+ int numTasks = runQueue.Length();
+ for (int i = 0; i < numTasks; i++) {
+ mTime = runQueue[i].second;
+ runQueue[i].first->Run();
+
+ // Deleting the task is important in order to release the reference to
+ // the callee object.
+ runQueue[i].first = nullptr;
+ }
+ return numTasks;
+ }
+
+private:
+ void RunNextDelayedTask() {
+ std::pair<RefPtr<Runnable>, TimeStamp> next = mTaskQueue[0];
+ mTaskQueue.RemoveElementAt(0);
+ mTime = next.second;
+ next.first->Run();
+ // Deleting the task is important in order to release the reference to
+ // the callee object.
+ next.first = nullptr;
+ }
+
+ // The following array is sorted by timestamp (tasks are inserted in order by
+ // timestamp).
+ nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> mTaskQueue;
+ TimeStamp mTime;
+};
+
+class TestAPZCTreeManager : public APZCTreeManager {
+public:
+ explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc) : mcc(aMcc) {}
+
+ RefPtr<InputQueue> GetInputQueue() const {
+ return mInputQueue;
+ }
+
+ void ClearContentController() {
+ mcc = nullptr;
+ }
+
+protected:
+ AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController) override;
+
+ TimeStamp GetFrameTime() override {
+ return mcc->Time();
+ }
+
+private:
+ RefPtr<MockContentControllerDelayed> mcc;
+};
+
+class TestAsyncPanZoomController : public AsyncPanZoomController {
+public:
+ TestAsyncPanZoomController(uint64_t aLayersId, MockContentControllerDelayed* aMcc,
+ TestAPZCTreeManager* aTreeManager,
+ GestureBehavior aBehavior = DEFAULT_GESTURES)
+ : AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(),
+ aMcc, aBehavior)
+ , mWaitForMainThread(false)
+ , mcc(aMcc)
+ {}
+
+ nsEventStatus ReceiveInputEvent(const InputData& aEvent, ScrollableLayerGuid* aDummy, uint64_t* aOutInputBlockId) {
+ // This is a function whose signature matches exactly the ReceiveInputEvent
+ // on APZCTreeManager. This allows us to templates for functions like
+ // TouchDown, TouchUp, etc so that we can reuse the code for dispatching
+ // events into both APZC and APZCTM.
+ return ReceiveInputEvent(aEvent, aOutInputBlockId);
+ }
+
+ nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
+ return GetInputQueue()->ReceiveInputEvent(this, !mWaitForMainThread, aEvent, aOutInputBlockId);
+ }
+
+ void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
+ GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+ }
+
+ void ConfirmTarget(uint64_t aInputBlockId) {
+ RefPtr<AsyncPanZoomController> target = this;
+ GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target);
+ }
+
+ void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+ GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
+ }
+
+ void SetFrameMetrics(const FrameMetrics& metrics) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mFrameMetrics = metrics;
+ }
+
+ FrameMetrics& GetFrameMetrics() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics;
+ }
+
+ ScrollMetadata& GetScrollMetadata() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mScrollMetadata;
+ }
+
+ const FrameMetrics& GetFrameMetrics() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ return mFrameMetrics;
+ }
+
+ using AsyncPanZoomController::GetVelocityVector;
+
+ void AssertStateIsReset() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ EXPECT_EQ(NOTHING, mState);
+ }
+
+ void AssertStateIsFling() const {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ EXPECT_EQ(FLING, mState);
+ }
+
+ void AdvanceAnimationsUntilEnd(const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
+ while (AdvanceAnimations(mcc->Time())) {
+ mcc->AdvanceBy(aIncrement);
+ }
+ }
+
+ bool SampleContentTransformForFrame(AsyncTransform* aOutTransform,
+ ParentLayerPoint& aScrollOffset,
+ const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
+ mcc->AdvanceBy(aIncrement);
+ bool ret = AdvanceAnimations(mcc->Time());
+ if (aOutTransform) {
+ *aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL);
+ }
+ aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
+ return ret;
+ }
+
+ void SetWaitForMainThread() {
+ mWaitForMainThread = true;
+ }
+
+private:
+ bool mWaitForMainThread;
+ MockContentControllerDelayed* mcc;
+};
+
+class APZCTesterBase : public ::testing::Test {
+public:
+ APZCTesterBase() {
+ mcc = new NiceMock<MockContentControllerDelayed>();
+ }
+
+ template<class InputReceiver>
+ void Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeDuration aTapLength,
+ nsEventStatus (*aOutEventStatuses)[2] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ template<class InputReceiver>
+ void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint, TimeDuration aTapLength);
+
+ template<class InputReceiver>
+ void Pan(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aTouchStart,
+ const ScreenIntPoint& aTouchEnd,
+ bool aKeepFingerDown = false,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ /*
+ * A version of Pan() that only takes y coordinates rather than (x, y) points
+ * for the touch start and end points, and uses 10 for the x coordinates.
+ * This is for convenience, as most tests only need to pan in one direction.
+ */
+ template<class InputReceiver>
+ void Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
+ int aTouchEndY, bool aKeepFingerDown = false,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ /*
+ * Dispatches mock touch events to the apzc and checks whether apzc properly
+ * consumed them and triggered scrolling behavior.
+ */
+ template<class InputReceiver>
+ void PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
+ int aTouchEndY,
+ bool aExpectConsumed,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ void ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
+ int aTouchStartY,
+ int aTouchEndY,
+ uint64_t* aOutInputBlockId = nullptr);
+
+ template<class InputReceiver>
+ void DoubleTap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t (*aOutInputBlockIds)[2] = nullptr);
+
+ template<class InputReceiver>
+ void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ uint64_t (*aOutInputBlockIds)[2] = nullptr);
+
+protected:
+ RefPtr<MockContentControllerDelayed> mcc;
+};
+
+template<class InputReceiver>
+void
+APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint, TimeDuration aTapLength,
+ nsEventStatus (*aOutEventStatuses)[2],
+ uint64_t* aOutInputBlockId)
+{
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+ mcc->AdvanceBy(aTapLength);
+
+ // If touch-action is enabled then simulate the allowed touch behaviour
+ // notification that the main thread is supposed to deliver.
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ TimeDuration aTapLength)
+{
+ nsEventStatus statuses[2];
+ Tap(aTarget, aPoint, aTapLength, &statuses);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aTouchStart,
+ const ScreenIntPoint& aTouchEnd,
+ bool aKeepFingerDown,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t* aOutInputBlockId)
+{
+ // Reduce the touch start and move tolerance to a tiny value.
+ // We can't use a scoped pref because this value might be read at some later
+ // time when the events are actually processed, rather than when we deliver
+ // them.
+ gfxPrefs::SetAPZTouchStartTolerance(1.0f / 1000.0f);
+ gfxPrefs::SetAPZTouchMoveTolerance(0.0f);
+ const int OVERCOME_TOUCH_TOLERANCE = 1;
+
+ const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50);
+
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ // Make sure the move is large enough to not be handled as a tap
+ nsEventStatus status = TouchDown(aTarget,
+ ScreenIntPoint(aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE),
+ mcc->Time(), aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ // Allowed touch behaviours must be set after sending touch-start.
+ if (status != nsEventStatus_eConsumeNoDefault) {
+ if (aAllowedTouchBehaviors) {
+ EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
+ aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
+ } else if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
+ }
+ }
+
+ status = TouchMove(aTarget, aTouchStart, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ status = TouchMove(aTarget, aTouchEnd, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+
+ mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
+
+ if (!aKeepFingerDown) {
+ status = TouchUp(aTarget, aTouchEnd, mcc->Time());
+ } else {
+ status = nsEventStatus_eIgnore;
+ }
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+
+ // Don't increment the time here. Animations started on touch-up, such as
+ // flings, are affected by elapsed time, and we want to be able to sample
+ // them immediately after they start, without time having elapsed.
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
+ int aTouchStartY, int aTouchEndY, bool aKeepFingerDown,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t* aOutInputBlockId)
+{
+ Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY),
+ aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ int aTouchStartY,
+ int aTouchEndY,
+ bool aExpectConsumed,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors,
+ uint64_t* aOutInputBlockId)
+{
+ nsEventStatus statuses[4]; // down, move, move, up
+ Pan(aTarget, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
+
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+
+ nsEventStatus touchMoveStatus;
+ if (aExpectConsumed) {
+ touchMoveStatus = nsEventStatus_eConsumeDoDefault;
+ } else {
+ touchMoveStatus = nsEventStatus_eIgnore;
+ }
+ EXPECT_EQ(touchMoveStatus, statuses[1]);
+ EXPECT_EQ(touchMoveStatus, statuses[2]);
+}
+
+void
+APZCTesterBase::ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
+ int aTouchStartY, int aTouchEndY,
+ uint64_t* aOutInputBlockId)
+{
+ Pan(aApzc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
+ aApzc->CancelAnimation();
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::DoubleTap(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ nsEventStatus (*aOutEventStatuses)[4],
+ uint64_t (*aOutInputBlockIds)[2])
+{
+ uint64_t blockId;
+ nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+ if (aOutInputBlockIds) {
+ (*aOutInputBlockIds)[0] = blockId;
+ }
+ mcc->AdvanceByMillis(10);
+
+ // If touch-action is enabled then simulate the allowed touch behaviour
+ // notification that the main thread is supposed to deliver.
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, blockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+ mcc->AdvanceByMillis(10);
+ status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+ if (aOutInputBlockIds) {
+ (*aOutInputBlockIds)[1] = blockId;
+ }
+ mcc->AdvanceByMillis(10);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(aTarget, blockId);
+ }
+
+ status = TouchUp(aTarget, aPoint, mcc->Time());
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+}
+
+template<class InputReceiver>
+void
+APZCTesterBase::DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aPoint,
+ uint64_t (*aOutInputBlockIds)[2])
+{
+ nsEventStatus statuses[4];
+ DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
+}
+
+AsyncPanZoomController*
+TestAPZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
+ return new TestAsyncPanZoomController(aLayersId, mcc, this,
+ AsyncPanZoomController::USE_GESTURE_DETECTOR);
+}
+
+FrameMetrics
+TestFrameMetrics()
+{
+ FrameMetrics fm;
+
+ fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
+ fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
+ fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
+ fm.SetScrollableRect(CSSRect(0, 0, 100, 100));
+
+ return fm;
+}
+
+uint32_t
+MillisecondsSinceStartup(TimeStamp aTime)
+{
+ return (aTime - GetStartupTime()).ToMilliseconds();
+}
+
+#endif // mozilla_layers_APZTestCommon_h
diff --git a/gfx/layers/apz/test/gtest/InputUtils.h b/gfx/layers/apz/test/gtest/InputUtils.h
new file mode 100644
index 000000000..a1bd2851e
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/InputUtils.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputUtils_h
+#define mozilla_layers_InputUtils_h
+
+/**
+ * Defines a set of utility functions for generating input events
+ * to an APZC/APZCTM during APZ gtests.
+ */
+
+#include "APZTestCommon.h"
+
+/* The InputReceiver template parameter used in the helper functions below needs
+ * to be a class that implements functions with the signatures:
+ * nsEventStatus ReceiveInputEvent(const InputData& aEvent,
+ * ScrollableLayerGuid* aGuid,
+ * uint64_t* aOutInputBlockId);
+ * void SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ * const nsTArray<uint32_t>& aBehaviours);
+ * The classes that currently implement these are APZCTreeManager and
+ * TestAsyncPanZoomController. Using this template allows us to test individual
+ * APZC instances in isolation and also an entire APZ tree, while using the same
+ * code to dispatch input events.
+ */
+
+// Some helper functions for constructing input event objects suitable to be
+// passed either to an APZC (which expects an transformed point), or to an APZTM
+// (which expects an untransformed point). We handle both cases by setting both
+// the transformed and untransformed fields to the same value.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
+{
+ SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
+ touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
+ return touch;
+}
+
+// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
+{
+ return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
+}
+
+PinchGestureInput
+CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
+ const ScreenIntPoint& aFocus,
+ float aCurrentSpan, float aPreviousSpan)
+{
+ ParentLayerPoint localFocus(aFocus.x, aFocus.y);
+ PinchGestureInput result(aType, 0, TimeStamp(), localFocus,
+ aCurrentSpan, aPreviousSpan, 0);
+ result.mFocusPoint = aFocus;
+ return result;
+}
+
+template<class InputReceiver>
+void
+SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
+ uint64_t aInputBlockId,
+ int touchPoints = 1)
+{
+ nsTArray<uint32_t> defaultBehaviors;
+ // use the default value where everything is allowed
+ for (int i = 0; i < touchPoints; i++) {
+ defaultBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM
+ | mozilla::layers::AllowedTouchBehavior::DOUBLE_TAP_ZOOM);
+ }
+ aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
+}
+
+
+MultiTouchInput
+CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType, TimeStamp aTime)
+{
+ return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
+}
+
+template<class InputReceiver>
+nsEventStatus
+TouchUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime)
+{
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime);
+ mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
+ return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
+}
+
+template<class InputReceiver>
+void
+PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus,
+ const ScreenIntPoint& aSecondFocus, float aScale,
+ nsEventStatus (*aOutEventStatuses)[3] = nullptr)
+{
+ nsEventStatus actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
+ aFocus, 10.0, 10.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = actualStatus;
+ }
+ actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
+ aSecondFocus, 10.0 * aScale, 10.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = actualStatus;
+ }
+ actualStatus = aTarget->ReceiveInputEvent(
+ CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
+ // note: negative values here tell APZC
+ // not to turn the pinch into a pan
+ aFocus, -1.0, -1.0),
+ nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = actualStatus;
+ }
+}
+
+template<class InputReceiver>
+void
+PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ bool aShouldTriggerPinch)
+{
+ nsEventStatus statuses[3]; // scalebegin, scale, scaleend
+ PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses);
+
+ nsEventStatus expectedStatus = aShouldTriggerPinch
+ ? nsEventStatus_eConsumeNoDefault
+ : nsEventStatus_eIgnore;
+ EXPECT_EQ(expectedStatus, statuses[0]);
+ EXPECT_EQ(expectedStatus, statuses[1]);
+}
+
+template<class InputReceiver>
+void
+PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ int& inputId,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
+ nsEventStatus (*aOutEventStatuses)[4] = nullptr,
+ uint64_t* aOutInputBlockId = nullptr)
+{
+ // Having pinch coordinates in float type may cause problems with high-precision scale values
+ // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
+ float pinchLength = 100.0;
+ float pinchLengthScaled = pinchLength * aScale;
+
+ // Even if the caller doesn't care about the block id, we need it to set the
+ // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
+ uint64_t blockId;
+ if (!aOutInputBlockId) {
+ aOutInputBlockId = &blockId;
+ }
+
+ MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
+ nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[0] = status;
+ }
+
+ if (aAllowedTouchBehaviors) {
+ EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
+ aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
+ } else if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
+ }
+
+ MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[1] = status;
+ }
+
+ MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[2] = status;
+ }
+
+ MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
+ status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
+ if (aOutEventStatuses) {
+ (*aOutEventStatuses)[3] = status;
+ }
+
+ inputId += 2;
+}
+
+template<class InputReceiver>
+void
+PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+ const ScreenIntPoint& aFocus, float aScale,
+ int& inputId, bool aShouldTriggerPinch,
+ nsTArray<uint32_t>* aAllowedTouchBehaviors)
+{
+ nsEventStatus statuses[4]; // down, move, move, up
+ PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
+
+ nsEventStatus expectedMoveStatus = aShouldTriggerPinch
+ ? nsEventStatus_eConsumeDoDefault
+ : nsEventStatus_eIgnore;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+ EXPECT_EQ(expectedMoveStatus, statuses[1]);
+ EXPECT_EQ(expectedMoveStatus, statuses[2]);
+}
+
+template<class InputReceiver>
+nsEventStatus
+Wheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ aPoint, aDelta.x, aDelta.y, false);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+SmoothWheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
+ ScrollWheelInput::SCROLLMODE_SMOOTH, ScrollWheelInput::SCROLLDELTA_LINE,
+ aPoint, aDelta.x, aDelta.y, false);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_DOWN, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_MOVE, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+template<class InputReceiver>
+nsEventStatus
+MouseUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+ TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+{
+ MouseInput input(MouseInput::MOUSE_UP, MouseInput::ButtonType::LEFT_BUTTON,
+ 0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
+ return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
+}
+
+
+#endif // mozilla_layers_InputUtils_h
diff --git a/gfx/layers/apz/test/gtest/TestBasic.cpp b/gfx/layers/apz/test/gtest/TestBasic.cpp
new file mode 100644
index 000000000..921ea4080
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestBasic.cpp
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+TEST_F(APZCBasicTester, Overzoom) {
+ // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
+ fm.SetScrollableRect(CSSRect(0, 0, 125, 150));
+ fm.SetScrollOffset(CSSPoint(10, 0));
+ fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0));
+ fm.SetIsRootContent(true);
+ apzc->SetFrameMetrics(fm);
+
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true);
+
+ fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
+ // bug 936721 - PGO builds introduce rounding error so
+ // use a fuzzy match instead
+ EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
+ EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
+}
+
+TEST_F(APZCBasicTester, SimpleTransform) {
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+}
+
+
+TEST_F(APZCBasicTester, ComplexTransform) {
+ // This test assumes there is a page that gets rendered to
+ // two layers. In CSS pixels, the first layer is 50x50 and
+ // the second layer is 25x50. The widget scale factor is 3.0
+ // and the presShell resolution is 2.0. Therefore, these layers
+ // end up being 300x300 and 150x300 in layer pixels.
+ //
+ // The second (child) layer has an additional CSS transform that
+ // stretches it by 2.0 on the x-axis. Therefore, after applying
+ // CSS transforms, the two layers are the same size in screen
+ // pixels.
+ //
+ // The screen itself is 24x24 in screen pixels (therefore 4x4 in
+ // CSS pixels). The displayport is 1 extra CSS pixel on all
+ // sides.
+
+ RefPtr<TestAsyncPanZoomController> childApzc =
+ new TestAsyncPanZoomController(0, mcc, tm);
+
+ const char* layerTreeSyntax = "c(c)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 300, 300)),
+ nsIntRegion(IntRect(0, 0, 150, 300)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
+ transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
+
+ ScrollMetadata metadata;
+ FrameMetrics& metrics = metadata.GetMetrics();
+ metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24));
+ metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6));
+ metrics.SetScrollOffset(CSSPoint(10, 10));
+ metrics.SetScrollableRect(CSSRect(0, 0, 50, 50));
+ metrics.SetCumulativeResolution(LayoutDeviceToLayerScale2D(2, 2));
+ metrics.SetPresShellResolution(2.0f);
+ metrics.SetZoom(CSSToParentLayerScale2D(6, 6));
+ metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
+ metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
+
+ ScrollMetadata childMetadata = metadata;
+ FrameMetrics& childMetrics = childMetadata.GetMetrics();
+ childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
+
+ layers[0]->SetScrollMetadata(metadata);
+ layers[1]->SetScrollMetadata(childMetadata);
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // Both the parent and child layer should behave exactly the same here, because
+ // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
+
+ // initial transform
+ apzc->SetFrameMetrics(metrics);
+ apzc->NotifyLayersUpdated(metadata, true, true);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
+
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->NotifyLayersUpdated(childMetadata, true, true);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
+
+ // do an async scroll by 5 pixels and check the transform
+ metrics.ScrollBy(CSSPoint(5, 0));
+ apzc->SetFrameMetrics(metrics);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
+
+ childMetrics.ScrollBy(CSSPoint(5, 0));
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
+
+ // do an async zoom of 1.5x and check the transform
+ metrics.ZoomBy(1.5f);
+ apzc->SetFrameMetrics(metrics);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
+
+ childMetrics.ZoomBy(1.5f);
+ childApzc->SetFrameMetrics(childMetrics);
+ childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
+ EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
+
+ childApzc->Destroy();
+}
+
+TEST_F(APZCBasicTester, Fling) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // Fling down. Each step scroll further down
+ Pan(apzc, touchStart, touchEnd);
+ ParentLayerPoint lastPoint;
+ for (int i = 1; i < 50; i+=1) {
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(1));
+ EXPECT_GT(pointOut.y, lastPoint.y);
+ lastPoint = pointOut;
+ }
+}
+
+TEST_F(APZCBasicTester, FlingIntoOverscroll) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ // Scroll down by 25 px. Don't fling for simplicity.
+ ApzcPanNoFling(apzc, 50, 25);
+
+ // Now scroll back up by 20px, this time flinging after.
+ // The fling should cover the remaining 5 px of room to scroll, then
+ // go into overscroll, and finally snap-back to recover from overscroll.
+ Pan(apzc, 25, 45);
+ const TimeDuration increment = TimeDuration::FromMilliseconds(1);
+ bool reachedOverscroll = false;
+ bool recoveredFromOverscroll = false;
+ while (apzc->AdvanceAnimations(mcc->Time())) {
+ if (!reachedOverscroll && apzc->IsOverscrolled()) {
+ reachedOverscroll = true;
+ }
+ if (reachedOverscroll && !apzc->IsOverscrolled()) {
+ recoveredFromOverscroll = true;
+ }
+ mcc->AdvanceBy(increment);
+ }
+ EXPECT_TRUE(reachedOverscroll);
+ EXPECT_TRUE(recoveredFromOverscroll);
+}
+
+TEST_F(APZCBasicTester, PanningTransformNotifications) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Scroll down by 25 px. Ensure we only get one set of
+ // state change notifications.
+ //
+ // Then, scroll back up by 20px, this time flinging after.
+ // The fling should cover the remaining 5 px of room to scroll, then
+ // go into overscroll, and finally snap-back to recover from overscroll.
+ // Again, ensure we only get one set of state change notifications for
+ // this entire procedure.
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call("Simple pan"));
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
+ EXPECT_CALL(check, Call("Complex pan"));
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
+ EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
+ EXPECT_CALL(check, Call("Done"));
+ }
+
+ check.Call("Simple pan");
+ ApzcPanNoFling(apzc, 50, 25);
+ check.Call("Complex pan");
+ Pan(apzc, 25, 45);
+ apzc->AdvanceAnimationsUntilEnd();
+ check.Call("Done");
+}
+
+void APZCBasicTester::PanIntoOverscroll()
+{
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd);
+ EXPECT_TRUE(apzc->IsOverscrolled());
+}
+
+void APZCBasicTester::TestOverscroll()
+{
+ // Pan sufficiently to hit overscroll behavior
+ PanIntoOverscroll();
+
+ // Check that we recover from overscroll via an animation.
+ ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
+ SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
+}
+
+
+TEST_F(APZCBasicTester, OverScrollPanning) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ TestOverscroll();
+}
+
+// Tests that an overscroll animation doesn't trigger an assertion failure
+// in the case where a sample has a velocity of zero.
+TEST_F(APZCBasicTester, OverScroll_Bug1152051a) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Doctor the prefs to make the velocity zero at the end of the first sample.
+
+ // This ensures our incoming velocity to the overscroll animation is
+ // a round(ish) number, 4.9 (that being the distance of the pan before
+ // overscroll, which is 500 - 10 = 490 pixels, divided by the duration of
+ // the pan, which is 100 ms).
+ SCOPED_GFX_PREF(APZFlingFriction, float, 0);
+
+ // To ensure the velocity after the first sample is 0, set the spring
+ // stiffness to the incoming velocity (4.9) divided by the overscroll
+ // (400 pixels) times the step duration (1 ms).
+ SCOPED_GFX_PREF(APZOverscrollSpringStiffness, float, 0.01225f);
+
+ TestOverscroll();
+}
+
+// Tests that ending an overscroll animation doesn't leave around state that
+// confuses the next overscroll animation.
+TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ SCOPED_GFX_PREF(APZOverscrollStopDistanceThreshold, float, 0.1f);
+
+ // Pan sufficiently to hit overscroll behavior
+ PanIntoOverscroll();
+
+ // Sample animations once, to give the fling animation started on touch-up
+ // a chance to realize it's overscrolled, and schedule a call to
+ // HandleFlingOverscroll().
+ SampleAnimationOnce();
+
+ // This advances the time and runs the HandleFlingOverscroll task scheduled in
+ // the previous call, which starts an overscroll animation. It then samples
+ // the overscroll animation once, to get it to initialize the first overscroll
+ // sample.
+ SampleAnimationOnce();
+
+ // Do a touch-down to cancel the overscroll animation, and then a touch-up
+ // to schedule a new one since we're still overscrolled. We don't pan because
+ // panning can trigger functions that clear the overscroll animation state
+ // in other ways.
+ uint64_t blockId;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(apzc, blockId);
+ }
+ TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+
+ // Sample the second overscroll animation to its end.
+ // If the ending of the first overscroll animation fails to clear state
+ // properly, this will assert.
+ ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
+ SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
+}
+
+TEST_F(APZCBasicTester, OverScrollAbort) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Pan sufficiently to hit overscroll behavior
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd);
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ // This sample call will run to the end of the fling animation
+ // and will schedule the overscroll animation.
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(10000));
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ // At this point, we have an active overscroll animation.
+ // Check that cancelling the animation clears the overscroll.
+ apzc->CancelAnimation();
+ EXPECT_FALSE(apzc->IsOverscrolled());
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCBasicTester, OverScrollPanningAbort) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Pan sufficiently to hit overscroll behaviour. Keep the finger down so
+ // the pan does not end.
+ int touchStart = 500;
+ int touchEnd = 10;
+ Pan(apzc, touchStart, touchEnd, true); // keep finger down
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ // Check that calling CancelAnimation() while the user is still panning
+ // (and thus no fling or snap-back animation has had a chance to start)
+ // clears the overscroll.
+ apzc->CancelAnimation();
+ EXPECT_FALSE(apzc->IsOverscrolled());
+ apzc->AssertStateIsReset();
+}
diff --git a/gfx/layers/apz/test/gtest/TestEventRegions.cpp b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
new file mode 100644
index 000000000..8b3aac348
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZEventRegionsTester : public APZCTreeManagerTester {
+protected:
+ UniquePtr<ScopedLayerTreeRegistration> registration;
+ TestAsyncPanZoomController* rootApzc;
+
+ void CreateEventRegionsLayerTree1() {
+ const char* layerTreeSyntax = "c(tt)";
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 200, 200)), // root
+ nsIntRegion(IntRect(0, 0, 100, 200)), // left half
+ nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollHandoff(layers[1], root);
+ SetScrollHandoff(layers[2], root);
+
+ // Set up the event regions over a 200x200 area. The root layer has the
+ // whole 200x200 as the hit region; layers[1] has the left half and
+ // layers[2] has the bottom half. The bottom-left 100x100 area is also
+ // in the d-t-c region for both layers[1] and layers[2] (but layers[2] is
+ // on top so it gets the events by default if the main thread doesn't
+ // respond).
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
+ root->SetEventRegions(regions);
+ regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100));
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
+ layers[1]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
+ layers[2]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateEventRegionsLayerTree2() {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 500)),
+ nsIntRegion(IntRect(0, 150, 100, 100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+
+ // Set up the event regions so that the child thebes layer is positioned far
+ // away from the scrolling container layer.
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
+ root->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
+ layers[1]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateObscuringLayerTree() {
+ const char* layerTreeSyntax = "c(c(t)t)";
+ // LayerID 0 1 2 3
+ // 0 is the root.
+ // 1 is a parent scrollable layer.
+ // 2 is a child scrollable layer.
+ // 3 is the Obscurer, who ruins everything.
+ nsIntRegion layerVisibleRegions[] = {
+ // x coordinates are uninteresting
+ nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
+ nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
+ nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150]
+ nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200]
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100));
+ SetScrollHandoff(layers[2], layers[1]);
+ SetScrollHandoff(layers[1], root);
+
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
+ root->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
+ layers[1]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
+ layers[2]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateBug1119497LayerTree() {
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ // 0 is the root and has an APZC
+ // 1 is behind 2 and has an APZC
+ // 2 entirely covers 1 and should take all the input events, but has no APZC
+ // so hits to 2 should go to to the root APZC
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+
+ void CreateBug1117712LayerTree() {
+ const char* layerTreeSyntax = "c(c(t)t)";
+ // LayerID 0 1 2 3
+ // 0 is the root
+ // 1 is a container layer whose sole purpose to make a non-empty ancestor
+ // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
+ // transforms are different from 3's.
+ // 2 is a small layer that is the actual target
+ // 3 is a big layer obscuring 2 with a dispatch-to-content region
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 0, 0)),
+ nsIntRegion(IntRect(0, 0, 10, 10)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ };
+ Matrix4x4 layerTransforms[] = {
+ Matrix4x4(),
+ Matrix4x4::Translation(50, 0, 0),
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers);
+
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 10, 10));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[3], layers[2]);
+
+ EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
+ layers[2]->SetEventRegions(regions);
+ regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
+ layers[3]->SetEventRegions(regions);
+
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+};
+
+TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) {
+ CreateEventRegionsLayerTree1();
+
+ TestAsyncPanZoomController* root = ApzcOf(layers[0]);
+ TestAsyncPanZoomController* left = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on left"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on bottom"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on root"));
+ EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on bottom again"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped on left this time"));
+ }
+
+ TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
+
+ // Tap in the exposed hit regions of each of the layers once and ensure
+ // the clicks are dispatched right away
+ Tap(manager, ScreenIntPoint(10, 10), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on left");
+ Tap(manager, ScreenIntPoint(110, 110), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on bottom");
+ Tap(manager, ScreenIntPoint(110, 10), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on root");
+
+ // Now tap on the dispatch-to-content region where the layers overlap
+ Tap(manager, ScreenIntPoint(10, 110), tapDuration);
+ mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout
+ check.Call("Tap pending on d-t-c region");
+ mcc->RunThroughDelayedTasks(); // this runs the tap event
+ check.Call("Tapped on bottom again");
+
+ // Now let's do that again, but simulate a main-thread response
+ uint64_t inputBlockId = 0;
+ Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
+ nsTArray<ScrollableLayerGuid> targets;
+ targets.AppendElement(left->GetGuid());
+ manager->SetTargetAPZC(inputBlockId, targets);
+ while (mcc->RunThroughDelayedTasks()); // this runs the tap event
+ check.Call("Tapped on left this time");
+}
+
+TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
+ CreateEventRegionsLayerTree2();
+
+ // Tap in the area of the child layer that's not directly included in the
+ // parent layer's hit region. Verify that it comes out of the APZC's
+ // content controller, which indicates the input events got routed correctly
+ // to the APZC.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1);
+ Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
+}
+
+TEST_F(APZEventRegionsTester, Obscuration) {
+ CreateObscuringLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* parent = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* child = ApzcOf(layers[2]);
+
+ ApzcPanNoFling(parent, 75, 25);
+
+ HitTestResult result;
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result);
+ EXPECT_EQ(child, hit.get());
+ EXPECT_EQ(HitTestResult::HitLayer, result);
+}
+
+TEST_F(APZEventRegionsTester, Bug1119497) {
+ CreateBug1119497LayerTree();
+
+ HitTestResult result;
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result);
+ // We should hit layers[2], so |result| will be HitLayer but there's no
+ // actual APZC on layers[2], so it will be the APZC of the root layer.
+ EXPECT_EQ(ApzcOf(layers[0]), hit.get());
+ EXPECT_EQ(HitTestResult::HitLayer, result);
+}
+
+TEST_F(APZEventRegionsTester, Bug1117712) {
+ CreateBug1117712LayerTree();
+
+ TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
+
+ // These touch events should hit the dispatch-to-content region of layers[3]
+ // and so get queued with that APZC as the tentative target.
+ uint64_t inputBlockId = 0;
+ Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
+ // But now we tell the APZ that really it hit layers[2], and expect the tap
+ // to be delivered at the correct coordinates.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
+
+ nsTArray<ScrollableLayerGuid> targets;
+ targets.AppendElement(apzc2->GetGuid());
+ manager->SetTargetAPZC(inputBlockId, targets);
+}
diff --git a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
new file mode 100644
index 000000000..fcbc250f7
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -0,0 +1,638 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+
+class APZCGestureDetectorTester : public APZCBasicTester {
+public:
+ APZCGestureDetectorTester()
+ : APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
+ {
+ }
+
+protected:
+ FrameMetrics GetPinchableFrameMetrics()
+ {
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
+ fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
+ fm.SetScrollOffset(CSSPoint(300, 300));
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ // APZC only allows zooming on the root scrollable frame.
+ fm.SetIsRootContent(true);
+ // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
+ return fm;
+ }
+};
+
+TEST_F(APZCGestureDetectorTester, Pan_After_Pinch) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ MakeApzcZoomable();
+
+ // Test parameters
+ float zoomAmount = 1.25;
+ float pinchLength = 100.0;
+ float pinchLengthScaled = pinchLength * zoomAmount;
+ int focusX = 250;
+ int focusY = 300;
+ int panDistance = 20;
+
+ int firstFingerId = 0;
+ int secondFingerId = firstFingerId + 1;
+
+ // Put fingers down
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Spread fingers out to enter the pinch state
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLength, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLength, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do the actual pinch of 1.25x
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that the zoom changed, just to make sure our code above did what it
+ // was supposed to.
+ FrameMetrics zoomedMetrics = apzc->GetFrameMetrics();
+ float newZoom = zoomedMetrics.GetZoom().ToScaleFactor().scale;
+ EXPECT_EQ(originalMetrics.GetZoom().ToScaleFactor().scale * zoomAmount, newZoom);
+
+ // Now we lift one finger...
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // ... and pan with the remaining finger. This pan just breaks through the
+ // distance threshold.
+ focusY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // This one does an actual pan of 20 pixels
+ focusY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the remaining finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that we scrolled
+ FrameMetrics finalMetrics = apzc->GetFrameMetrics();
+ EXPECT_EQ(zoomedMetrics.GetScrollOffset().y - (panDistance / newZoom), finalMetrics.GetScrollOffset().y);
+
+ // Clear out any remaining fling animation and pending tasks
+ apzc->AdvanceAnimationsUntilEnd();
+ while (mcc->RunThroughDelayedTasks());
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, Pan_With_Tap) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // Making the APZC zoomable isn't really needed for the correct operation of
+ // this test, but it could help catch regressions where we accidentally enter
+ // a pinch state.
+ MakeApzcZoomable();
+
+ // Test parameters
+ int touchX = 250;
+ int touchY = 300;
+ int panDistance = 20;
+
+ int firstFingerId = 0;
+ int secondFingerId = firstFingerId + 1;
+
+ // Put finger down
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Start a pan, break through the threshold
+ touchY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do an actual pan for a bit
+ touchY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Put a second finger down
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the second finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Bust through the threshold again
+ touchY += 40;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Do some more actual panning
+ touchY += panDistance;
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Lift the first finger
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ // Verify that we scrolled
+ FrameMetrics finalMetrics = apzc->GetFrameMetrics();
+ float zoom = finalMetrics.GetZoom().ToScaleFactor().scale;
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y - (panDistance * 2 / zoom), finalMetrics.GetScrollOffset().y);
+
+ // Clear out any remaining fling animation and pending tasks
+ apzc->AdvanceAnimationsUntilEnd();
+ while (mcc->RunThroughDelayedTasks());
+ apzc->AssertStateIsReset();
+}
+
+class APZCFlingStopTester : public APZCGestureDetectorTester {
+protected:
+ // Start a fling, and then tap while the fling is ongoing. When
+ // aSlow is false, the tap will happen while the fling is at a
+ // high velocity, and we check that the tap doesn't trigger sending a tap
+ // to content. If aSlow is true, the tap will happen while the fling
+ // is at a slow velocity, and we check that the tap does trigger sending
+ // a tap to content. See bug 1022956.
+ void DoFlingStopTest(bool aSlow) {
+ int touchStart = 50;
+ int touchEnd = 10;
+
+ // Start the fling down.
+ Pan(apzc, touchStart, touchEnd);
+ // The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
+
+ // If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
+ // the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
+ // friction and threshold values change, but they should be deterministic at least.
+ int timeDelta = aSlow ? 2000 : 10;
+ int tapCallsExpected = aSlow ? 2 : 1;
+
+ // Advance the fling animation by timeDelta milliseconds.
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
+
+ // Deliver a tap to abort the fling. Ensure that we get a SingleTap
+ // call out of it if and only if the fling is slow.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _)).Times(tapCallsExpected);
+ Tap(apzc, ScreenIntPoint(10, 10), 0);
+ while (mcc->RunThroughDelayedTasks());
+
+ // Deliver another tap, to make sure that taps are flowing properly once
+ // the fling is aborted.
+ Tap(apzc, ScreenIntPoint(100, 100), 0);
+ while (mcc->RunThroughDelayedTasks());
+
+ // Verify that we didn't advance any further after the fling was aborted, in either case.
+ ParentLayerPoint finalPointOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut);
+ EXPECT_EQ(pointOut.x, finalPointOut.x);
+ EXPECT_EQ(pointOut.y, finalPointOut.y);
+
+ apzc->AssertStateIsReset();
+ }
+
+ void DoFlingStopWithSlowListener(bool aPreventDefault) {
+ MakeApzcWaitForMainThread();
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ uint64_t blockId = 0;
+
+ // Start the fling down.
+ Pan(apzc, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
+ apzc->ConfirmTarget(blockId);
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ // Sample the fling a couple of times to ensure it's going.
+ ParentLayerPoint point, finalPoint;
+ AsyncTransform viewTransform;
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
+ apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, TimeDuration::FromMilliseconds(10));
+ EXPECT_GT(finalPoint.y, point.y);
+
+ // Now we put our finger down to stop the fling
+ TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+
+ // Re-sample to make sure it hasn't moved
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
+ EXPECT_EQ(finalPoint.x, point.x);
+ EXPECT_EQ(finalPoint.y, point.y);
+
+ // respond to the touchdown that stopped the fling.
+ // even if we do a prevent-default on it, the animation should remain stopped.
+ apzc->ContentReceivedInputBlock(blockId, aPreventDefault);
+
+ // Verify the page hasn't moved
+ apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(70));
+ EXPECT_EQ(finalPoint.x, point.x);
+ EXPECT_EQ(finalPoint.y, point.y);
+
+ // clean up
+ TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCFlingStopTester, FlingStop) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopTest(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopTap) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopTest(true);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopWithSlowListener(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ DoFlingStopWithSlowListener(true);
+}
+
+TEST_F(APZCGestureDetectorTester, ShortPress) {
+ MakeApzcUnzoomable();
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ // This verifies that the single tap notification is sent after the
+ // touchup is fully processed. The ordering here is important.
+ EXPECT_CALL(check, Call("pre-tap"));
+ EXPECT_CALL(check, Call("post-tap"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ }
+
+ check.Call("pre-tap");
+ TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+ check.Call("post-tap");
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, MediumPress) {
+ MakeApzcUnzoomable();
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ // This verifies that the single tap notification is sent after the
+ // touchup is fully processed. The ordering here is important.
+ EXPECT_CALL(check, Call("pre-tap"));
+ EXPECT_CALL(check, Call("post-tap"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ }
+
+ check.Call("pre-tap");
+ TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(400));
+ check.Call("post-tap");
+
+ apzc->AssertStateIsReset();
+}
+
+class APZCLongPressTester : public APZCGestureDetectorTester {
+protected:
+ void DoLongPressTest(uint32_t aBehavior) {
+ MakeApzcUnzoomable();
+
+ uint64_t blockId = 0;
+
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ // SetAllowedTouchBehavior() must be called after sending touch-start.
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+ apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
+ }
+ // Have content "respond" to the touchstart
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call("preHandleLongTap"));
+ blockId++;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTap"));
+
+ EXPECT_CALL(check, Call("preHandleLongTapUp"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTapUp"));
+ }
+
+ // Manually invoke the longpress while the touch is currently down.
+ check.Call("preHandleLongTap");
+ mcc->RunThroughDelayedTasks();
+ check.Call("postHandleLongTap");
+
+ // Dispatching the longpress event starts a new touch block, which
+ // needs a new content response and also has a pending timeout task
+ // in the queue. Deal with those here. We do the content response first
+ // with preventDefault=false, and then we run the timeout task which
+ // "loses the race" and does nothing.
+ apzc->ContentReceivedInputBlock(blockId, false);
+ mcc->AdvanceByMillis(1000);
+
+ // Finally, simulate lifting the finger. Since the long-press wasn't
+ // prevent-defaulted, we should get a long-tap-up event.
+ check.Call("preHandleLongTapUp");
+ status = TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
+ mcc->RunThroughDelayedTasks();
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+ check.Call("postHandleLongTapUp");
+
+ apzc->AssertStateIsReset();
+ }
+
+ void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
+ MakeApzcUnzoomable();
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+
+ int touchX = 10,
+ touchStartY = 10,
+ touchEndY = 50;
+
+ uint64_t blockId = 0;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(touchX, touchStartY), mcc->Time(), &blockId);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ // SetAllowedTouchBehavior() must be called after sending touch-start.
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+ apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
+ }
+ // Have content "respond" to the touchstart
+ apzc->ContentReceivedInputBlock(blockId, false);
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call("preHandleLongTap"));
+ blockId++;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
+ EXPECT_CALL(check, Call("postHandleLongTap"));
+ }
+
+ // Manually invoke the longpress while the touch is currently down.
+ check.Call("preHandleLongTap");
+ mcc->RunThroughDelayedTasks();
+ check.Call("postHandleLongTap");
+
+ // There should be a TimeoutContentResponse task in the queue still,
+ // waiting for the response from the longtap event dispatched above.
+ // Send the signal that content has handled the long-tap, and then run
+ // the timeout task (it will be a no-op because the content "wins" the
+ // race. This takes the place of the "contextmenu" event.
+ apzc->ContentReceivedInputBlock(blockId, true);
+ mcc->AdvanceByMillis(1000);
+
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
+ status = apzc->ReceiveInputEvent(mti, nullptr);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
+ status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCLongPressTester, LongPress) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoLongPressTest(mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+TEST_F(APZCLongPressTester, LongPressWithTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoLongPressTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+}
+
+TEST_F(APZCLongPressTester, LongPressPreventDefault) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTap) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], false);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
+ MakeApzcWaitForMainThread();
+ MakeApzcUnzoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSecondTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], false);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], true);
+ apzc->ContentReceivedInputBlock(blockIds[1], false);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+
+ uint64_t blockIds[2];
+ DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
+
+ // responses to the two touchstarts
+ apzc->ContentReceivedInputBlock(blockIds[0], true);
+ apzc->ContentReceivedInputBlock(blockIds[1], true);
+
+ apzc->AssertStateIsReset();
+}
+
+// Test for bug 947892
+// We test whether we dispatch tap event when the tap is followed by pinch.
+TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+
+ int inputId = 0;
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
+ MakeApzcZoomable();
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
+
+ int inputId = 0;
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
+ mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
+ apzc->ReceiveInputEvent(mti, nullptr);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
+ // Since we try to allow concurrent input blocks of different types to
+ // co-exist, the wheel block shouldn't interrupt the long-press detection.
+ // But more importantly, this shouldn't crash, which is what it did at one
+ // point in time.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(1);
+
+ uint64_t touchBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
+ if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+ SetDefaultAllowedTouchBehavior(apzc, touchBlockId);
+ }
+ mcc->AdvanceByMillis(10);
+ Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(touchBlockId, wheelBlockId);
+ mcc->AdvanceByMillis(1000);
+}
+
+TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
+ // In this test, even though the wheel block comes right after the tap, the
+ // tap should still be dispatched because it completes fully before the wheel
+ // block arrived.
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+
+ // We make the APZC zoomable so the gesture detector needs to wait to
+ // distinguish between tap and double-tap. During that timeout is when we
+ // insert the wheel event.
+ MakeApzcZoomable();
+
+ uint64_t touchBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100),
+ nullptr, &touchBlockId);
+ mcc->AdvanceByMillis(10);
+ Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(touchBlockId, wheelBlockId);
+ while (mcc->RunThroughDelayedTasks());
+}
diff --git a/gfx/layers/apz/test/gtest/TestHitTesting.cpp b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
new file mode 100644
index 000000000..182194208
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZHitTestingTester : public APZCTreeManagerTester {
+protected:
+ ScreenToParentLayerMatrix4x4 transformToApzc;
+ ParentLayerToScreenMatrix4x4 transformToGecko;
+
+ already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
+ RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
+ if (hit) {
+ transformToApzc = manager->GetScreenToApzcTransform(hit.get());
+ transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
+ }
+ return hit.forget();
+ }
+
+protected:
+ void CreateHitTesting1LayerTree() {
+ const char* layerTreeSyntax = "c(tttt)";
+ // LayerID 0 1234
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(10,10,20,20)),
+ nsIntRegion(IntRect(10,10,20,20)),
+ nsIntRegion(IntRect(5,5,20,20)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ }
+
+ void CreateHitTesting2LayerTree() {
+ const char* layerTreeSyntax = "c(tc(t))";
+ // LayerID 0 12 3
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,100,100)),
+ nsIntRegion(IntRect(10,10,40,40)),
+ nsIntRegion(IntRect(10,60,40,40)),
+ nsIntRegion(IntRect(10,60,40,40)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4::Scaling(2, 1, 1),
+ Matrix4x4(),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
+
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
+ }
+
+ void DisableApzOn(Layer* aLayer) {
+ ScrollMetadata m = aLayer->GetScrollMetadata(0);
+ m.SetForceDisableApz(true);
+ aLayer->SetScrollMetadata(m);
+ }
+
+ void CreateComplexMultiLayerTree() {
+ const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
+ // LayerID 0 12 3 45 6 7 89
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,300,400)), // root(0)
+ nsIntRegion(IntRect(0,0,100,100)), // thebes(1) in top-left
+ nsIntRegion(IntRect(50,50,200,300)), // container(2) centered in root(0)
+ nsIntRegion(IntRect(50,50,200,300)), // thebes(3) fully occupying parent container(2)
+ nsIntRegion(IntRect(0,200,100,100)), // thebes(4) in bottom-left
+ nsIntRegion(IntRect(200,0,100,400)), // container(5) along the right 100px of root(0)
+ nsIntRegion(IntRect(200,0,100,200)), // container(6) taking up the top half of parent container(5)
+ nsIntRegion(IntRect(200,0,100,200)), // thebes(7) fully occupying parent container(6)
+ nsIntRegion(IntRect(200,200,100,100)), // thebes(8) in bottom-right (below (6))
+ nsIntRegion(IntRect(200,300,100,100)), // thebes(9) in bottom-right (below (8))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2);
+ SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1);
+ SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3);
+ }
+
+ void CreateBug1148350LayerTree() {
+ const char* layerTreeSyntax = "c(t)";
+ // LayerID 0 1
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,200)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ }
+};
+
+// A simple hit testing test that doesn't involve any transforms on layers.
+TEST_F(APZHitTestingTester, HitTesting1) {
+ CreateHitTesting1LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ // No APZC attached so hit testing will return no APZC at (20,20)
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+
+ uint32_t paintSequenceNumber = 0;
+
+ // Now we have a root APZC that will match the page
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+ // expect hit point at LayerIntPoint(15, 15)
+ EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
+ EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
+
+ // Now we have a sub APZC with a better fit
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(ApzcOf(layers[3]), hit.get());
+ // expect hit point at LayerIntPoint(25, 25)
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+
+ // At this point, layers[4] obscures layers[3] at the point (15, 15) so
+ // hitting there should hit the root APZC
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+
+ // Now test hit testing when we have two scrollable layers
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
+ manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
+ hit = GetTargetAPZC(ScreenPoint(15, 15));
+ EXPECT_EQ(ApzcOf(layers[4]), hit.get());
+ // expect hit point at LayerIntPoint(15, 15)
+ EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
+ EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
+
+ // Hit test ouside the reach of layer[3,4] but inside root
+ hit = GetTargetAPZC(ScreenPoint(90, 90));
+ EXPECT_EQ(ApzcOf(root), hit.get());
+ // expect hit point at LayerIntPoint(90, 90)
+ EXPECT_EQ(ParentLayerPoint(90, 90), transformToApzc.TransformPoint(ScreenPoint(90, 90)));
+ EXPECT_EQ(ScreenPoint(90, 90), transformToGecko.TransformPoint(ParentLayerPoint(90, 90)));
+
+ // Hit test ouside the reach of any layer
+ hit = GetTargetAPZC(ScreenPoint(1000, 10));
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+ hit = GetTargetAPZC(ScreenPoint(-1000, 10));
+ EXPECT_EQ(nullAPZC, hit.get());
+ EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
+ EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
+}
+
+// A more involved hit testing test that involves css and async transforms.
+TEST_F(APZHitTestingTester, HitTesting2) {
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+
+ CreateHitTesting2LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ // At this point, the following holds (all coordinates in screen pixels):
+ // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
+ // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
+ // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
+ // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
+
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+ TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
+
+ // Hit an area that's clearly on the root layer but not any of the child layers.
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
+ EXPECT_EQ(apzcroot, hit.get());
+ EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc.TransformPoint(ScreenPoint(75, 25)));
+ EXPECT_EQ(ScreenPoint(75, 25), transformToGecko.TransformPoint(ParentLayerPoint(75, 25)));
+
+ // Hit an area on the root that would be on layers[3] if layers[2]
+ // weren't transformed.
+ // Note that if layers[2] were scrollable, then this would hit layers[2]
+ // because its composition bounds would be at (10,60)-(50,100) (and the
+ // scale-only transform that we set on layers[2] would be invalid because
+ // it would place the layer into overscroll, as its composition bounds
+ // start at x=10 but its content at x=20).
+ hit = GetTargetAPZC(ScreenPoint(15, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc.TransformPoint(ScreenPoint(15, 75)));
+ EXPECT_EQ(ScreenPoint(15, 75), transformToGecko.TransformPoint(ParentLayerPoint(15, 75)));
+
+ // Hit an area on layers[1].
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzc1, hit.get());
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+
+ // Hit an area on layers[3].
+ hit = GetTargetAPZC(ScreenPoint(25, 75));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc should unapply layers[2]'s transform
+ EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 75)));
+ // and transformToGecko should reapply it
+ EXPECT_EQ(ScreenPoint(25, 75), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
+
+ // Hit an area on layers[3] that would be on the root if layers[2]
+ // weren't transformed.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc should unapply layers[2]'s transform
+ EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // and transformToGecko should reapply it
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(37.5, 75)));
+
+ // Pan the root layer upward by 50 pixels.
+ // This causes layers[1] to scroll out of view, and an async transform
+ // of -50 to be set on the root layer.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+
+ // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
+ // Since this paint request is in the queue to Gecko, transformToGecko will
+ // take it into account.
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Hit where layers[3] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // and transformToGecko unapplies it and then reapplies it, because by the
+ // time the event being transformed reaches Gecko the new paint request will
+ // have been handled.
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
+
+ // Hit where layers[1] used to be and where layers[3] should now be.
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzc3, hit.get());
+ // transformToApzc unapplies both layers[2]'s css transform and the root's
+ // async transform
+ EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ // transformToGecko reapplies both the css transform and the async transform
+ // because we have already issued a paint request with it.
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
+
+ // This second pan will move the APZC by another 50 pixels.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Hit where layers[3] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(75, 75));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
+ // transformToGecko unapplies the full async transform of -100 pixels
+ EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
+
+ // Hit where layers[1] used to be. It should now hit the root.
+ hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(apzcroot, hit.get());
+ // transformToApzc doesn't unapply the root's own async transform
+ EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
+ // transformToGecko unapplies the full async transform of -100 pixels
+ EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
+}
+
+TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
+ CreateComplexMultiLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ /* The layer tree looks like this:
+
+ 0
+ |----|--+--|----|
+ 1 2 4 5
+ | /|\
+ 3 6 8 9
+ |
+ 7
+
+ Layers 1,2 have the same APZC
+ Layers 4,6,8 have the same APZC
+ Layer 7 has an APZC
+ Layer 9 has an APZC
+ */
+
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ // Ensure all the scrollable layers have an APZC
+ EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
+ EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
+ EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
+ // Ensure those that scroll together have the same APZCs
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+ EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
+ EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
+ // Ensure those that don't scroll together have different APZCs
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
+ EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
+ EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
+ EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
+ // Ensure the APZC parent chains are set up correctly
+ TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
+ TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
+ TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
+ TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
+ EXPECT_EQ(nullptr, layers1_2->GetParent());
+ EXPECT_EQ(nullptr, layers4_6_8->GetParent());
+ EXPECT_EQ(layers4_6_8, layer7->GetParent());
+ EXPECT_EQ(nullptr, layer9->GetParent());
+ // Ensure the hit-testing tree looks like the layer tree
+ RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
+ RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
+ RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
+ RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
+ RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
+ RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
+ EXPECT_EQ(nullptr, node1->GetPrevSibling());
+ EXPECT_EQ(nullptr, node3->GetPrevSibling());
+ EXPECT_EQ(nullptr, node6->GetPrevSibling());
+ EXPECT_EQ(nullptr, node7->GetPrevSibling());
+ EXPECT_EQ(nullptr, node1->GetLastChild());
+ EXPECT_EQ(nullptr, node3->GetLastChild());
+ EXPECT_EQ(nullptr, node4->GetLastChild());
+ EXPECT_EQ(nullptr, node7->GetLastChild());
+ EXPECT_EQ(nullptr, node8->GetLastChild());
+ EXPECT_EQ(nullptr, node9->GetLastChild());
+
+ RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
+ EXPECT_EQ(ApzcOf(layers[1]), hit.get());
+ hit = GetTargetAPZC(ScreenPoint(275, 375));
+ EXPECT_EQ(ApzcOf(layers[9]), hit.get());
+ hit = GetTargetAPZC(ScreenPoint(250, 100));
+ EXPECT_EQ(ApzcOf(layers[7]), hit.get());
+}
+
+TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ // The main purpose of this test is to verify that touch-start events (or anything
+ // that starts a new input block) don't ever get untransformed. This should always
+ // hold because the APZ code should flush repaints when we start a new input block
+ // and the transform to gecko space should be empty.
+
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ // At this point, the following holds (all coordinates in screen pixels):
+ // layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
+
+ MockFunction<void(std::string checkPointName)> check;
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-first-touch-start"));
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-second-fling"));
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
+ EXPECT_CALL(check, Call("post-second-touch-start"));
+ }
+
+ // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Verify that a touch start doesn't get untransformed
+ ScreenIntPoint touchPoint(50, 50);
+ MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
+
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+ check.Call("post-first-touch-start");
+
+ // Send a touchend to clear state
+ mti.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(mti, nullptr, nullptr);
+
+ mcc->AdvanceByMillis(1000);
+
+ // Now do two pans. The first of these will dispatch a repaint request, as above.
+ // The second will get stuck in the paint throttler because the first one doesn't
+ // get marked as "completed", so this will result in a non-empty LD transform.
+ // (Note that any outstanding repaint requests from the first half of this test
+ // don't impact this half because we advance the time by 1 second, which will trigger
+ // the max-wait-exceeded codepath in the paint throttler).
+ ApzcPanNoFling(apzcroot, 100, 50);
+ check.Call("post-second-fling");
+ ApzcPanNoFling(apzcroot, 100, 50);
+
+ // Ensure that a touch start again doesn't get untransformed by flushing
+ // a repaint
+ mti.mType = MultiTouchInput::MULTITOUCH_START;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+ check.Call("post-second-touch-start");
+
+ mti.mType = MultiTouchInput::MULTITOUCH_END;
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
+ EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
+}
+
+TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
+ // The purpose of this test is to ensure that wheel events trigger a repaint
+ // flush as per bug 1166871, and that the wheel event untransform is a no-op.
+
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
+ ScreenPoint origin(100, 50);
+ for (int i = 0; i < 3; i++) {
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+
+ AsyncTransform viewTransform;
+ ParentLayerPoint point;
+ apzcroot->SampleContentTransformForFrame(&viewTransform, point);
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ((i + 1) * 10, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
+
+ mcc->AdvanceByMillis(5);
+ }
+}
+
+TEST_F(APZHitTestingTester, TestForceDisableApz) {
+ CreateSimpleScrollingLayer();
+ DisableApzOn(root);
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+ ScreenPoint origin(100, 50);
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+
+ AsyncTransform viewTransform;
+ ParentLayerPoint point;
+ apzcroot->SampleContentTransformForFrame(&viewTransform, point);
+ // Since APZ is force-disabled, we expect to see the async transform via
+ // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ(10, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ(-10, viewTransform.mTranslation.y);
+ viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ EXPECT_EQ(0, point.x);
+ EXPECT_EQ(0, point.y);
+ EXPECT_EQ(0, viewTransform.mTranslation.x);
+ EXPECT_EQ(0, viewTransform.mTranslation.y);
+
+ mcc->AdvanceByMillis(10);
+
+ // With untransforming events we should get normal behaviour (in this case,
+ // no noticeable untransform, because the repaint request already got
+ // flushed).
+ swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 0, false);
+ EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+ EXPECT_EQ(origin, swi.mOrigin);
+}
+
+TEST_F(APZHitTestingTester, Bug1148350) {
+ CreateBug1148350LayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ MockFunction<void(std::string checkPointName)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped without transform"));
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+ EXPECT_CALL(check, Call("Tapped with interleaved transform"));
+ }
+
+ Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
+ mcc->RunThroughDelayedTasks();
+ check.Call("Tapped without transform");
+
+ uint64_t blockId;
+ TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
+ if (gfxPrefs::TouchActionEnabled()) {
+ SetDefaultAllowedTouchBehavior(manager, blockId);
+ }
+ mcc->AdvanceByMillis(100);
+
+ layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
+ layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
+ mcc->RunThroughDelayedTasks();
+ check.Call("Tapped with interleaved transform");
+}
+
+TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
+ // Create the layer tree.
+ const char* layerTreeSyntax = "c(tt)";
+ // LayerID 0 12
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,200)),
+ nsIntRegion(IntRect(0,0,200,100))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+
+ // Add root scroll metadata to the first painted layer.
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200));
+
+ // Add root and subframe scroll metadata to the second painted layer.
+ // Give the subframe metadata a scroll clip corresponding to the subframe's
+ // composition bounds.
+ // Importantly, give the layer a layer clip which leaks outside of the
+ // subframe's composition bounds.
+ ScrollMetadata rootMetadata = BuildScrollMetadata(
+ FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200),
+ ParentLayerRect(0,0,200,200));
+ ScrollMetadata subframeMetadata = BuildScrollMetadata(
+ FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200),
+ ParentLayerRect(0,0,200,100));
+ subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100))));
+ layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
+ layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200)));
+ SetEventRegionsBasedOnBottommostMetrics(layers[2]);
+
+ // Build the hit testing tree.
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ // Pan on a region that's inside layers[2]'s layer clip, but outside
+ // its subframe metadata's scroll clip.
+ Pan(manager, 120, 110);
+
+ // Test that the subframe hasn't scrolled.
+ EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset());
+}
diff --git a/gfx/layers/apz/test/gtest/TestInputQueue.cpp b/gfx/layers/apz/test/gtest/TestInputQueue.cpp
new file mode 100644
index 000000000..d05b6d66e
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestInputQueue.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+// Test of scenario described in bug 1269067 - that a continuing mouse drag
+// doesn't interrupt a wheel scrolling animation
+TEST_F(APZCTreeManagerTester, WheelInterruptedByMouseDrag) {
+ // Set up a scrollable layer
+ CreateSimpleScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
+
+ uint64_t dragBlockId = 0;
+ uint64_t wheelBlockId = 0;
+ uint64_t tmpBlockId = 0;
+
+ // First start the mouse drag
+ MouseDown(apzc, ScreenIntPoint(5, 5), mcc->Time(), &dragBlockId);
+ MouseMove(apzc, ScreenIntPoint(6, 6), mcc->Time(), &tmpBlockId);
+ EXPECT_EQ(dragBlockId, tmpBlockId);
+
+ // Insert the wheel event, check that it has a new block id
+ SmoothWheel(apzc, ScreenIntPoint(6, 6), ScreenPoint(0, 1), mcc->Time(), &wheelBlockId);
+ EXPECT_NE(dragBlockId, wheelBlockId);
+
+ // Continue the drag, check that the block id is the same as before
+ MouseMove(apzc, ScreenIntPoint(7, 5), mcc->Time(), &tmpBlockId);
+ EXPECT_EQ(dragBlockId, tmpBlockId);
+
+ // Finish the wheel animation
+ apzc->AdvanceAnimationsUntilEnd();
+
+ // Check that it scrolled
+ ParentLayerPoint scroll = apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
+ EXPECT_EQ(scroll.x, 0);
+ EXPECT_EQ(scroll.y, 10); // We scrolled 1 "line" or 10 pixels
+}
diff --git a/gfx/layers/apz/test/gtest/TestPanning.cpp b/gfx/layers/apz/test/gtest/TestPanning.cpp
new file mode 100644
index 000000000..3ee19a58d
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestPanning.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCPanningTester : public APZCBasicTester {
+protected:
+ void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
+ {
+ if (aShouldTriggerScroll) {
+ // One repaint request for each pan.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
+ } else {
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+ }
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(aBehavior);
+
+ // Pan down
+ PanAndCheckStatus(apzc, touchStart, touchEnd, aShouldBeConsumed, &allowedTouchBehaviors);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ if (aShouldTriggerScroll) {
+ EXPECT_EQ(ParentLayerPoint(0, -(touchEnd-touchStart)), pointOut);
+ EXPECT_NE(AsyncTransform(), viewTransformOut);
+ } else {
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+ }
+
+ // Clear the fling from the previous pan, or stopping it will
+ // consume the next touchstart
+ apzc->CancelAnimation();
+
+ // Pan back
+ PanAndCheckStatus(apzc, touchEnd, touchStart, aShouldBeConsumed, &allowedTouchBehaviors);
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+ }
+
+ void DoPanWithPreventDefaultTest()
+ {
+ MakeApzcWaitForMainThread();
+
+ int touchStart = 50;
+ int touchEnd = 10;
+ ParentLayerPoint pointOut;
+ AsyncTransform viewTransformOut;
+ uint64_t blockId = 0;
+
+ // Pan down
+ nsTArray<uint32_t> allowedTouchBehaviors;
+ allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+ PanAndCheckStatus(apzc, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
+
+ // Send the signal that content has handled and preventDefaulted the touch
+ // events. This flushes the event queue.
+ apzc->ContentReceivedInputBlock(blockId, true);
+
+ apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
+ EXPECT_EQ(ParentLayerPoint(), pointOut);
+ EXPECT_EQ(AsyncTransform(), viewTransformOut);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCPanningTester, Pan) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
+// to bottom and back - from bottom to top.
+// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
+// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
+// behavior.
+// However, the events will be marked as consumed even if the behavior in PAN_X, because the user could
+// move their finger horizontally too - APZ has no way of knowing beforehand and so must consume the
+// events.
+TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+ | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionNone) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(false, false, 0);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
+ DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ DoPanWithPreventDefaultTest();
+}
+
+TEST_F(APZCPanningTester, PanWithPreventDefault) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPanWithPreventDefaultTest();
+}
diff --git a/gfx/layers/apz/test/gtest/TestPinching.cpp b/gfx/layers/apz/test/gtest/TestPinching.cpp
new file mode 100644
index 000000000..54fb7a757
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestPinching.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCBasicTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCPinchTester : public APZCBasicTester {
+public:
+ explicit APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
+ : APZCBasicTester(aGestureBehavior)
+ {
+ }
+
+protected:
+ FrameMetrics GetPinchableFrameMetrics()
+ {
+ FrameMetrics fm;
+ fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
+ fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
+ fm.SetScrollOffset(CSSPoint(300, 300));
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ // APZC only allows zooming on the root scrollable frame.
+ fm.SetIsRootContent(true);
+ // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
+ return fm;
+ }
+
+ void DoPinchTest(bool aShouldTriggerPinch,
+ nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
+ {
+ apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+ MakeApzcZoomable();
+
+ if (aShouldTriggerPinch) {
+ // One repaint request for each gesture.
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
+ } else {
+ EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
+ }
+
+ int touchInputId = 0;
+ if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
+ PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+ touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+ } else {
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+ aShouldTriggerPinch);
+ }
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+
+ if (aShouldTriggerPinch) {
+ // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
+ EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(305, fm.GetScrollOffset().x);
+ EXPECT_EQ(310, fm.GetScrollOffset().y);
+ } else {
+ // The frame metrics should stay the same since touch-action:none makes
+ // apzc ignore pinch gestures.
+ EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(300, fm.GetScrollOffset().x);
+ EXPECT_EQ(300, fm.GetScrollOffset().y);
+ }
+
+ // part 2 of the test, move to the top-right corner of the page and pinch and
+ // make sure we stay in the correct spot
+ fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
+ fm.SetScrollOffset(CSSPoint(930, 5));
+ apzc->SetFrameMetrics(fm);
+ // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
+
+ if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
+ PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+ touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+ } else {
+ PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+ aShouldTriggerPinch);
+ }
+
+ fm = apzc->GetFrameMetrics();
+
+ if (aShouldTriggerPinch) {
+ // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
+ EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(880, fm.GetScrollOffset().x);
+ EXPECT_EQ(0, fm.GetScrollOffset().y);
+ } else {
+ EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
+ EXPECT_EQ(930, fm.GetScrollOffset().x);
+ EXPECT_EQ(5, fm.GetScrollOffset().y);
+ }
+ }
+};
+
+class APZCPinchGestureDetectorTester : public APZCPinchTester {
+public:
+ APZCPinchGestureDetectorTester()
+ : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
+ {
+ }
+
+ void DoPinchWithPreventDefaultTest() {
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ MakeApzcWaitForMainThread();
+ MakeApzcZoomable();
+
+ int touchInputId = 0;
+ uint64_t blockId = 0;
+ PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
+ nullptr, nullptr, &blockId);
+
+ // Send the prevent-default notification for the touch block
+ apzc->ContentReceivedInputBlock(blockId, true);
+
+ // verify the metrics didn't change (i.e. the pinch was ignored)
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+ }
+};
+
+TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPinchTest(true);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+ DoPinchTest(true);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
+ mozilla::layers::AllowedTouchBehavior::NONE };
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors;
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ DoPinchTest(true, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ nsTArray<uint32_t> behaviors;
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+ behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) {
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ // Since we are preventing the pinch action via touch-action we should not be
+ // sending the pinch gesture notifications that would normally be sent when
+ // APZAllowZooming is false.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
+ nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
+ mozilla::layers::AllowedTouchBehavior::NONE };
+ DoPinchTest(false, &behaviors);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
+ DoPinchWithPreventDefaultTest();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ // Since we are preventing the pinch action we should not be sending the pinch
+ // gesture notifications that would normally be sent when APZAllowZooming is
+ // false.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
+
+ DoPinchWithPreventDefaultTest();
+}
+
+TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
+ // set up APZ
+ apzc->SetFrameMetrics(GetPinchableFrameMetrics());
+ MakeApzcUnzoomable();
+
+ nsEventStatus statuses[3]; // scalebegin, scale, scaleend
+ PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
+ 10, &statuses);
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+
+ // It starts from (300, 300), then moves the focus point from (250, 350) to
+ // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
+ // causes the scroll offset to change by half of that (25, 25) pixels.
+ EXPECT_EQ(325, fm.GetScrollOffset().x);
+ EXPECT_EQ(325, fm.GetScrollOffset().y);
+ EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // When APZAllowZooming is false, the ZoomConstraintsClient produces
+ // ZoomConstraints with mAllowZoom set to false.
+ MakeApzcUnzoomable();
+
+ // With APZAllowZooming false, we expect the NotifyPinchGesture function to
+ // get called as the pinch progresses, but the metrics shouldn't change.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+
+ int touchInputId = 0;
+ uint64_t blockId = 0;
+ PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
+ nullptr, nullptr, &blockId);
+
+ // verify the metrics didn't change (i.e. the pinch was ignored inside APZ)
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) {
+ SCOPED_GFX_PREF(APZAllowZooming, bool, false);
+ SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+
+ FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+ apzc->SetFrameMetrics(originalMetrics);
+
+ // When APZAllowZooming is false, the ZoomConstraintsClient produces
+ // ZoomConstraints with mAllowZoom set to false.
+ MakeApzcUnzoomable();
+
+ // With APZAllowZooming false, we expect the NotifyPinchGesture function to
+ // get called as the pinch progresses, but the metrics shouldn't change.
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
+
+ int inputId = 0;
+ ScreenIntPoint focus(250, 300);
+
+ // Do a pinch holding a zero span and moving the focus by y=100
+
+ MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiStart, nullptr);
+
+ focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in GestureEventListener.cpp
+ MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiMove1, nullptr);
+
+ focus.y -= 100; // do a two-finger scroll of 100 screen pixels
+ MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiMove2, nullptr);
+
+ MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
+ mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
+ apzc->ReceiveInputEvent(mtiEnd, nullptr);
+
+ // Done, check the metrics to make sure we scrolled by 100 screen pixels,
+ // which is 50 CSS pixels for the pinchable frame metrics.
+
+ FrameMetrics fm = apzc->GetFrameMetrics();
+ EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
+ EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+ EXPECT_EQ(originalMetrics.GetScrollOffset().y + 50, fm.GetScrollOffset().y);
+
+ apzc->AssertStateIsReset();
+}
diff --git a/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
new file mode 100644
index 000000000..d57d09ead
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
@@ -0,0 +1,521 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZScrollHandoffTester : public APZCTreeManagerTester {
+protected:
+ UniquePtr<ScopedLayerTreeRegistration> registration;
+ TestAsyncPanZoomController* rootApzc;
+
+ void CreateScrollHandoffLayerTree1() {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 50))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[1], root);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ rootApzc->GetFrameMetrics().SetIsRootContent(true); // make root APZC zoomable
+ }
+
+ void CreateScrollHandoffLayerTree2() {
+ const char* layerTreeSyntax = "c(c(t))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 50))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollHandoff(layers[1], root);
+ SetScrollHandoff(layers[2], layers[1]);
+ // No ScopedLayerTreeRegistration as that just needs to be done once per test
+ // and this is the second layer tree for a particular test.
+ MOZ_ASSERT(registration);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ }
+
+ void CreateScrollHandoffLayerTree3() {
+ const char* layerTreeSyntax = "c(c(t)c(t))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)), // root
+ nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling parent 1
+ nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling child 1
+ nsIntRegion(IntRect(0, 50, 100, 50)), // scrolling parent 2
+ nsIntRegion(IntRect(0, 50, 100, 50)) // scrolling child 2
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100));
+ SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
+ SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 4, CSSRect(0, 50, 100, 100));
+ SetScrollHandoff(layers[1], layers[0]);
+ SetScrollHandoff(layers[3], layers[0]);
+ SetScrollHandoff(layers[2], layers[1]);
+ SetScrollHandoff(layers[4], layers[3]);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ }
+
+ void CreateScrollgrabLayerTree(bool makeParentScrollable = true) {
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)), // scroll-grabbing parent
+ nsIntRegion(IntRect(0, 20, 100, 80)) // child
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ float parentHeight = makeParentScrollable ? 120 : 100;
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
+ SetScrollHandoff(layers[1], root);
+ registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ rootApzc = ApzcOf(root);
+ rootApzc->GetScrollMetadata().SetHasScrollgrab(true);
+ }
+
+ void TestFlingAcceleration() {
+ // Jack up the fling acceleration multiplier so we can easily determine
+ // whether acceleration occured.
+ const float kAcceleration = 100.0f;
+ SCOPED_GFX_PREF(APZFlingAccelBaseMultiplier, float, kAcceleration);
+ SCOPED_GFX_PREF(APZFlingAccelMinVelocity, float, 0.0);
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan once, enough to fully scroll the scrollgrab parent and then scroll
+ // and fling the child.
+ Pan(manager, 70, 40);
+
+ // Give the fling animation a chance to start.
+ SampleAnimationsOnce();
+
+ float childVelocityAfterFling1 = childApzc->GetVelocityVector().y;
+
+ // Pan again.
+ Pan(manager, 70, 40);
+
+ // Give the fling animation a chance to start.
+ // This time it should be accelerated.
+ SampleAnimationsOnce();
+
+ float childVelocityAfterFling2 = childApzc->GetVelocityVector().y;
+
+ // We should have accelerated once.
+ // The division by 2 is to account for friction.
+ EXPECT_GT(childVelocityAfterFling2,
+ childVelocityAfterFling1 * kAcceleration / 2);
+
+ // We should not have accelerated twice.
+ // The division by 4 is to account for friction.
+ EXPECT_LE(childVelocityAfterFling2,
+ childVelocityAfterFling1 * kAcceleration * kAcceleration / 4);
+ }
+};
+
+// Here we test that if the processing of a touch block is deferred while we
+// wait for content to send a prevent-default message, overscroll is still
+// handed off correctly when the block is processed.
+TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) {
+ // Set up the APZC tree.
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
+
+ // Enable touch-listeners so that we can separate the queueing of input
+ // events from them being processed.
+ childApzc->SetWaitForMainThread();
+
+ // Queue input events for a pan.
+ uint64_t blockId = 0;
+ ApzcPanNoFling(childApzc, 90, 30, &blockId);
+
+ // Allow the pan to be processed.
+ childApzc->ContentReceivedInputBlock(blockId, false);
+ childApzc->ConfirmTarget(blockId);
+
+ // Make sure overscroll was handed off correctly.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+// Here we test that if the layer structure changes in between two input
+// blocks being queued, and the first block is only processed after the second
+// one has been queued, overscroll handoff for the first block follows
+// the original layer structure while overscroll handoff for the second block
+// follows the new layer structure.
+TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
+ // Set up an initial APZC tree.
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
+
+ // Enable touch-listeners so that we can separate the queueing of input
+ // events from them being processed.
+ childApzc->SetWaitForMainThread();
+
+ // Queue input events for a pan.
+ uint64_t blockId = 0;
+ ApzcPanNoFling(childApzc, 90, 30, &blockId);
+
+ // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
+ // between the child and the root.
+ CreateScrollHandoffLayerTree2();
+ RefPtr<Layer> middle = layers[1];
+ childApzc->SetWaitForMainThread();
+ TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
+
+ // Queue input events for another pan.
+ uint64_t secondBlockId = 0;
+ ApzcPanNoFling(childApzc, 30, 90, &secondBlockId);
+
+ // Allow the first pan to be processed.
+ childApzc->ContentReceivedInputBlock(blockId, false);
+ childApzc->ConfirmTarget(blockId);
+
+ // Make sure things have scrolled according to the handoff chain in
+ // place at the time the touch-start of the first pan was queued.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Allow the second pan to be processed.
+ childApzc->ContentReceivedInputBlock(secondBlockId, false);
+ childApzc->ConfirmTarget(secondBlockId);
+
+ // Make sure things have scrolled according to the handoff chain in
+ // place at the time the touch-start of the second pan was queued.
+ EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+// Test that putting a second finger down on an APZC while a down-chain APZC
+// is overscrolled doesn't result in being stuck in overscroll.
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 10, 40, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+// This is almost exactly like StuckInOverscroll_Bug1073250, except the
+// APZC receiving the input events for the first touch block is the child
+// (and thus not the same APZC that overscrolls, which is the parent).
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Lift the finger, triggering an overscroll animation
+ // (but don't allow it to run).
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put the finger down again, interrupting the animation
+ // and entering the TOUCHING state.
+ TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Lift the finger once again.
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateScrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ Pan(manager, 60, 90, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Lift the finger, triggering an overscroll animation
+ // (but don't allow it to run).
+ TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put the finger down again, interrupting the animation
+ // and entering the TOUCHING state.
+ TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+ // Put a second finger down. Since we're in the TOUCHING state,
+ // the "are we panned into overscroll" check will fail and we
+ // will not ignore the second finger, instead entering the
+ // PINCHING state.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as TouchDown(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd();
+ rootApzc->AdvanceAnimationsUntilEnd();
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+// Test that flinging in a direction where one component of the fling goes into
+// overscroll but the other doesn't, results in just the one component being
+// handed off to the parent, while the original APZC continues flinging in the
+// other direction.
+TEST_F(APZScrollHandoffTester, PartialFlingHandoff) {
+ CreateScrollHandoffLayerTree1();
+
+ // Fling up and to the left. The child APZC has room to scroll up, but not
+ // to the left, so the horizontal component of the fling should be handed
+ // off to the parent APZC.
+ Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55));
+
+ RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]);
+
+ // Advance the child's fling animation once to give the partial handoff
+ // a chance to occur.
+ mcc->AdvanceByMillis(10);
+ child->AdvanceAnimations(mcc->Time());
+
+ // Assert that partial handoff has occurred.
+ child->AssertStateIsFling();
+ parent->AssertStateIsFling();
+}
+
+// Here we test that if two flings are happening simultaneously, overscroll
+// is handed off correctly for each.
+TEST_F(APZScrollHandoffTester, SimultaneousFlings) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ // Set up an initial APZC tree.
+ CreateScrollHandoffLayerTree3();
+
+ RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]);
+ RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]);
+ RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]);
+ RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]);
+
+ // Pan on the lower child.
+ Pan(child2, 45, 5);
+
+ // Pan on the upper child.
+ Pan(child1, 95, 55);
+
+ // Check that child1 and child2 are in a FLING state.
+ child1->AssertStateIsFling();
+ child2->AssertStateIsFling();
+
+ // Advance the animations on child1 and child2 until their end.
+ child1->AdvanceAnimationsUntilEnd();
+ child2->AdvanceAnimationsUntilEnd();
+
+ // Check that the flings have been handed off to the parents.
+ child1->AssertStateIsReset();
+ parent1->AssertStateIsFling();
+ child2->AssertStateIsReset();
+ parent2->AssertStateIsFling();
+}
+
+TEST_F(APZScrollHandoffTester, Scrollgrab) {
+ // Set up the layer tree
+ CreateScrollgrabLayerTree();
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to fully scroll the scrollgrab parent (20 px)
+ // and leave some more (another 15 px) for the child.
+ Pan(childApzc, 80, 45);
+
+ // Check that the parent and child have scrolled as much as we expect.
+ EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFling) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ // Set up the layer tree
+ CreateScrollgrabLayerTree();
+
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, not enough to fully scroll the scrollgrab parent.
+ Pan(childApzc, 80, 70);
+
+ // Check that it is the scrollgrab parent that's in a fling, not the child.
+ rootApzc->AssertStateIsFling();
+ childApzc->AssertStateIsReset();
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ CreateScrollgrabLayerTree(true /* make parent scrollable */);
+ TestFlingAcceleration();
+}
+
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) {
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+ CreateScrollgrabLayerTree(false /* do not make parent scrollable */);
+ TestFlingAcceleration();
+}
+
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
+ SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
+
+ CreateScrollHandoffLayerTree1();
+
+ RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to scroll it to its end and have scroll
+ // left to hand off. Since immediate handoff is disallowed, we expect
+ // the leftover scroll not to be handed off.
+ Pan(childApzc, 60, 5);
+
+ // Verify that the parent has not scrolled.
+ EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
+ EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Pan again on the child. This time, since the child was scrolled to
+ // its end when the gesture began, we expect the scroll to be handed off.
+ Pan(childApzc, 60, 50);
+
+ // Verify that the parent scrolled.
+ EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+}
+
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
+ SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
+ SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
+
+ CreateScrollHandoffLayerTree1();
+
+ RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
+ RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
+
+ // Pan on the child, enough to get very close to the end, so that the
+ // subsequent fling reaches the end and has leftover velocity to hand off.
+ Pan(childApzc, 60, 12);
+
+ // Allow the fling to run its course.
+ childApzc->AdvanceAnimationsUntilEnd();
+ parentApzc->AdvanceAnimationsUntilEnd();
+
+ // Verify that the parent has not scrolled.
+ // The first comparison needs to be an ASSERT_NEAR because the fling
+ // computations are such that the final scroll position can be within
+ // COORDINATE_EPSILON of the end rather than right at the end.
+ ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetScrollOffset().y, COORDINATE_EPSILON);
+ EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
+
+ // Pan again on the child. This time, since the child was scrolled to
+ // its end when the gesture began, we expect the scroll to be handed off.
+ Pan(childApzc, 60, 50);
+
+ // Allow the fling to run its course. The fling should also be handed off.
+ childApzc->AdvanceAnimationsUntilEnd();
+ parentApzc->AdvanceAnimationsUntilEnd();
+
+ // Verify that the parent scrolled from the fling.
+ EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10);
+}
diff --git a/gfx/layers/apz/test/gtest/TestSnapping.cpp b/gfx/layers/apz/test/gtest/TestSnapping.cpp
new file mode 100644
index 000000000..95c21ca44
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestSnapping.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+class APZCSnappingTester : public APZCTreeManagerTester
+{
+};
+
+TEST_F(APZCSnappingTester, Bug1265510)
+{
+ const char* layerTreeSyntax = "c(t)";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 100, 100, 100))
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
+ SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200));
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
+ SetScrollHandoff(layers[1], root);
+
+ ScrollSnapInfo snap;
+ snap.mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY;
+ snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel());
+
+ ScrollMetadata metadata = root->GetScrollMetadata(0);
+ metadata.SetSnapInfo(ScrollSnapInfo(snap));
+ root->SetScrollMetadata(metadata);
+
+ UniquePtr<ScopedLayerTreeRegistration> registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* outer = ApzcOf(layers[0]);
+ TestAsyncPanZoomController* inner = ApzcOf(layers[1]);
+
+ // Position the mouse near the bottom of the outer frame and scroll by 60px.
+ // (6 lines of 10px each). APZC will actually scroll to y=100 because of the
+ // mandatory snap coordinate there.
+ TimeStamp now = mcc->Time();
+ SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), now);
+ // Advance in 5ms increments until we've scrolled by 70px. At this point, the
+ // closest snap point is y=100, and the inner frame should be under the mouse
+ // cursor.
+ while (outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y < 70) {
+ mcc->AdvanceByMillis(5);
+ outer->AdvanceAnimations(mcc->Time());
+ }
+ // Now do another wheel in a new transaction. This should start scrolling the
+ // inner frame; we verify that it does by checking the inner scroll position.
+ TimeStamp newTransactionTime = now + TimeDuration::FromMilliseconds(gfxPrefs::MouseWheelTransactionTimeoutMs() + 100);
+ SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), newTransactionTime);
+ inner->AdvanceAnimationsUntilEnd();
+ EXPECT_LT(0.0f, inner->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
+
+ // However, the outer frame should also continue to the snap point, otherwise
+ // it is demonstrating incorrect behaviour by violating the mandatory snapping.
+ outer->AdvanceAnimationsUntilEnd();
+ EXPECT_EQ(100.0f, outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
+}
diff --git a/gfx/layers/apz/test/gtest/TestTreeManager.cpp b/gfx/layers/apz/test/gtest/TestTreeManager.cpp
new file mode 100644
index 000000000..80a7d0579
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestTreeManager.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+
+TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
+ CreateSimpleMultiLayerTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ // both layers have the same scrollId
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ TestAsyncPanZoomController* nullAPZC = nullptr;
+ // so they should have the same APZC
+ EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
+ EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
+ EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+
+ // Change the scrollId of layers[1], and verify the APZC changes
+ SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[2]));
+
+ // Change the scrollId of layers[2] to match that of layers[1], ensure we get the same
+ // APZC for both again
+ SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
+}
+
+TEST_F(APZCTreeManagerTester, Bug1068268) {
+ CreatePotentiallyLeakingTree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+ RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
+ RefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
+ RefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
+
+ EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
+ EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
+
+ EXPECT_EQ(node2->GetFirstChild(), node2->GetLastChild());
+ EXPECT_EQ(ApzcOf(layers[3]), node2->GetLastChild()->GetApzc());
+ EXPECT_EQ(node5->GetFirstChild(), node5->GetLastChild());
+ EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->GetApzc());
+ EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
+ EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
+}
+
+TEST_F(APZCTreeManagerTester, Bug1194876) {
+ CreateBug1194876Tree();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ uint64_t blockId;
+ nsTArray<ScrollableLayerGuid> targets;
+
+ // First touch goes down, APZCTM will hit layers[1] because it is on top of
+ // layers[0], but we tell it the real target APZC is layers[0].
+ MultiTouchInput mti;
+ mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
+ mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(25, 50), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(mti, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, false);
+ targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+ manager->SetTargetAPZC(blockId, targets);
+
+ // Around here, the above touch will get processed by ApzcOf(layers[0])
+
+ // Second touch goes down (first touch remains down), APZCTM will again hit
+ // layers[1]. Again we tell it both touches landed on layers[0], but because
+ // layers[1] is the RCD layer, it will end up being the multitouch target.
+ mti.mTouches.AppendElement(SingleTouchData(1, ParentLayerPoint(75, 50), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(mti, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, false);
+ targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+ manager->SetTargetAPZC(blockId, targets);
+
+ // Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
+ // We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
+ // otherwise it will do things like dispatch spurious long-tap events.
+
+ EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
+}
+
+TEST_F(APZCTreeManagerTester, Bug1198900) {
+ // This is just a test that cancels a wheel event to make sure it doesn't
+ // crash.
+ CreateSimpleDTCScrollingLayer();
+ ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+ manager->UpdateHitTestingTree(0, root, false, 0, 0);
+
+ ScreenPoint origin(100, 50);
+ ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+ ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+ origin, 0, 10, false);
+ uint64_t blockId;
+ manager->ReceiveInputEvent(swi, nullptr, &blockId);
+ manager->ContentReceivedInputBlock(blockId, /* preventDefault= */ true);
+}
+
diff --git a/gfx/layers/apz/test/gtest/moz.build b/gfx/layers/apz/test/gtest/moz.build
new file mode 100644
index 000000000..f3dc8c3dc
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'TestBasic.cpp',
+ 'TestEventRegions.cpp',
+ 'TestGestureDetector.cpp',
+ 'TestHitTesting.cpp',
+ 'TestInputQueue.cpp',
+ 'TestPanning.cpp',
+ 'TestPinching.cpp',
+ 'TestScrollHandoff.cpp',
+ 'TestSnapping.cpp',
+ 'TestTreeManager.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/gfx/2d',
+ '/gfx/layers',
+ '/gfx/tests/gtest' # for TestLayers.h, which is shared with the gfx gtests
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
new file mode 100644
index 000000000..7f820a936
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
@@ -0,0 +1,261 @@
+// Utilities for synthesizing of native events.
+
+function getPlatform() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ return "windows";
+ }
+ if (navigator.platform.indexOf("Mac") == 0) {
+ return "mac";
+ }
+ // Check for Android before Linux
+ if (navigator.appVersion.indexOf("Android") >= 0) {
+ return "android"
+ }
+ if (navigator.platform.indexOf("Linux") == 0) {
+ return "linux";
+ }
+ return "unknown";
+}
+
+function nativeVerticalWheelEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 0x020A; // WM_MOUSEWHEEL
+ case "mac": return 0; // value is unused, can be anything
+ case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
+ }
+ throw "Native wheel events not supported on platform " + getPlatform();
+}
+
+function nativeHorizontalWheelEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 0x020E; // WM_MOUSEHWHEEL
+ case "mac": return 0; // value is unused, can be anything
+ case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
+ }
+ throw "Native wheel events not supported on platform " + getPlatform();
+}
+
+// Given a pixel scrolling delta, converts it to the platform's native units.
+function nativeScrollUnits(aElement, aDimen) {
+ switch (getPlatform()) {
+ case "linux": {
+ // GTK deltas are treated as line height divided by 3 by gecko.
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"];
+ return aDimen / (parseInt(lineHeight) * 3);
+ }
+ }
+ return aDimen;
+}
+
+function nativeMouseDownEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 2; // MOUSEEVENTF_LEFTDOWN
+ case "mac": return 1; // NSLeftMouseDown
+ case "linux": return 4; // GDK_BUTTON_PRESS
+ case "android": return 5; // ACTION_POINTER_DOWN
+ }
+ throw "Native mouse-down events not supported on platform " + getPlatform();
+}
+
+function nativeMouseMoveEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 1; // MOUSEEVENTF_MOVE
+ case "mac": return 5; // NSMouseMoved
+ case "linux": return 3; // GDK_MOTION_NOTIFY
+ case "android": return 7; // ACTION_HOVER_MOVE
+ }
+ throw "Native mouse-move events not supported on platform " + getPlatform();
+}
+
+function nativeMouseUpEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 4; // MOUSEEVENTF_LEFTUP
+ case "mac": return 2; // NSLeftMouseUp
+ case "linux": return 7; // GDK_BUTTON_RELEASE
+ case "android": return 6; // ACTION_POINTER_UP
+ }
+ throw "Native mouse-up events not supported on platform " + getPlatform();
+}
+
+// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
+// to device pixels relative to the screen.
+function coordinatesRelativeToScreen(aX, aY, aElement) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var scale = targetWindow.devicePixelRatio;
+ var rect = aElement.getBoundingClientRect();
+ return {
+ x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale,
+ y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale
+ };
+}
+
+// Get the bounding box of aElement, and return it in device pixels
+// relative to the screen.
+function rectRelativeToScreen(aElement) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var scale = targetWindow.devicePixelRatio;
+ var rect = aElement.getBoundingClientRect();
+ return {
+ x: (targetWindow.mozInnerScreenX + rect.left) * scale,
+ y: (targetWindow.mozInnerScreenY + rect.top) * scale,
+ w: (rect.width * scale),
+ h: (rect.height * scale)
+ };
+}
+
+// Synthesizes a native mousewheel event and returns immediately. This does not
+// guarantee anything; you probably want to use one of the other functions below
+// which actually wait for results.
+// aX and aY are relative to the top-left of |aElement|'s containing window.
+// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined
+// if not needed.
+function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ if (aDeltaX && aDeltaY) {
+ throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
+ }
+ aDeltaX = nativeScrollUnits(aElement, aDeltaX);
+ aDeltaY = nativeScrollUnits(aElement, aDeltaY);
+ var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
+ return true;
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// request has been successfully made to the OS. This does not necessarily
+// guarantee that the OS generates the event we requested. See
+// synthesizeNativeWheel for details on the parameters.
+function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aCallback && aTopic == "mousescrollevent") {
+ setTimeout(aCallback, 0);
+ }
+ }
+ };
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer);
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// wheel event is dispatched to |aElement|'s containing window. If the event
+// targets content in a subdocument, |aElement| should be inside the
+// subdocument. See synthesizeNativeWheel for details on the other parameters.
+function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ targetWindow.addEventListener("wheel", function wheelWaiter(e) {
+ targetWindow.removeEventListener("wheel", wheelWaiter);
+ setTimeout(aCallback, 0);
+ });
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
+}
+
+// Synthesizes a native mousewheel event and invokes the callback once the
+// first resulting scroll event is dispatched to |aElement|'s containing window.
+// If the event targets content in a subdocument, |aElement| should be inside
+// the subdocument. See synthesizeNativeWheel for details on the other
+// parameters.
+function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var useCapture = true; // scroll events don't always bubble
+ targetWindow.addEventListener("scroll", function scrollWaiter(e) {
+ targetWindow.removeEventListener("scroll", scrollWaiter, useCapture);
+ setTimeout(aCallback, 0);
+ }, useCapture);
+ return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
+}
+
+// Synthesizes a native mouse move event and returns immediately.
+// aX and aY are relative to the top-left of |aElement|'s containing window.
+function synthesizeNativeMouseMove(aElement, aX, aY) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
+ return true;
+}
+
+// Synthesizes a native mouse move event and invokes the callback once the
+// mouse move event is dispatched to |aElement|'s containing window. If the event
+// targets content in a subdocument, |aElement| should be inside the
+// subdocument. See synthesizeNativeMouseMove for details on the other
+// parameters.
+function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
+ targetWindow.removeEventListener("mousemove", mousemoveWaiter);
+ setTimeout(aCallback, 0);
+ });
+ return synthesizeNativeMouseMove(aElement, aX, aY);
+}
+
+// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
+// relative to the top-left of |aElement|'s bounding rect.
+function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
+ return true;
+}
+
+// A handy constant when synthesizing native touch drag events with the pref
+// "apz.touch_start_tolerance" set to 0. In this case, the first touchmove with
+// a nonzero pixel movement is consumed by the APZ to transition from the
+// "touching" state to the "panning" state, so calls to synthesizeNativeTouchDrag
+// should add an extra pixel pixel for this purpose. The TOUCH_SLOP provides
+// a constant that can be used for this purpose. Note that if the touch start
+// tolerance is set to something higher, the touch slop amount used must be
+// correspondingly increased so as to be higher than the tolerance.
+const TOUCH_SLOP = 1;
+function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
+ synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY));
+ for (var i = 1; i < steps; i++) {
+ var dx = i * (aDeltaX / steps);
+ var dy = i * (aDeltaY / steps);
+ synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ }
+ synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
+ return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId);
+}
+
+function synthesizeNativeTap(aElement, aX, aY, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
+ return true;
+}
+
+function synthesizeNativeMouseEvent(aElement, aX, aY, aType, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, aType, 0, aElement, aObserver);
+ return true;
+}
+
+function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
+ var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+ var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseDownEventMsg(), 0, aElement, function() {
+ utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseUpEventMsg(), 0, aElement, aObserver);
+ });
+ return true;
+}
+
+// Move the mouse to (dx, dy) relative to |element|, and scroll the wheel
+// at that location.
+// Moving the mouse is necessary to avoid wheel events from two consecutive
+// moveMouseAndScrollWheelOver() calls on different elements being incorrectly
+// considered as part of the same wheel transaction.
+// We also wait for the mouse move event to be processed before sending the
+// wheel event, otherwise there is a chance they might get reordered, and
+// we have the transaction problem again.
+function moveMouseAndScrollWheelOver(element, dx, dy, testDriver, waitForScroll = true) {
+ return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
+ if (waitForScroll) {
+ synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
+ } else {
+ synthesizeNativeWheelAndWaitForWheelEvent(element, dx, dy, 0, -10, testDriver);
+ }
+ });
+}
diff --git a/gfx/layers/apz/test/mochitest/apz_test_utils.js b/gfx/layers/apz/test/mochitest/apz_test_utils.js
new file mode 100644
index 000000000..c97738434
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -0,0 +1,403 @@
+// Utilities for writing APZ tests using the framework added in bug 961289
+
+// ----------------------------------------------------------------------
+// Functions that convert the APZ test data into a more usable form.
+// Every place we have a WebIDL sequence whose elements are dictionaries
+// with two elements, a key, and a value, we convert this into a JS
+// object with a property for each key/value pair. (This is the structure
+// we really want, but we can't express in directly in WebIDL.)
+// ----------------------------------------------------------------------
+
+function convertEntries(entries) {
+ var result = {};
+ for (var i = 0; i < entries.length; ++i) {
+ result[entries[i].key] = entries[i].value;
+ }
+ return result;
+}
+
+function getPropertyAsRect(scrollFrames, scrollId, prop) {
+ SimpleTest.ok(scrollId in scrollFrames,
+ 'expected scroll frame data for scroll id ' + scrollId);
+ var scrollFrameData = scrollFrames[scrollId];
+ SimpleTest.ok('displayport' in scrollFrameData,
+ 'expected a ' + prop + ' for scroll id ' + scrollId);
+ var value = scrollFrameData[prop];
+ var pieces = value.replace(/[()\s]+/g, '').split(',');
+ SimpleTest.is(pieces.length, 4, "expected string of form (x,y,w,h)");
+ return { x: parseInt(pieces[0]),
+ y: parseInt(pieces[1]),
+ w: parseInt(pieces[2]),
+ h: parseInt(pieces[3]) };
+}
+
+function convertScrollFrameData(scrollFrames) {
+ var result = {};
+ for (var i = 0; i < scrollFrames.length; ++i) {
+ result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
+ }
+ return result;
+}
+
+function convertBuckets(buckets) {
+ var result = {};
+ for (var i = 0; i < buckets.length; ++i) {
+ result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
+ }
+ return result;
+}
+
+function convertTestData(testData) {
+ var result = {};
+ result.paints = convertBuckets(testData.paints);
+ result.repaintRequests = convertBuckets(testData.repaintRequests);
+ return result;
+}
+
+// Given APZ test data for a single paint on the compositor side,
+// reconstruct the APZC tree structure from the 'parentScrollId'
+// entries that were logged. More specifically, the subset of the
+// APZC tree structure corresponding to the layer subtree for the
+// content process that triggered the paint, is reconstructed (as
+// the APZ test data only contains information abot this subtree).
+function buildApzcTree(paint) {
+ // The APZC tree can potentially have multiple root nodes,
+ // so we invent a node that is the parent of all roots.
+ // This 'root' does not correspond to an APZC.
+ var root = {scrollId: -1, children: []};
+ for (var scrollId in paint) {
+ paint[scrollId].children = [];
+ paint[scrollId].scrollId = scrollId;
+ }
+ for (var scrollId in paint) {
+ var parentNode = null;
+ if ("hasNoParentWithSameLayersId" in paint[scrollId]) {
+ parentNode = root;
+ } else if ("parentScrollId" in paint[scrollId]) {
+ parentNode = paint[paint[scrollId].parentScrollId];
+ }
+ parentNode.children.push(paint[scrollId]);
+ }
+ return root;
+}
+
+// Given an APZC tree produced by buildApzcTree, return the RCD node in
+// the tree, or null if there was none.
+function findRcdNode(apzcTree) {
+ if (!!apzcTree.isRootContent) { // isRootContent will be undefined or "1"
+ return apzcTree;
+ }
+ for (var i = 0; i < apzcTree.children.length; i++) {
+ var rcd = findRcdNode(apzcTree.children[i]);
+ if (rcd != null) {
+ return rcd;
+ }
+ }
+ return null;
+}
+
+// Return whether an element whose id includes |elementId| has been layerized.
+// Assumes |elementId| will be present in the content description for the
+// element, and not in the content descriptions of other elements.
+function isLayerized(elementId) {
+ var contentTestData = SpecialPowers.getDOMWindowUtils(window).getContentAPZTestData();
+ ok(contentTestData.paints.length > 0, "expected at least one paint");
+ var seqno = contentTestData.paints[contentTestData.paints.length - 1].sequenceNumber;
+ contentTestData = convertTestData(contentTestData);
+ var paint = contentTestData.paints[seqno];
+ for (var scrollId in paint) {
+ if ("contentDescription" in paint[scrollId]) {
+ if (paint[scrollId]["contentDescription"].includes(elementId)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function flushApzRepaints(aCallback, aWindow = window) {
+ if (!aCallback) {
+ throw "A callback must be provided!";
+ }
+ var repaintDone = function() {
+ SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
+ setTimeout(aCallback, 0);
+ };
+ SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
+ if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) {
+ dump("Flushed APZ repaints, waiting for callback...\n");
+ } else {
+ dump("Flushing APZ repaints was a no-op, triggering callback directly...\n");
+ repaintDone();
+ }
+}
+
+// Flush repaints, APZ pending repaints, and any repaints resulting from that
+// flush. This is particularly useful if the test needs to reach some sort of
+// "idle" state in terms of repaints. Usually just doing waitForAllPaints
+// followed by flushApzRepaints is sufficient to flush all APZ state back to
+// the main thread, but it can leave a paint scheduled which will get triggered
+// at some later time. For tests that specifically test for painting at
+// specific times, this method is the way to go. Even if in doubt, this is the
+// preferred method as the extra step is "safe" and shouldn't interfere with
+// most tests.
+function waitForApzFlushedRepaints(aCallback) {
+ // First flush the main-thread paints and send transactions to the APZ
+ waitForAllPaints(function() {
+ // Then flush the APZ to make sure any repaint requests have been sent
+ // back to the main thread
+ flushApzRepaints(function() {
+ // Then flush the main-thread again to process the repaint requests.
+ // Once this is done, we should be in a stable state with nothing
+ // pending, so we can trigger the callback.
+ waitForAllPaints(aCallback);
+ });
+ });
+}
+
+// This function takes a set of subtests to run one at a time in new top-level
+// windows, and returns a Promise that is resolved once all the subtests are
+// done running.
+//
+// The aSubtests array is an array of objects with the following keys:
+// file: required, the filename of the subtest.
+// prefs: optional, an array of arrays containing key-value prefs to set.
+// dp_suppression: optional, a boolean on whether or not to respect displayport
+// suppression during the test.
+// onload: optional, a function that will be registered as a load event listener
+// for the child window that will hold the subtest. the function will be
+// passed exactly one argument, which will be the child window.
+// An example of an array is:
+// aSubtests = [
+// { 'file': 'test_file_name.html' },
+// { 'file': 'test_file_2.html', 'prefs': [['pref.name', true], ['other.pref', 1000]], 'dp_suppression': false }
+// { 'file': 'file_3.html', 'onload': function(w) { w.subtestDone(); } }
+// ];
+//
+// Each subtest should call the subtestDone() function when it is done, to
+// indicate that the window should be torn down and the next text should run.
+// The subtestDone() function is injected into the subtest's window by this
+// function prior to loading the subtest. For convenience, the |is| and |ok|
+// functions provided by SimpleTest are also mapped into the subtest's window.
+// For other things from the parent, the subtest can use window.opener.<whatever>
+// to access objects.
+function runSubtestsSeriallyInFreshWindows(aSubtests) {
+ return new Promise(function(resolve, reject) {
+ var testIndex = -1;
+ var w = null;
+
+ function advanceSubtestExecution() {
+ var test = aSubtests[testIndex];
+ if (w) {
+ if (typeof test.dp_suppression != 'undefined') {
+ // We modified the suppression when starting the test, so now undo that.
+ SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(!test.dp_suppression);
+ }
+ if (test.prefs) {
+ // We pushed some prefs for this test, pop them, and re-invoke
+ // advanceSubtestExecution() after that's been processed
+ SpecialPowers.popPrefEnv(function() {
+ w.close();
+ w = null;
+ advanceSubtestExecution();
+ });
+ return;
+ }
+
+ w.close();
+ }
+
+ testIndex++;
+ if (testIndex >= aSubtests.length) {
+ resolve();
+ return;
+ }
+
+ test = aSubtests[testIndex];
+ if (typeof test.dp_suppression != 'undefined') {
+ // Normally during a test, the displayport will get suppressed during page
+ // load, and unsuppressed at a non-deterministic time during the test. The
+ // unsuppression can trigger a repaint which interferes with the test, so
+ // to avoid that we can force the displayport to be unsuppressed for the
+ // entire test which is more deterministic.
+ SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(test.dp_suppression);
+ }
+
+ function spawnTest(aFile) {
+ w = window.open('', "_blank");
+ w.subtestDone = advanceSubtestExecution;
+ w.SimpleTest = SimpleTest;
+ w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
+ w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
+ if (test.onload) {
+ w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
+ }
+ w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
+ return w;
+ }
+
+ if (test.prefs) {
+ // Got some prefs for this subtest, push them
+ SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
+ w = spawnTest(test.file);
+ });
+ } else {
+ w = spawnTest(test.file);
+ }
+ }
+
+ advanceSubtestExecution();
+ });
+}
+
+function pushPrefs(prefs) {
+ return SpecialPowers.pushPrefEnv({'set': prefs});
+}
+
+function waitUntilApzStable() {
+ return new Promise(function(resolve, reject) {
+ SimpleTest.waitForFocus(function() {
+ waitForAllPaints(function() {
+ flushApzRepaints(resolve);
+ });
+ }, window);
+ });
+}
+
+function isApzEnabled() {
+ var enabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
+ if (!enabled) {
+ // All tests are required to have at least one assertion. Since APZ is
+ // disabled, and the main test is presumably not going to run, we stick in
+ // a dummy assertion here to keep the test passing.
+ SimpleTest.ok(true, "APZ is not enabled; this test will be skipped");
+ }
+ return enabled;
+}
+
+// Despite what this function name says, this does not *directly* run the
+// provided continuation testFunction. Instead, it returns a function that
+// can be used to run the continuation. The extra level of indirection allows
+// it to be more easily added to a promise chain, like so:
+// waitUntilApzStable().then(runContinuation(myTest));
+//
+// If you want to run the continuation directly, outside of a promise chain,
+// you can invoke the return value of this function, like so:
+// runContinuation(myTest)();
+function runContinuation(testFunction) {
+ // We need to wrap this in an extra function, so that the call site can
+ // be more readable without running the promise too early. In other words,
+ // if we didn't have this extra function, the promise would start running
+ // during construction of the promise chain, concurrently with the first
+ // promise in the chain.
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var testContinuation = null;
+
+ function driveTest() {
+ if (!testContinuation) {
+ testContinuation = testFunction(driveTest);
+ }
+ var ret = testContinuation.next();
+ if (ret.done) {
+ resolve();
+ }
+ }
+
+ driveTest();
+ });
+ };
+}
+
+// Take a snapshot of the given rect, *including compositor transforms* (i.e.
+// includes async scroll transforms applied by APZ). If you don't need the
+// compositor transforms, you can probably get away with using
+// SpecialPowers.snapshotWindowWithOptions or one of the friendlier wrappers.
+// The rect provided is expected to be relative to the screen, for example as
+// returned by rectRelativeToScreen in apz_test_native_event_utils.js.
+// Example usage:
+// var snapshot = getSnapshot(rectRelativeToScreen(myDiv));
+// which will take a snapshot of the 'myDiv' element. Note that if part of the
+// element is obscured by other things on top, the snapshot will include those
+// things. If it is clipped by a scroll container, the snapshot will include
+// that area anyway, so you will probably get parts of the scroll container in
+// the snapshot. If the rect extends outside the browser window then the
+// results are undefined.
+// The snapshot is returned in the form of a data URL.
+function getSnapshot(rect) {
+ function parentProcessSnapshot() {
+ addMessageListener('snapshot', function(rect) {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+
+ // reposition the rect relative to the top-level browser window
+ rect = JSON.parse(rect);
+ rect.x -= topWin.mozInnerScreenX;
+ rect.y -= topWin.mozInnerScreenY;
+
+ // take the snapshot
+ var canvas = topWin.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ canvas.width = rect.w;
+ canvas.height = rect.h;
+ var ctx = canvas.getContext("2d");
+ ctx.drawWindow(topWin, rect.x, rect.y, rect.w, rect.h, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_CARET);
+ return canvas.toDataURL();
+ });
+ }
+
+ if (typeof getSnapshot.chromeHelper == 'undefined') {
+ // This is the first time getSnapshot is being called; do initialization
+ getSnapshot.chromeHelper = SpecialPowers.loadChromeScript(parentProcessSnapshot);
+ SimpleTest.registerCleanupFunction(function() { getSnapshot.chromeHelper.destroy() });
+ }
+
+ return getSnapshot.chromeHelper.sendSyncMessage('snapshot', JSON.stringify(rect)).toString();
+}
+
+// Takes the document's query string and parses it, assuming the query string
+// is composed of key-value pairs where the value is in JSON format. The object
+// returned contains the various values indexed by their respective keys. In
+// case of duplicate keys, the last value be used.
+// Examples:
+// ?key="value"&key2=false&key3=500
+// produces { "key": "value", "key2": false, "key3": 500 }
+// ?key={"x":0,"y":50}&key2=[1,2,true]
+// produces { "key": { "x": 0, "y": 0 }, "key2": [1, 2, true] }
+function getQueryArgs() {
+ var args = {};
+ if (location.search.length > 0) {
+ var params = location.search.substr(1).split('&');
+ for (var p of params) {
+ var [k, v] = p.split('=');
+ args[k] = JSON.parse(v);
+ }
+ }
+ return args;
+}
+
+// Return a function that returns a promise to create a script element with the
+// given URI and append it to the head of the document in the given window.
+// As with runContinuation(), the extra function wrapper is for convenience
+// at the call site, so that this can be chained with other promises:
+// waitUntilApzStable().then(injectScript('foo'))
+// .then(injectScript('bar'));
+// If you want to do the injection right away, run the function returned by
+// this function:
+// injectScript('foo')();
+function injectScript(aScript, aWindow = window) {
+ return function() {
+ return new Promise(function(resolve, reject) {
+ var e = aWindow.document.createElement('script');
+ e.type = 'text/javascript';
+ e.onload = function() {
+ resolve();
+ };
+ e.onerror = function() {
+ dump('Script [' + aScript + '] errored out\n');
+ reject();
+ };
+ e.src = aScript;
+ aWindow.document.getElementsByTagName('head')[0].appendChild(e);
+ });
+ };
+}
diff --git a/gfx/layers/apz/test/mochitest/chrome.ini b/gfx/layers/apz/test/mochitest/chrome.ini
new file mode 100644
index 000000000..d52da5928
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/chrome.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ apz_test_native_event_utils.js
+tags = apz-chrome
+
+[test_smoothness.html]
+# hardware vsync only on win/mac
+# e10s only since APZ is only enabled on e10s
+skip-if = debug || (os != 'mac' && os != 'win') || !e10s
diff --git a/gfx/layers/apz/test/mochitest/helper_basic_pan.html b/gfx/layers/apz/test/mochitest/helper_basic_pan.html
new file mode 100644
index 000000000..c33258da8
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_basic_pan.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollPage() {
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(document.body, 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ is(window.scrollY, 50, "check that the window scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollPage);
+
+ </script>
+</head>
+<body>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the page scrollable.
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1151663.html b/gfx/layers/apz/test/mochitest/helper_bug1151663.html
new file mode 100644
index 000000000..ef2fde9a9
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1151663.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1151663, helper page</title>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript">
+
+ // -------------------------------------------------------------------
+ // Infrastructure to get the test assertions to run at the right time.
+ // -------------------------------------------------------------------
+ var SimpleTest = window.opener.SimpleTest;
+
+ window.onload = function() {
+ window.addEventListener("MozAfterPaint", afterPaint, false);
+ };
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ function afterPaint(e) {
+ // If there is another paint pending, wait for it.
+ if (utils.isMozAfterPaintPending) {
+ return;
+ }
+
+ // Once there are no more paints pending, remove the
+ // MozAfterPaint listener and run the test logic.
+ window.removeEventListener("MozAfterPaint", afterPaint, false);
+ testBug1151663();
+ }
+
+ // --------------------------------------------------------------------
+ // The actual logic for testing bug 1151663.
+ //
+ // In this test we have a simple page which is scrollable, with a
+ // scrollable <div> which is also scrollable. We test that the
+ // <div> does not get an initial APZC, since primary scrollable
+ // frame is the page's root scroll frame.
+ // --------------------------------------------------------------------
+
+ function testBug1151663() {
+ // Get the content- and compositor-side test data from nsIDOMWindowUtils.
+ var contentTestData = utils.getContentAPZTestData();
+ var compositorTestData = utils.getCompositorAPZTestData();
+
+ // Get the sequence number of the last paint on the compositor side.
+ // We do this before converting the APZ test data because the conversion
+ // loses the order of the paints.
+ SimpleTest.ok(compositorTestData.paints.length > 0,
+ "expected at least one paint in compositor test data");
+ var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
+ var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
+
+ // Convert the test data into a representation that's easier to navigate.
+ contentTestData = convertTestData(contentTestData);
+ compositorTestData = convertTestData(compositorTestData);
+ var paint = compositorTestData.paints[lastCompositorPaintSeqNo];
+
+ // Reconstruct the APZC tree structure in the last paint.
+ var apzcTree = buildApzcTree(paint);
+
+ // The apzc tree for this page should consist of a single root APZC,
+ // which either is the RCD with no child APZCs (e10s/B2G case) or has a
+ // single child APZC which is for the RCD (fennec case).
+ var rcd = findRcdNode(apzcTree);
+ SimpleTest.ok(rcd != null, "found the RCD node");
+ SimpleTest.is(rcd.children.length, 0, "expected no children on the RCD");
+
+ window.opener.finishTest();
+ }
+ </script>
+</head>
+<body style="height: 500px; overflow: scroll">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
+ <div style="height: 50px; width: 50px; overflow: scroll">
+ <!-- Put enough content into the subframe to make it have a nonzero scroll range -->
+ <div style="height: 100px; width: 50px"></div>
+ </div>
+ <!-- Put enough content into the page to make it have a nonzero scroll range -->
+ <div style="height: 1000px"></div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1162771.html b/gfx/layers/apz/test/mochitest/helper_bug1162771.html
new file mode 100644
index 000000000..18e4a2f05
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1162771.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test for touchend on media elements</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var v = document.getElementById('video');
+ var a = document.getElementById('audio');
+ var d = document.getElementById('div');
+
+ document.body.ontouchstart = function(e) {
+ if (e.target === v || e.target === a || e.target === d) {
+ e.target.style.display = 'none';
+ ok(true, 'Set display to none on #' + e.target.id);
+ } else {
+ ok(false, 'Got unexpected touchstart on ' + e.target);
+ }
+ waitForAllPaints(testDriver);
+ };
+
+ document.body.ontouchend = function(e) {
+ if (e.target === v || e.target === a || e.target === d) {
+ e.target._gotTouchend = true;
+ ok(true, 'Got touchend event on #' + e.target.id);
+ }
+ testDriver();
+ };
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+
+ var pt = coordinatesRelativeToScreen(25, 5, v);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(v._gotTouchend, 'Touchend was received on video element');
+
+ pt = coordinatesRelativeToScreen(25, 5, a);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(a._gotTouchend, 'Touchend was received on audio element');
+
+ pt = coordinatesRelativeToScreen(25, 5, d);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
+ yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
+ ok(d._gotTouchend, 'Touchend was received on div element');
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ * {
+ font-size: 24px;
+ box-sizing: border-box;
+ }
+
+ #video {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left:0;
+ width: 33%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #8a8;
+ }
+
+ #audio {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left:33%;
+ width: 33%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #a88;
+ }
+
+ #div {
+ display:block;
+ position:absolute;
+ top: 100px;
+ left: 66%;
+ width: 34%;
+ height: 100px;
+ border:solid black 1px;
+ background-color: #88a;
+ }
+ </style>
+</head>
+<body>
+ <p>Tap on the colored boxes to hide them.</p>
+ <video id="video"></video>
+ <audio id="audio" controls></audio>
+ <div id="div"></div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1271432.html b/gfx/layers/apz/test/mochitest/helper_bug1271432.html
new file mode 100644
index 000000000..8234b8232
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1271432.html
@@ -0,0 +1,574 @@
+<head>
+ <title>Ensure that the hit region doesn't get unexpectedly expanded</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+<script type="application/javascript">
+function* test(testDriver) {
+ var scroller = document.getElementById('scroller');
+ var scrollerPos = scroller.scrollTop;
+ var dx = 100, dy = 50;
+
+ is(window.scrollY, 0, "Initial page scroll position should be 0");
+ is(scrollerPos, 0, "Initial scroller position should be 0");
+
+ yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
+
+ is(window.scrollY, 0, "Page scroll position should still be 0");
+ ok(scroller.scrollTop > scrollerPos, "Scroller should have scrolled");
+
+ // wait for it to layerize fully and then try again
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ scrollerPos = scroller.scrollTop;
+
+ yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
+ is(window.scrollY, 0, "Page scroll position should still be 0 after layerization");
+ ok(scroller.scrollTop > scrollerPos, "Scroller should have continued scrolling");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+</script>
+<style>
+a#with_after_content {
+ background-color: #F16725;
+ opacity: 0.8;
+ display: inline-block;
+ margin-top: 40px;
+ margin-left: 40px;
+}
+a#with_after_content::after {
+ content: " ";
+ position: absolute;
+ width: 0px;
+ height: 0px;
+ bottom: 40px;
+ z-index: -1;
+ right: 40px;
+ background-color: transparent;
+ border-style: solid;
+ border-width: 15px 15px 15px 0;
+ border-color: #d54e0e transparent transparent transparent;
+ box-shadow: none;
+ box-sizing: border-box;
+}
+div#scroller {
+ overflow-y: scroll;
+ width: 50%;
+ height: 50%;
+}
+</style>
+</head>
+<body>
+<a id="with_after_content">Some text</a>
+
+<div id="scroller">
+Scrolling on the very left edge of this div will work.
+Scrolling on the right side of this div (starting with the left edge of the orange box above) should work, but doesn't.<br/>
+0<br>
+1<br>
+2<br>
+3<br>
+4<br>
+5<br>
+6<br>
+7<br>
+8<br>
+9<br>
+10<br>
+11<br>
+12<br>
+13<br>
+14<br>
+15<br>
+16<br>
+17<br>
+18<br>
+19<br>
+20<br>
+21<br>
+22<br>
+23<br>
+24<br>
+25<br>
+26<br>
+27<br>
+28<br>
+29<br>
+30<br>
+31<br>
+32<br>
+33<br>
+34<br>
+35<br>
+36<br>
+37<br>
+38<br>
+39<br>
+40<br>
+41<br>
+42<br>
+43<br>
+44<br>
+45<br>
+46<br>
+47<br>
+48<br>
+49<br>
+50<br>
+51<br>
+52<br>
+53<br>
+54<br>
+55<br>
+56<br>
+57<br>
+58<br>
+59<br>
+60<br>
+61<br>
+62<br>
+63<br>
+64<br>
+65<br>
+66<br>
+67<br>
+68<br>
+69<br>
+70<br>
+71<br>
+72<br>
+73<br>
+74<br>
+75<br>
+76<br>
+77<br>
+78<br>
+79<br>
+80<br>
+81<br>
+82<br>
+83<br>
+84<br>
+85<br>
+86<br>
+87<br>
+88<br>
+89<br>
+90<br>
+91<br>
+92<br>
+93<br>
+94<br>
+95<br>
+96<br>
+97<br>
+98<br>
+99<br>
+100<br>
+101<br>
+102<br>
+103<br>
+104<br>
+105<br>
+106<br>
+107<br>
+108<br>
+109<br>
+110<br>
+111<br>
+112<br>
+113<br>
+114<br>
+115<br>
+116<br>
+117<br>
+118<br>
+119<br>
+120<br>
+121<br>
+122<br>
+123<br>
+124<br>
+125<br>
+126<br>
+127<br>
+128<br>
+129<br>
+130<br>
+131<br>
+132<br>
+133<br>
+134<br>
+135<br>
+136<br>
+137<br>
+138<br>
+139<br>
+140<br>
+141<br>
+142<br>
+143<br>
+144<br>
+145<br>
+146<br>
+147<br>
+148<br>
+149<br>
+150<br>
+151<br>
+152<br>
+153<br>
+154<br>
+155<br>
+156<br>
+157<br>
+158<br>
+159<br>
+160<br>
+161<br>
+162<br>
+163<br>
+164<br>
+165<br>
+166<br>
+167<br>
+168<br>
+169<br>
+170<br>
+171<br>
+172<br>
+173<br>
+174<br>
+175<br>
+176<br>
+177<br>
+178<br>
+179<br>
+180<br>
+181<br>
+182<br>
+183<br>
+184<br>
+185<br>
+186<br>
+187<br>
+188<br>
+189<br>
+190<br>
+191<br>
+192<br>
+193<br>
+194<br>
+195<br>
+196<br>
+197<br>
+198<br>
+199<br>
+200<br>
+201<br>
+202<br>
+203<br>
+204<br>
+205<br>
+206<br>
+207<br>
+208<br>
+209<br>
+210<br>
+211<br>
+212<br>
+213<br>
+214<br>
+215<br>
+216<br>
+217<br>
+218<br>
+219<br>
+220<br>
+221<br>
+222<br>
+223<br>
+224<br>
+225<br>
+226<br>
+227<br>
+228<br>
+229<br>
+230<br>
+231<br>
+232<br>
+233<br>
+234<br>
+235<br>
+236<br>
+237<br>
+238<br>
+239<br>
+240<br>
+241<br>
+242<br>
+243<br>
+244<br>
+245<br>
+246<br>
+247<br>
+248<br>
+249<br>
+250<br>
+251<br>
+252<br>
+253<br>
+254<br>
+255<br>
+256<br>
+257<br>
+258<br>
+259<br>
+260<br>
+261<br>
+262<br>
+263<br>
+264<br>
+265<br>
+266<br>
+267<br>
+268<br>
+269<br>
+270<br>
+271<br>
+272<br>
+273<br>
+274<br>
+275<br>
+276<br>
+277<br>
+278<br>
+279<br>
+280<br>
+281<br>
+282<br>
+283<br>
+284<br>
+285<br>
+286<br>
+287<br>
+288<br>
+289<br>
+290<br>
+291<br>
+292<br>
+293<br>
+294<br>
+295<br>
+296<br>
+297<br>
+298<br>
+299<br>
+300<br>
+301<br>
+302<br>
+303<br>
+304<br>
+305<br>
+306<br>
+307<br>
+308<br>
+309<br>
+310<br>
+311<br>
+312<br>
+313<br>
+314<br>
+315<br>
+316<br>
+317<br>
+318<br>
+319<br>
+320<br>
+321<br>
+322<br>
+323<br>
+324<br>
+325<br>
+326<br>
+327<br>
+328<br>
+329<br>
+330<br>
+331<br>
+332<br>
+333<br>
+334<br>
+335<br>
+336<br>
+337<br>
+338<br>
+339<br>
+340<br>
+341<br>
+342<br>
+343<br>
+344<br>
+345<br>
+346<br>
+347<br>
+348<br>
+349<br>
+350<br>
+351<br>
+352<br>
+353<br>
+354<br>
+355<br>
+356<br>
+357<br>
+358<br>
+359<br>
+360<br>
+361<br>
+362<br>
+363<br>
+364<br>
+365<br>
+366<br>
+367<br>
+368<br>
+369<br>
+370<br>
+371<br>
+372<br>
+373<br>
+374<br>
+375<br>
+376<br>
+377<br>
+378<br>
+379<br>
+380<br>
+381<br>
+382<br>
+383<br>
+384<br>
+385<br>
+386<br>
+387<br>
+388<br>
+389<br>
+390<br>
+391<br>
+392<br>
+393<br>
+394<br>
+395<br>
+396<br>
+397<br>
+398<br>
+399<br>
+400<br>
+401<br>
+402<br>
+403<br>
+404<br>
+405<br>
+406<br>
+407<br>
+408<br>
+409<br>
+410<br>
+411<br>
+412<br>
+413<br>
+414<br>
+415<br>
+416<br>
+417<br>
+418<br>
+419<br>
+420<br>
+421<br>
+422<br>
+423<br>
+424<br>
+425<br>
+426<br>
+427<br>
+428<br>
+429<br>
+430<br>
+431<br>
+432<br>
+433<br>
+434<br>
+435<br>
+436<br>
+437<br>
+438<br>
+439<br>
+440<br>
+441<br>
+442<br>
+443<br>
+444<br>
+445<br>
+446<br>
+447<br>
+448<br>
+449<br>
+450<br>
+451<br>
+452<br>
+453<br>
+454<br>
+455<br>
+456<br>
+457<br>
+458<br>
+459<br>
+460<br>
+461<br>
+462<br>
+463<br>
+464<br>
+465<br>
+466<br>
+467<br>
+468<br>
+469<br>
+470<br>
+471<br>
+472<br>
+473<br>
+474<br>
+475<br>
+476<br>
+477<br>
+478<br>
+479<br>
+480<br>
+481<br>
+482<br>
+483<br>
+484<br>
+485<br>
+486<br>
+487<br>
+488<br>
+489<br>
+490<br>
+491<br>
+492<br>
+493<br>
+494<br>
+495<br>
+496<br>
+497<br>
+498<br>
+499<br>
+</div>
+<div style="height: 1000px">this div makes the page scrollable</div>
+</body>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1280013.html b/gfx/layers/apz/test/mochitest/helper_bug1280013.html
new file mode 100644
index 000000000..0c602901a
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1280013.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html style="overflow:hidden">
+<head>
+ <meta charset="utf-8">
+ <!-- The viewport tag will result in APZ being in a "zoomed-in" state, assuming
+ the device width is less than 980px. -->
+ <meta name="viewport" content="width=980; initial-scale=1.0">
+ <title>Test for bug 1280013</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+function* test(testDriver) {
+ SimpleTest.ok(screen.height > 500, "Screen height must be at least 500 pixels for this test to work");
+
+ // This listener will trigger the test to continue once APZ is done with
+ // processing the scroll.
+ SpecialPowers.Services.obs.addObserver(testDriver, "APZ:TransformEnd", false);
+
+ // Scroll down to the iframe. Do it in two drags instead of one in case the
+ // device screen is short
+ yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
+ yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
+ // Now the top of the visible area should be at y=700 of the top-level page,
+ // so if the screen is >= 500px tall, the entire iframe should be visible, at
+ // least vertically.
+
+ // However, because of the overflow:hidden on the root elements, all this
+ // scrolling is happening in APZ and is not reflected in the main-thread
+ // scroll position (it is stored in the callback transform instead). We check
+ // this by checking the scroll offset.
+ yield flushApzRepaints(testDriver);
+ SimpleTest.is(window.scrollY, 0, "Main-thread scroll position is still at 0");
+
+ // Scroll the iframe by 300px. Note that since the main-thread scroll position
+ // is still 0, the subframe's getBoundingClientRect is going to be off by
+ // 700 pixels, so we compensate for that here.
+ var subframe = document.getElementById('subframe');
+ yield synthesizeNativeTouchDrag(subframe, 10, 200 - 700, 0, -(300 + TOUCH_SLOP));
+
+ // Remove the observer, we don't need it any more.
+ SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
+
+ // Flush any pending paints
+ yield flushApzRepaints(testDriver);
+
+ // get the displayport for the subframe
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var contentPaints = utils.getContentAPZTestData().paints;
+ var lastPaint = convertScrollFrameData(contentPaints[contentPaints.length - 1].scrollFrames);
+ var foundIt = 0;
+ for (var scrollId in lastPaint) {
+ if (('contentDescription' in lastPaint[scrollId]) &&
+ (lastPaint[scrollId]['contentDescription'].includes('tall_html'))) {
+ var dp = getPropertyAsRect(lastPaint, scrollId, 'criticalDisplayport');
+ SimpleTest.ok(dp.y <= 0, 'The critical displayport top should be less than or equal to zero to cover the visible part of the subframe; it is ' + dp.y);
+ SimpleTest.ok(dp.y + dp.h >= subframe.clientHeight, 'The critical displayport bottom should be greater than the clientHeight; it is ' + (dp.y + dp.h));
+ foundIt++;
+ }
+ }
+ SimpleTest.is(foundIt, 1, "Found exactly one critical displayport for the subframe we were interested in.");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="overflow:hidden">
+ The iframe below is at (0, 800). Scroll it into view, and then scroll the contents. The content should be fully rendered in high-resolution.
+ <iframe id="subframe" style="position:absolute; left: 0px; top: 800px; width: 600px; height: 350px" src="helper_tall.html"></iframe>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1285070.html b/gfx/layers/apz/test/mochitest/helper_bug1285070.html
new file mode 100644
index 000000000..3a4879034
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1285070.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test pointer events are dispatched once for touch tap</title>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript">
+ function test() {
+ let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
+ "pointerup", "pointerleave", "pointerout"];
+ let pointerEventsCount = {};
+
+ pointerEventsList.forEach((eventName) => {
+ pointerEventsCount[eventName] = 0;
+ document.getElementById('div1').addEventListener(eventName, (event) => {
+ dump("Received event " + event.type + "\n");
+ ++pointerEventsCount[event.type];
+ }, false);
+ });
+
+ document.addEventListener("click", (event) => {
+ is(event.target, document.getElementById('div1'), "Clicked on div (at " + event.clientX + "," + event.clientY + ")");
+ for (var key in pointerEventsCount) {
+ is(pointerEventsCount[key], 1, "Event " + key + " should be generated once");
+ }
+ subtestDone();
+ }, false);
+
+ synthesizeNativeTap(document.getElementById('div1'), 100, 100, () => {
+ dump("Finished synthesizing tap, waiting for div to be clicked...\n");
+ });
+ }
+
+ waitUntilApzStable().then(test);
+
+ </script>
+</head>
+<body>
+ <div id="div1" style="width: 200px; height: 200px; background: black"></div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1299195.html b/gfx/layers/apz/test/mochitest/helper_bug1299195.html
new file mode 100644
index 000000000..8e746749c
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1299195.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test pointer events are dispatched once for touch tap</title>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript">
+ /** Test for Bug 1299195 **/
+ function runTests() {
+ let target0 = document.getElementById("target0");
+ let mouseup_count = 0;
+ let mousedown_count = 0;
+ let pointerup_count = 0;
+ let pointerdown_count = 0;
+
+ target0.addEventListener("mouseup", () => {
+ ++mouseup_count;
+ if (mouseup_count == 2) {
+ is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
+ is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
+ is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
+ is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
+ subtestDone();
+ }
+ });
+ target0.addEventListener("mousedown", () => {
+ ++mousedown_count;
+ });
+ target0.addEventListener("pointerup", () => {
+ ++pointerup_count;
+ });
+ target0.addEventListener("pointerdown", () => {
+ ++pointerdown_count;
+ });
+ synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+ synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+ }
+ waitUntilApzStable().then(runTests);
+ </script>
+</head>
+<body>
+ <div id="target0" style="width: 200px; height: 200px; background: green"></div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug982141.html b/gfx/layers/apz/test/mochitest/helper_bug982141.html
new file mode 100644
index 000000000..5d2f15397
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug982141.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982141
+-->
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="user-scalable=no">
+ <title>Test for Bug 982141, helper page</title>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript">
+
+ // -------------------------------------------------------------------
+ // Infrastructure to get the test assertions to run at the right time.
+ // -------------------------------------------------------------------
+ var SimpleTest = window.opener.SimpleTest;
+
+ window.onload = function() {
+ window.addEventListener("MozAfterPaint", afterPaint, false);
+ };
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ function afterPaint(e) {
+ // If there is another paint pending, wait for it.
+ if (utils.isMozAfterPaintPending) {
+ return;
+ }
+
+ // Once there are no more paints pending, remove the
+ // MozAfterPaint listener and run the test logic.
+ window.removeEventListener("MozAfterPaint", afterPaint, false);
+ testBug982141();
+ }
+
+ // --------------------------------------------------------------------
+ // The actual logic for testing bug 982141.
+ //
+ // In this test we have a simple page with a scrollable <div> which has
+ // enough content to make it scrollable. We test that this <div> got
+ // a displayport.
+ // --------------------------------------------------------------------
+
+ function testBug982141() {
+ // Get the content- and compositor-side test data from nsIDOMWindowUtils.
+ var contentTestData = utils.getContentAPZTestData();
+ var compositorTestData = utils.getCompositorAPZTestData();
+
+ // Get the sequence number of the last paint on the compositor side.
+ // We do this before converting the APZ test data because the conversion
+ // loses the order of the paints.
+ SimpleTest.ok(compositorTestData.paints.length > 0,
+ "expected at least one paint in compositor test data");
+ var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
+ var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
+
+ // Convert the test data into a representation that's easier to navigate.
+ contentTestData = convertTestData(contentTestData);
+ compositorTestData = convertTestData(compositorTestData);
+
+ // Reconstruct the APZC tree structure in the last paint.
+ var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]);
+
+ // The apzc tree for this page should consist of a single child APZC on
+ // the RCD node (the child is for scrollable <div>). Note that in e10s/B2G
+ // cases the RCD will be the root of the tree but on Fennec it will not.
+ var rcd = findRcdNode(apzcTree);
+ SimpleTest.ok(rcd != null, "found the RCD node");
+ SimpleTest.is(rcd.children.length, 1, "expected a single child APZC");
+ var childScrollId = rcd.children[0].scrollId;
+
+ // We should have content-side data for the same paint.
+ SimpleTest.ok(lastCompositorPaintSeqNo in contentTestData.paints,
+ "expected a content paint with sequence number" + lastCompositorPaintSeqNo);
+ var correspondingContentPaint = contentTestData.paints[lastCompositorPaintSeqNo];
+
+ var dp = getPropertyAsRect(correspondingContentPaint, childScrollId, 'displayport');
+ var subframe = document.getElementById('subframe');
+ // The clientWidth and clientHeight may be less than 50 if there are scrollbars showing.
+ // In general they will be (50 - <scrollbarwidth>, 50 - <scrollbarheight>).
+ SimpleTest.ok(subframe.clientWidth > 0, "Expected a non-zero clientWidth, got: " + subframe.clientWidth);
+ SimpleTest.ok(subframe.clientHeight > 0, "Expected a non-zero clientHeight, got: " + subframe.clientHeight);
+ SimpleTest.ok(dp.w >= subframe.clientWidth && dp.h >= subframe.clientHeight,
+ "expected a displayport at least as large as the scrollable element, got " + JSON.stringify(dp));
+
+ window.opener.finishTest();
+ }
+ </script>
+</head>
+<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
+ <!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
+ <div id="subframe" style="height: 50px; width: 50px; overflow: scroll">
+ <div style="width: 100px">
+ Wide content so that the vertical scrollbar for the parent div
+ doesn't eat into the 50px width and reduce the width of the
+ displayport.
+ </div>
+ Line 1<br>
+ Line 2<br>
+ Line 3<br>
+ Line 4<br>
+ Line 5<br>
+ Line 6<br>
+ Line 7<br>
+ Line 8<br>
+ Line 9<br>
+ Line 10<br>
+ Line 11<br>
+ Line 12<br>
+ Line 13<br>
+ Line 14<br>
+ Line 15<br>
+ Line 16<br>
+ Line 17<br>
+ Line 18<br>
+ Line 19<br>
+ Line 20<br>
+ Line 21<br>
+ Line 22<br>
+ Line 23<br>
+ Line 24<br>
+ Line 25<br>
+ Line 26<br>
+ Line 27<br>
+ Line 28<br>
+ Line 29<br>
+ Line 30<br>
+ Line 31<br>
+ Line 32<br>
+ Line 33<br>
+ Line 34<br>
+ Line 35<br>
+ Line 36<br>
+ Line 37<br>
+ Line 38<br>
+ Line 39<br>
+ Line 40<br>
+ Line 41<br>
+ Line 42<br>
+ Line 43<br>
+ Line 44<br>
+ Line 45<br>
+ Line 46<br>
+ Line 40<br>
+ Line 48<br>
+ Line 49<br>
+ Line 50<br>
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_click.html b/gfx/layers/apz/test/mochitest/helper_click.html
new file mode 100644
index 000000000..b74f175fe
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_click.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity mouse-clicking test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* clickButton(testDriver) {
+ document.addEventListener('click', clicked, false);
+
+ if (getQueryArgs()['dtc']) {
+ // force a dispatch-to-content region on the document
+ document.addEventListener('wheel', function() { /* no-op */ }, { passive: false });
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ }
+
+ synthesizeNativeClick(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing click, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable()
+.then(runContinuation(clickButton));
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_div_pan.html b/gfx/layers/apz/test/mochitest/helper_div_pan.html
new file mode 100644
index 000000000..f37be8ba6
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_div_pan.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test for scrollable div</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollOuter() {
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(document.getElementById('outer'), 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ var outerScroll = document.getElementById('outer').scrollTop;
+ is(outerScroll, 50, "check that the div scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollOuter);
+
+ </script>
+</head>
+<body>
+ <div id="outer" style="height: 250px; border: solid 1px black; overflow:scroll">
+ <div style="height: 5000px; background-color: lightblue">
+ This div makes the |outer| div scrollable.
+ </div>
+ </div>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the top-level page scrollable.
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_drag_click.html b/gfx/layers/apz/test/mochitest/helper_drag_click.html
new file mode 100644
index 000000000..cf7117339
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_drag_click.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity mouse-drag click test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ document.addEventListener('click', clicked, false);
+
+ // Ensure the pointer is inside the window
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseMoveEventMsg(), testDriver);
+ // mouse down, move it around, and release it near where it went down. this
+ // should generate a click at the release point
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseDownEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 100, 100, nativeMouseMoveEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 10, 10, nativeMouseMoveEventMsg(), testDriver);
+ yield synthesizeNativeMouseEvent(document.getElementById('b'), 8, 8, nativeMouseUpEventMsg(), testDriver);
+ dump("Finished synthesizing click with a drag in the middle\n");
+}
+
+function clicked(e) {
+ // The mouse down at (5, 5) should not have generated a click, but the up
+ // at (8, 8) should have.
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ is(e.clientX, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().left), 'x-coord of click event looks sane');
+ is(e.clientY, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().top), 'y-coord of click event looks sane');
+ subtestDone();
+}
+
+waitUntilApzStable()
+.then(runContinuation(test));
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_drag_scroll.html b/gfx/layers/apz/test/mochitest/helper_drag_scroll.html
new file mode 100644
index 000000000..3c06a5b7e
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_drag_scroll.html
@@ -0,0 +1,603 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Dragging the mouse on a content-implemented scrollbar</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <style>
+ body {
+ background: linear-gradient(135deg, red, blue);
+ }
+ #scrollbar {
+ position:fixed;
+ top: 0;
+ right: 10px;
+ height: 100%;
+ width: 150px;
+ background-color: gray;
+ }
+ </style>
+ <script type="text/javascript">
+var bar = null;
+var mouseDown = false;
+
+function moveTo(mouseY, testDriver) {
+ var fraction = (mouseY - bar.getBoundingClientRect().top) / bar.getBoundingClientRect().height;
+ fraction = Math.max(0, fraction);
+ fraction = Math.min(1, fraction);
+ var oldScrollPos = document.scrollingElement.scrollTop;
+ var newScrollPos = fraction * window.scrollMaxY;
+ SimpleTest.ok(newScrollPos > oldScrollPos, "Scroll position strictly increased");
+ // split the scroll in two with a paint in between, just to increase the
+ // complexity of the simulated web content, and to ensure this works as well.
+ document.scrollingElement.scrollTop = (oldScrollPos + newScrollPos) / 2;
+ waitForAllPaints(function() {
+ document.scrollingElement.scrollTop = newScrollPos;
+ testDriver();
+ });
+}
+
+function setupDragging(testDriver) {
+ bar = document.getElementById('scrollbar');
+ mouseDown = false;
+
+ bar.addEventListener('mousedown', function(e) {
+ mouseDown = true;
+ moveTo(e.clientY, testDriver);
+ }, true);
+
+ bar.addEventListener('mousemove', function(e) {
+ if (mouseDown) {
+ dump("Got mousemove clientY " + e.clientY + "\n");
+ moveTo(e.clientY, testDriver);
+ e.stopPropagation();
+ }
+ }, true);
+
+ bar.addEventListener('mouseup', function(e) {
+ mouseDown = false;
+ }, true);
+
+ window.addEventListener('mousemove', function(e) {
+ if (mouseDown) {
+ SimpleTest.ok(false, "The mousemove at " + e.clientY + " was not stopped by the bar listener, and is a glitchy event!");
+ setTimeout(testDriver, 0);
+ }
+ }, false);
+}
+
+function* test(testDriver) {
+ setupDragging(testDriver);
+
+ // Move the mouse to the "scrollbar" (the div upon which dragging changes scroll position)
+ yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseMoveEventMsg(), testDriver);
+ // mouse down
+ yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseDownEventMsg());
+ // drag vertically by 400px, in 50px increments
+ yield synthesizeNativeMouseEvent(bar, 10, 60, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 110, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 160, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 210, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 260, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 310, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 360, nativeMouseMoveEventMsg());
+ yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseMoveEventMsg());
+ // and release
+ yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseUpEventMsg(), testDriver);
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body>
+
+<div id="scrollbar">Drag up and down on this bar. The background/scrollbar shouldn't glitch</div>
+This is a tall page<br/>
+1<br/>
+2<br/>
+3<br/>
+4<br/>
+5<br/>
+6<br/>
+7<br/>
+8<br/>
+9<br/>
+10<br/>
+11<br/>
+12<br/>
+13<br/>
+14<br/>
+15<br/>
+16<br/>
+17<br/>
+18<br/>
+19<br/>
+20<br/>
+21<br/>
+22<br/>
+23<br/>
+24<br/>
+25<br/>
+26<br/>
+27<br/>
+28<br/>
+29<br/>
+30<br/>
+31<br/>
+32<br/>
+33<br/>
+34<br/>
+35<br/>
+36<br/>
+37<br/>
+38<br/>
+39<br/>
+40<br/>
+41<br/>
+42<br/>
+43<br/>
+44<br/>
+45<br/>
+46<br/>
+47<br/>
+48<br/>
+49<br/>
+50<br/>
+51<br/>
+52<br/>
+53<br/>
+54<br/>
+55<br/>
+56<br/>
+57<br/>
+58<br/>
+59<br/>
+60<br/>
+61<br/>
+62<br/>
+63<br/>
+64<br/>
+65<br/>
+66<br/>
+67<br/>
+68<br/>
+69<br/>
+70<br/>
+71<br/>
+72<br/>
+73<br/>
+74<br/>
+75<br/>
+76<br/>
+77<br/>
+78<br/>
+79<br/>
+80<br/>
+81<br/>
+82<br/>
+83<br/>
+84<br/>
+85<br/>
+86<br/>
+87<br/>
+88<br/>
+89<br/>
+90<br/>
+91<br/>
+92<br/>
+93<br/>
+94<br/>
+95<br/>
+96<br/>
+97<br/>
+98<br/>
+99<br/>
+100<br/>
+101<br/>
+102<br/>
+103<br/>
+104<br/>
+105<br/>
+106<br/>
+107<br/>
+108<br/>
+109<br/>
+110<br/>
+111<br/>
+112<br/>
+113<br/>
+114<br/>
+115<br/>
+116<br/>
+117<br/>
+118<br/>
+119<br/>
+120<br/>
+121<br/>
+122<br/>
+123<br/>
+124<br/>
+125<br/>
+126<br/>
+127<br/>
+128<br/>
+129<br/>
+130<br/>
+131<br/>
+132<br/>
+133<br/>
+134<br/>
+135<br/>
+136<br/>
+137<br/>
+138<br/>
+139<br/>
+140<br/>
+141<br/>
+142<br/>
+143<br/>
+144<br/>
+145<br/>
+146<br/>
+147<br/>
+148<br/>
+149<br/>
+150<br/>
+151<br/>
+152<br/>
+153<br/>
+154<br/>
+155<br/>
+156<br/>
+157<br/>
+158<br/>
+159<br/>
+160<br/>
+161<br/>
+162<br/>
+163<br/>
+164<br/>
+165<br/>
+166<br/>
+167<br/>
+168<br/>
+169<br/>
+170<br/>
+171<br/>
+172<br/>
+173<br/>
+174<br/>
+175<br/>
+176<br/>
+177<br/>
+178<br/>
+179<br/>
+180<br/>
+181<br/>
+182<br/>
+183<br/>
+184<br/>
+185<br/>
+186<br/>
+187<br/>
+188<br/>
+189<br/>
+190<br/>
+191<br/>
+192<br/>
+193<br/>
+194<br/>
+195<br/>
+196<br/>
+197<br/>
+198<br/>
+199<br/>
+200<br/>
+201<br/>
+202<br/>
+203<br/>
+204<br/>
+205<br/>
+206<br/>
+207<br/>
+208<br/>
+209<br/>
+210<br/>
+211<br/>
+212<br/>
+213<br/>
+214<br/>
+215<br/>
+216<br/>
+217<br/>
+218<br/>
+219<br/>
+220<br/>
+221<br/>
+222<br/>
+223<br/>
+224<br/>
+225<br/>
+226<br/>
+227<br/>
+228<br/>
+229<br/>
+230<br/>
+231<br/>
+232<br/>
+233<br/>
+234<br/>
+235<br/>
+236<br/>
+237<br/>
+238<br/>
+239<br/>
+240<br/>
+241<br/>
+242<br/>
+243<br/>
+244<br/>
+245<br/>
+246<br/>
+247<br/>
+248<br/>
+249<br/>
+250<br/>
+251<br/>
+252<br/>
+253<br/>
+254<br/>
+255<br/>
+256<br/>
+257<br/>
+258<br/>
+259<br/>
+260<br/>
+261<br/>
+262<br/>
+263<br/>
+264<br/>
+265<br/>
+266<br/>
+267<br/>
+268<br/>
+269<br/>
+270<br/>
+271<br/>
+272<br/>
+273<br/>
+274<br/>
+275<br/>
+276<br/>
+277<br/>
+278<br/>
+279<br/>
+280<br/>
+281<br/>
+282<br/>
+283<br/>
+284<br/>
+285<br/>
+286<br/>
+287<br/>
+288<br/>
+289<br/>
+290<br/>
+291<br/>
+292<br/>
+293<br/>
+294<br/>
+295<br/>
+296<br/>
+297<br/>
+298<br/>
+299<br/>
+300<br/>
+301<br/>
+302<br/>
+303<br/>
+304<br/>
+305<br/>
+306<br/>
+307<br/>
+308<br/>
+309<br/>
+310<br/>
+311<br/>
+312<br/>
+313<br/>
+314<br/>
+315<br/>
+316<br/>
+317<br/>
+318<br/>
+319<br/>
+320<br/>
+321<br/>
+322<br/>
+323<br/>
+324<br/>
+325<br/>
+326<br/>
+327<br/>
+328<br/>
+329<br/>
+330<br/>
+331<br/>
+332<br/>
+333<br/>
+334<br/>
+335<br/>
+336<br/>
+337<br/>
+338<br/>
+339<br/>
+340<br/>
+341<br/>
+342<br/>
+343<br/>
+344<br/>
+345<br/>
+346<br/>
+347<br/>
+348<br/>
+349<br/>
+350<br/>
+351<br/>
+352<br/>
+353<br/>
+354<br/>
+355<br/>
+356<br/>
+357<br/>
+358<br/>
+359<br/>
+360<br/>
+361<br/>
+362<br/>
+363<br/>
+364<br/>
+365<br/>
+366<br/>
+367<br/>
+368<br/>
+369<br/>
+370<br/>
+371<br/>
+372<br/>
+373<br/>
+374<br/>
+375<br/>
+376<br/>
+377<br/>
+378<br/>
+379<br/>
+380<br/>
+381<br/>
+382<br/>
+383<br/>
+384<br/>
+385<br/>
+386<br/>
+387<br/>
+388<br/>
+389<br/>
+390<br/>
+391<br/>
+392<br/>
+393<br/>
+394<br/>
+395<br/>
+396<br/>
+397<br/>
+398<br/>
+399<br/>
+400<br/>
+401<br/>
+402<br/>
+403<br/>
+404<br/>
+405<br/>
+406<br/>
+407<br/>
+408<br/>
+409<br/>
+410<br/>
+411<br/>
+412<br/>
+413<br/>
+414<br/>
+415<br/>
+416<br/>
+417<br/>
+418<br/>
+419<br/>
+420<br/>
+421<br/>
+422<br/>
+423<br/>
+424<br/>
+425<br/>
+426<br/>
+427<br/>
+428<br/>
+429<br/>
+430<br/>
+431<br/>
+432<br/>
+433<br/>
+434<br/>
+435<br/>
+436<br/>
+437<br/>
+438<br/>
+439<br/>
+440<br/>
+441<br/>
+442<br/>
+443<br/>
+444<br/>
+445<br/>
+446<br/>
+447<br/>
+448<br/>
+449<br/>
+450<br/>
+451<br/>
+452<br/>
+453<br/>
+454<br/>
+455<br/>
+456<br/>
+457<br/>
+458<br/>
+459<br/>
+460<br/>
+461<br/>
+462<br/>
+463<br/>
+464<br/>
+465<br/>
+466<br/>
+467<br/>
+468<br/>
+469<br/>
+470<br/>
+471<br/>
+472<br/>
+473<br/>
+474<br/>
+475<br/>
+476<br/>
+477<br/>
+478<br/>
+479<br/>
+480<br/>
+481<br/>
+482<br/>
+483<br/>
+484<br/>
+485<br/>
+486<br/>
+487<br/>
+488<br/>
+489<br/>
+490<br/>
+491<br/>
+492<br/>
+493<br/>
+494<br/>
+495<br/>
+496<br/>
+497<br/>
+498<br/>
+499<br/>
+
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_iframe1.html b/gfx/layers/apz/test/mochitest/helper_iframe1.html
new file mode 100644
index 000000000..047da96bd
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_iframe1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<!-- The purpose of the 'id' on the HTML element is to get something
+ identifiable to show up in the root scroll frame's content description,
+ so we can check it for layerization. -->
+<html id="outer3">
+ <head>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ </head>
+ <body>
+ <div id="inner3" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_iframe2.html b/gfx/layers/apz/test/mochitest/helper_iframe2.html
new file mode 100644
index 000000000..fee3883e9
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_iframe2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<!-- The purpose of the 'id' on the HTML element is to get something
+ identifiable to show up in the root scroll frame's content description,
+ so we can check it for layerization. -->
+<html id="outer4">
+ <head>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ </head>
+ <body>
+ <div id="inner4" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_iframe_pan.html b/gfx/layers/apz/test/mochitest/helper_iframe_pan.html
new file mode 100644
index 000000000..47213f33a
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_iframe_pan.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity panning test for scrollable div</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function scrollOuter() {
+ var outer = document.getElementById('outer');
+ var transformEnd = function() {
+ SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
+ dump("Transform complete; flushing repaints...\n");
+ flushApzRepaints(checkScroll, outer.contentWindow);
+ };
+ SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
+
+ synthesizeNativeTouchDrag(outer.contentDocument.body, 10, 100, 0, -(50 + TOUCH_SLOP));
+ dump("Finished native drag, waiting for transform-end observer...\n");
+}
+
+function checkScroll() {
+ var outerScroll = document.getElementById('outer').contentWindow.scrollY;
+ is(outerScroll, 50, "check that the iframe scrolled");
+ subtestDone();
+}
+
+waitUntilApzStable().then(scrollOuter);
+
+ </script>
+</head>
+<body>
+ <iframe id="outer" style="height: 250px; border: solid 1px black" src="data:text/html,<body style='height:5000px'>"></iframe>
+ <div style="height: 5000px; background-color: lightgreen;">
+ This div makes the top-level page scrollable.
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_long_tap.html b/gfx/layers/apz/test/mochitest/helper_long_tap.html
new file mode 100644
index 000000000..604d03d64
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_long_tap.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Ensure we get a touch-cancel after a contextmenu comes up</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function longPressLink() {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
+ dump("Finished synthesizing touch-start, waiting for events...\n");
+ });
+}
+
+var eventsFired = 0;
+function recordEvent(e) {
+ if (getPlatform() == "windows") {
+ // On Windows we get a mouselongtap event once the long-tap has been detected
+ // by APZ, and that's what we use as the trigger to lift the finger. That then
+ // triggers the contextmenu. This matches the platform convention.
+ switch (eventsFired) {
+ case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
+ case 1:
+ is(e.type, 'mouselongtap', 'Got a mouselongtap');
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
+ break;
+ case 2: is(e.type, 'touchend', 'Got a touchend'); break;
+ case 3: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
+ default: ok(false, 'Got an unexpected event of type ' + e.type); break;
+ }
+ eventsFired++;
+
+ if (eventsFired == 4) {
+ dump("Finished waiting for events, doing an APZ flush to see if any more unexpected events come through...\n");
+ flushApzRepaints(function() {
+ dump("Done APZ flush, ending test...\n");
+ subtestDone();
+ });
+ }
+ } else {
+ // On non-Windows platforms we get a contextmenu event once the long-tap has
+ // been detected. Since we prevent-default that, we don't get a mouselongtap
+ // event at all, and instead get a touchcancel.
+ switch (eventsFired) {
+ case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
+ case 1: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
+ case 2: is(e.type, 'touchcancel', 'Got a touchcancel'); break;
+ default: ok(false, 'Got an unexpected event of type ' + e.type); break;
+ }
+ eventsFired++;
+
+ if (eventsFired == 3) {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
+ dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
+ flushApzRepaints(function() {
+ dump("Done APZ flush, ending test...\n");
+ subtestDone();
+ });
+ });
+ }
+ }
+}
+
+window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
+window.addEventListener('touchend', recordEvent, { passive: true, capture: true });
+window.addEventListener('touchcancel', recordEvent, true);
+window.addEventListener('contextmenu', recordEvent, true);
+SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
+
+waitUntilApzStable()
+.then(longPressLink);
+
+ </script>
+</head>
+<body>
+ <a id="b" href="#">Link to nowhere</a>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html
new file mode 100644
index 000000000..da866c1ce
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html
@@ -0,0 +1,46 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over inactive subframe with perspective</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var subframe = document.getElementById('scroll');
+
+ // scroll over the middle of the subframe, to make sure it scrolls,
+ // not the page
+ var scrollPos = subframe.scrollTop;
+ yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+ dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+ ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ #scroll {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ perspective: 400px;
+ }
+ #scrolled {
+ width: 200px;
+ height: 1000px; /* so the subframe has room to scroll */
+ background: linear-gradient(red, blue); /* so you can see it scroll */
+ transform: translateZ(0px); /* so the perspective makes it to the display list */
+ }
+ </style>
+</head>
+<body>
+ <div id="scroll">
+ <div id="scrolled"></div>
+ </div>
+ <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
diff --git a/gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html
new file mode 100644
index 000000000..763aaf92b
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html
@@ -0,0 +1,47 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over inactive subframe with z-index</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var subframe = document.getElementById('scroll');
+
+ // scroll over the middle of the subframe, and make sure that it scrolls,
+ // not the page
+ var scrollPos = subframe.scrollTop;
+ yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+ dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+ ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+ <style>
+ #scroll {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ }
+ #scrolled {
+ width: 200px;
+ height: 1000px; /* so the subframe has room to scroll */
+ z-index: 2;
+ background: linear-gradient(red, blue); /* so you can see it scroll */
+ transform: translateZ(0px); /* to force active layers */
+ will-change: transform; /* to force active layers */
+ }
+ </style>
+</head>
+<body>
+ <div id="scroll">
+ <div id="scrolled"></div>
+ </div>
+ <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
diff --git a/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html b/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
new file mode 100644
index 000000000..b9d187faf
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
@@ -0,0 +1,62 @@
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var iframeWin = document.getElementById('iframe').contentWindow;
+
+ // scroll over the middle of the iframe's position:sticky element, check
+ // that it scrolls the iframe
+ var scrollPos = iframeWin.scrollY;
+ yield moveMouseAndScrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
+ ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
+
+ // same, but using the iframe's position:fixed element
+ scrollPos = iframeWin.scrollY;
+ yield moveMouseAndScrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
+ ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
+
+ // same, but scrolling the scrollable frame *inside* the position:fixed item
+ var fpos = document.getElementById('fpos_scrollable');
+ scrollPos = fpos.scrollTop;
+ yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
+ ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
+ // wait for it to layerize fully and then try again
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ scrollPos = fpos.scrollTop;
+ yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
+ ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
+
+ // same, but using the top-level window's position:sticky element
+ scrollPos = window.scrollY;
+ yield moveMouseAndScrollWheelOver(document.body, 50, 150, testDriver);
+ ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
+
+ // same, but using the top-level window's position:fixed element
+ scrollPos = window.scrollY;
+ yield moveMouseAndScrollWheelOver(document.body, 250, 150, testDriver);
+ ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:fixed element");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="height:5000px; margin:0">
+ <div style="position:sticky; width: 100px; height: 300px; top: 0; background-color:red">sticky</div>
+ <div style="position:fixed; width: 100px; height: 300px; top: 0; left: 200px; background-color: green">fixed</div>
+ <iframe id='iframe' width="300" height="400" src="data:text/html,<body style='height:5000px; margin:0'><div style='position:sticky; width:100px; height:300px; top: 0; background-color:red'>sticky</div><div style='position:fixed; right:0; top: 0; width:100px; height:300px; background-color:green'>fixed</div>"></iframe>
+
+ <div id="fpos_scrollable" style="position:fixed; width: 100px; height: 300px; top: 0; left: 400px; background-color: red; overflow:scroll">
+ <div style="background-color: blue; height: 1000px; margin: 3px">scrollable content inside a fixed-pos item</div>
+ </div>
+</body>
+</head>
diff --git a/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html b/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
new file mode 100644
index 000000000..fc444f2b7
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function startTest() {
+ if (window.scrollY == 0) {
+ // the scrollframe is not yet marked as APZ-scrollable. Mark it so and
+ // start over.
+ window.scrollTo(0, 1);
+ waitForApzFlushedRepaints(startTest);
+ return;
+ }
+
+ // This is a scroll by 20px that should use paint-skipping if possible.
+ // If paint-skipping is enabled, this should not trigger a paint, but go
+ // directly to the compositor using an empty transaction. We check for this
+ // by ensuring the document element did not get painted.
+ var utils = window.opener.SpecialPowers.getDOMWindowUtils(window);
+ var elem = document.documentElement;
+ var skipping = location.search == '?true';
+ utils.checkAndClearPaintedState(elem);
+ window.scrollTo(0, 20);
+ waitForAllPaints(function() {
+ if (skipping) {
+ is(utils.checkAndClearPaintedState(elem), false, "Document element didn't get painted");
+ }
+ // After that's done, we click on the button to make sure the
+ // skipped-paint codepath still has working APZ event transformations.
+ clickButton();
+ });
+}
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable().then(startTest);
+
+ </script>
+</head>
+<body style="height: 5000px">
+ <div style="height: 50px">spacer</div>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_subframe_style.css b/gfx/layers/apz/test/mochitest/helper_subframe_style.css
new file mode 100644
index 000000000..5af964080
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_subframe_style.css
@@ -0,0 +1,15 @@
+body {
+ height: 500px;
+}
+
+.inner-frame {
+ margin-top: 50px; /* this should be at least 30px */
+ height: 200%;
+ width: 75%;
+ overflow: scroll;
+}
+.inner-content {
+ height: 200%;
+ width: 200%;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+}
diff --git a/gfx/layers/apz/test/mochitest/helper_tall.html b/gfx/layers/apz/test/mochitest/helper_tall.html
new file mode 100644
index 000000000..7fde795fd
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tall.html
@@ -0,0 +1,504 @@
+<html id="tall_html">
+<body>
+This is a tall page<br/>
+1<br/>
+2<br/>
+3<br/>
+4<br/>
+5<br/>
+6<br/>
+7<br/>
+8<br/>
+9<br/>
+10<br/>
+11<br/>
+12<br/>
+13<br/>
+14<br/>
+15<br/>
+16<br/>
+17<br/>
+18<br/>
+19<br/>
+20<br/>
+21<br/>
+22<br/>
+23<br/>
+24<br/>
+25<br/>
+26<br/>
+27<br/>
+28<br/>
+29<br/>
+30<br/>
+31<br/>
+32<br/>
+33<br/>
+34<br/>
+35<br/>
+36<br/>
+37<br/>
+38<br/>
+39<br/>
+40<br/>
+41<br/>
+42<br/>
+43<br/>
+44<br/>
+45<br/>
+46<br/>
+47<br/>
+48<br/>
+49<br/>
+50<br/>
+51<br/>
+52<br/>
+53<br/>
+54<br/>
+55<br/>
+56<br/>
+57<br/>
+58<br/>
+59<br/>
+60<br/>
+61<br/>
+62<br/>
+63<br/>
+64<br/>
+65<br/>
+66<br/>
+67<br/>
+68<br/>
+69<br/>
+70<br/>
+71<br/>
+72<br/>
+73<br/>
+74<br/>
+75<br/>
+76<br/>
+77<br/>
+78<br/>
+79<br/>
+80<br/>
+81<br/>
+82<br/>
+83<br/>
+84<br/>
+85<br/>
+86<br/>
+87<br/>
+88<br/>
+89<br/>
+90<br/>
+91<br/>
+92<br/>
+93<br/>
+94<br/>
+95<br/>
+96<br/>
+97<br/>
+98<br/>
+99<br/>
+100<br/>
+101<br/>
+102<br/>
+103<br/>
+104<br/>
+105<br/>
+106<br/>
+107<br/>
+108<br/>
+109<br/>
+110<br/>
+111<br/>
+112<br/>
+113<br/>
+114<br/>
+115<br/>
+116<br/>
+117<br/>
+118<br/>
+119<br/>
+120<br/>
+121<br/>
+122<br/>
+123<br/>
+124<br/>
+125<br/>
+126<br/>
+127<br/>
+128<br/>
+129<br/>
+130<br/>
+131<br/>
+132<br/>
+133<br/>
+134<br/>
+135<br/>
+136<br/>
+137<br/>
+138<br/>
+139<br/>
+140<br/>
+141<br/>
+142<br/>
+143<br/>
+144<br/>
+145<br/>
+146<br/>
+147<br/>
+148<br/>
+149<br/>
+150<br/>
+151<br/>
+152<br/>
+153<br/>
+154<br/>
+155<br/>
+156<br/>
+157<br/>
+158<br/>
+159<br/>
+160<br/>
+161<br/>
+162<br/>
+163<br/>
+164<br/>
+165<br/>
+166<br/>
+167<br/>
+168<br/>
+169<br/>
+170<br/>
+171<br/>
+172<br/>
+173<br/>
+174<br/>
+175<br/>
+176<br/>
+177<br/>
+178<br/>
+179<br/>
+180<br/>
+181<br/>
+182<br/>
+183<br/>
+184<br/>
+185<br/>
+186<br/>
+187<br/>
+188<br/>
+189<br/>
+190<br/>
+191<br/>
+192<br/>
+193<br/>
+194<br/>
+195<br/>
+196<br/>
+197<br/>
+198<br/>
+199<br/>
+200<br/>
+201<br/>
+202<br/>
+203<br/>
+204<br/>
+205<br/>
+206<br/>
+207<br/>
+208<br/>
+209<br/>
+210<br/>
+211<br/>
+212<br/>
+213<br/>
+214<br/>
+215<br/>
+216<br/>
+217<br/>
+218<br/>
+219<br/>
+220<br/>
+221<br/>
+222<br/>
+223<br/>
+224<br/>
+225<br/>
+226<br/>
+227<br/>
+228<br/>
+229<br/>
+230<br/>
+231<br/>
+232<br/>
+233<br/>
+234<br/>
+235<br/>
+236<br/>
+237<br/>
+238<br/>
+239<br/>
+240<br/>
+241<br/>
+242<br/>
+243<br/>
+244<br/>
+245<br/>
+246<br/>
+247<br/>
+248<br/>
+249<br/>
+250<br/>
+251<br/>
+252<br/>
+253<br/>
+254<br/>
+255<br/>
+256<br/>
+257<br/>
+258<br/>
+259<br/>
+260<br/>
+261<br/>
+262<br/>
+263<br/>
+264<br/>
+265<br/>
+266<br/>
+267<br/>
+268<br/>
+269<br/>
+270<br/>
+271<br/>
+272<br/>
+273<br/>
+274<br/>
+275<br/>
+276<br/>
+277<br/>
+278<br/>
+279<br/>
+280<br/>
+281<br/>
+282<br/>
+283<br/>
+284<br/>
+285<br/>
+286<br/>
+287<br/>
+288<br/>
+289<br/>
+290<br/>
+291<br/>
+292<br/>
+293<br/>
+294<br/>
+295<br/>
+296<br/>
+297<br/>
+298<br/>
+299<br/>
+300<br/>
+301<br/>
+302<br/>
+303<br/>
+304<br/>
+305<br/>
+306<br/>
+307<br/>
+308<br/>
+309<br/>
+310<br/>
+311<br/>
+312<br/>
+313<br/>
+314<br/>
+315<br/>
+316<br/>
+317<br/>
+318<br/>
+319<br/>
+320<br/>
+321<br/>
+322<br/>
+323<br/>
+324<br/>
+325<br/>
+326<br/>
+327<br/>
+328<br/>
+329<br/>
+330<br/>
+331<br/>
+332<br/>
+333<br/>
+334<br/>
+335<br/>
+336<br/>
+337<br/>
+338<br/>
+339<br/>
+340<br/>
+341<br/>
+342<br/>
+343<br/>
+344<br/>
+345<br/>
+346<br/>
+347<br/>
+348<br/>
+349<br/>
+350<br/>
+351<br/>
+352<br/>
+353<br/>
+354<br/>
+355<br/>
+356<br/>
+357<br/>
+358<br/>
+359<br/>
+360<br/>
+361<br/>
+362<br/>
+363<br/>
+364<br/>
+365<br/>
+366<br/>
+367<br/>
+368<br/>
+369<br/>
+370<br/>
+371<br/>
+372<br/>
+373<br/>
+374<br/>
+375<br/>
+376<br/>
+377<br/>
+378<br/>
+379<br/>
+380<br/>
+381<br/>
+382<br/>
+383<br/>
+384<br/>
+385<br/>
+386<br/>
+387<br/>
+388<br/>
+389<br/>
+390<br/>
+391<br/>
+392<br/>
+393<br/>
+394<br/>
+395<br/>
+396<br/>
+397<br/>
+398<br/>
+399<br/>
+400<br/>
+401<br/>
+402<br/>
+403<br/>
+404<br/>
+405<br/>
+406<br/>
+407<br/>
+408<br/>
+409<br/>
+410<br/>
+411<br/>
+412<br/>
+413<br/>
+414<br/>
+415<br/>
+416<br/>
+417<br/>
+418<br/>
+419<br/>
+420<br/>
+421<br/>
+422<br/>
+423<br/>
+424<br/>
+425<br/>
+426<br/>
+427<br/>
+428<br/>
+429<br/>
+430<br/>
+431<br/>
+432<br/>
+433<br/>
+434<br/>
+435<br/>
+436<br/>
+437<br/>
+438<br/>
+439<br/>
+440<br/>
+441<br/>
+442<br/>
+443<br/>
+444<br/>
+445<br/>
+446<br/>
+447<br/>
+448<br/>
+449<br/>
+450<br/>
+451<br/>
+452<br/>
+453<br/>
+454<br/>
+455<br/>
+456<br/>
+457<br/>
+458<br/>
+459<br/>
+460<br/>
+461<br/>
+462<br/>
+463<br/>
+464<br/>
+465<br/>
+466<br/>
+467<br/>
+468<br/>
+469<br/>
+470<br/>
+471<br/>
+472<br/>
+473<br/>
+474<br/>
+475<br/>
+476<br/>
+477<br/>
+478<br/>
+479<br/>
+480<br/>
+481<br/>
+482<br/>
+483<br/>
+484<br/>
+485<br/>
+486<br/>
+487<br/>
+488<br/>
+489<br/>
+490<br/>
+491<br/>
+492<br/>
+493<br/>
+494<br/>
+495<br/>
+496<br/>
+497<br/>
+498<br/>
+499<br/>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_tap.html b/gfx/layers/apz/test/mochitest/helper_tap.html
new file mode 100644
index 000000000..6fde9387d
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tap.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+waitUntilApzStable().then(clickButton);
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px"></button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html b/gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html
new file mode 100644
index 000000000..494363b9c
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-tapping test with fullzoom</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function clickButton() {
+ document.addEventListener('click', clicked, false);
+
+ synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+ dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+ });
+}
+
+function clicked(e) {
+ is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+ subtestDone();
+}
+
+SpecialPowers.setFullZoom(window, 2.0);
+waitUntilApzStable().then(clickButton);
+
+ </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px; position: relative; top: 100px"></button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_tap_passive.html b/gfx/layers/apz/test/mochitest/helper_tap_passive.html
new file mode 100644
index 000000000..dc3d85ed2
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tap_passive.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Ensure APZ doesn't wait for passive listeners</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+var touchdownTime;
+
+function longPressLink() {
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
+ dump("Finished synthesizing touch-start, waiting for events...\n");
+ });
+}
+
+var touchstartReceived = false;
+function recordEvent(e) {
+ if (!touchstartReceived) {
+ touchstartReceived = true;
+ is(e.type, 'touchstart', 'Got a touchstart');
+ e.preventDefault(); // should be a no-op because it's a passive listener
+ return;
+ }
+
+ // If APZ decides to wait for the content response on a particular input block,
+ // it needs to wait until both the touchstart and touchmove event are handled
+ // by the main thread. In this case there is no touchmove at all, so APZ would
+ // end up waiting indefinitely and time out the test. The fact that we get this
+ // contextmenu event (mouselongtap on Windows) at all means that APZ decided
+ // not to wait for the content response, which is the desired behaviour, since
+ // the touchstart listener was registered as a passive listener.
+ if (getPlatform() == "windows") {
+ is(e.type, 'mouselongtap', 'Got a mouselongtap');
+ } else {
+ is(e.type, 'contextmenu', 'Got a contextmenu');
+ }
+ e.preventDefault();
+
+ synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
+ dump("Finished synthesizing touch-end to clear state; finishing test...\n");
+ subtestDone();
+ });
+}
+
+window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
+if (getPlatform() == "windows") {
+ SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
+} else {
+ window.addEventListener('contextmenu', recordEvent, true);
+}
+
+waitUntilApzStable()
+.then(longPressLink);
+
+ </script>
+</head>
+<body>
+ <a id="b" href="#">Link to nowhere</a>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_touch_action.html b/gfx/layers/apz/test/mochitest/helper_touch_action.html
new file mode 100644
index 000000000..4495dc76e
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_touch_action.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Sanity touch-action test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function checkScroll(x, y, desc) {
+ is(window.scrollX, x, desc + " - x axis");
+ is(window.scrollY, y, desc + " - y axis");
+}
+
+function* test(testDriver) {
+ var target = document.getElementById('target');
+
+ document.body.addEventListener('touchend', testDriver, { passive: true });
+
+ // drag the page up to scroll down by 50px
+ yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
+ "Synthesized native vertical drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(0, 50, "After first vertical drag, with pan-y" );
+
+ // switch style to pan-x
+ document.body.style.touchAction = 'pan-x';
+ ok(true, "Waiting for pan-x to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page up to scroll down by 50px, but it won't happen because pan-x
+ yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
+ "Synthesized native vertical drag (2), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(0, 50, "After second vertical drag, with pan-x");
+
+ // drag the page left to scroll right by 50px
+ yield ok(synthesizeNativeTouchDrag(target, 100, 10, -(50 + TOUCH_SLOP), 0),
+ "Synthesized horizontal drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(50, 50, "After first horizontal drag, with pan-x");
+
+ // drag the page diagonally right/down to scroll up/left by 40px each axis;
+ // only the x-axis will actually scroll because pan-x
+ yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (1), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 50, "After first diagonal drag, with pan-x");
+
+ // switch style back to pan-y
+ document.body.style.touchAction = 'pan-y';
+ ok(true, "Waiting for pan-y to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally right/down to scroll up/left by 40px each axis;
+ // only the y-axis will actually scroll because pan-y
+ yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (2), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 10, "After second diagonal drag, with pan-y");
+
+ // switch style to none
+ document.body.style.touchAction = 'none';
+ ok(true, "Waiting for none to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally up/left to scroll down/right by 40px each axis;
+ // neither will scroll because of touch-action
+ yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (3), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(10, 10, "After third diagonal drag, with none");
+
+ document.body.style.touchAction = 'manipulation';
+ ok(true, "Waiting for manipulation to propagate...");
+ yield waitForAllPaintsFlushed(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // drag the page diagonally up/left to scroll down/right by 40px each axis;
+ // both will scroll because of touch-action
+ yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
+ "Synthesized diagonal drag (4), waiting for touch-end event...");
+ yield flushApzRepaints(testDriver);
+ checkScroll(50, 50, "After fourth diagonal drag, with manipulation");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body style="touch-action: pan-y">
+ <div style="width: 5000px; height: 5000px; background-color: lightgreen;">
+ This div makes the page scrollable on both axes.<br>
+ This is the second line of text.<br>
+ This is the third line of text.<br>
+ This is the fourth line of text.
+ </div>
+ <!-- This fixed-position div remains in the same place relative to the browser chrome, so we
+ can use it as a targeting device for synthetic touch events. The body will move around
+ as we scroll, so we'd have to be constantly adjusting the synthetic drag coordinates
+ if we used that as the target element. -->
+ <div style="position:fixed; left: 10px; top: 10px; width: 1px; height: 1px" id="target"></div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_touch_action_complex.html b/gfx/layers/apz/test/mochitest/helper_touch_action_complex.html
new file mode 100644
index 000000000..11d6e66e1
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_touch_action_complex.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Complex touch-action test</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function checkScroll(target, x, y, desc) {
+ is(target.scrollLeft, x, desc + " - x axis");
+ is(target.scrollTop, y, desc + " - y axis");
+}
+
+function resetConfiguration(config, testDriver) {
+ // Cycle through all the configuration_X elements, setting them to display:none
+ // except for when X == config, in which case set it to display:block
+ var i = 0;
+ while (true) {
+ i++;
+ var element = document.getElementById('configuration_' + i);
+ if (element == null) {
+ if (i <= config) {
+ ok(false, "The configuration requested was not encountered!");
+ }
+ break;
+ }
+
+ if (i == config) {
+ element.style.display = 'block';
+ } else {
+ element.style.display = 'none';
+ }
+ }
+
+ // Also reset the scroll position on the scrollframe
+ var s = document.getElementById('scrollframe');
+ s.scrollLeft = 0;
+ s.scrollTop = 0;
+
+ return waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+}
+
+function* test(testDriver) {
+ var scrollframe = document.getElementById('scrollframe');
+
+ document.body.addEventListener('touchend', testDriver, { passive: true });
+
+ // Helper function for the tests below.
+ // Touch-pan configuration |configuration| towards scroll offset (dx, dy) with
+ // the pan touching down at (x, y). Check that the final scroll offset is
+ // (ex, ey). |desc| is some description string.
+ function* scrollAndCheck(configuration, x, y, dx, dy, ex, ey, desc) {
+ // Start with a clean slate
+ yield resetConfiguration(configuration, testDriver);
+ // Figure out the panning deltas
+ if (dx != 0) {
+ dx = -(dx + TOUCH_SLOP);
+ }
+ if (dy != 0) {
+ dy = -(dy + TOUCH_SLOP);
+ }
+ // Do the pan
+ yield ok(synthesizeNativeTouchDrag(scrollframe, x, y, dx, dy),
+ "Synthesized drag of (" + dx + ", " + dy + ") on configuration " + configuration);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Check for expected scroll position
+ checkScroll(scrollframe, ex, ey, 'configuration ' + configuration + ' ' + desc);
+ }
+
+ // Test configuration_1, which contains two sibling elements that are
+ // overlapping. The touch-action from the second sibling (which is on top)
+ // should be used for the overlapping area.
+ yield* scrollAndCheck(1, 25, 75, 20, 0, 20, 0, "first element horizontal scroll");
+ yield* scrollAndCheck(1, 25, 75, 0, 50, 0, 0, "first element vertical scroll");
+ yield* scrollAndCheck(1, 75, 75, 50, 0, 0, 0, "overlap horizontal scroll");
+ yield* scrollAndCheck(1, 75, 75, 0, 50, 0, 50, "overlap vertical scroll");
+ yield* scrollAndCheck(1, 125, 75, 20, 0, 0, 0, "second element horizontal scroll");
+ yield* scrollAndCheck(1, 125, 75, 0, 50, 0, 50, "second element vertical scroll");
+
+ // Test configuration_2, which contains two overlapping elements with a
+ // parent/child relationship. The parent has pan-x and the child has pan-y,
+ // which means that panning on the parent should work horizontally only, and
+ // on the child no panning should occur at all.
+ yield* scrollAndCheck(2, 125, 125, 50, 50, 0, 0, "child scroll");
+ yield* scrollAndCheck(2, 75, 75, 50, 50, 0, 0, "overlap scroll");
+ yield* scrollAndCheck(2, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
+ yield* scrollAndCheck(2, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
+
+ // Test configuration_3, which is the same as configuration_2, except the child
+ // has a rotation transform applied. This forces the event regions on the two
+ // elements to be built separately and then get merged.
+ yield* scrollAndCheck(3, 125, 125, 50, 50, 0, 0, "child scroll");
+ yield* scrollAndCheck(3, 75, 75, 50, 50, 0, 0, "overlap scroll");
+ yield* scrollAndCheck(3, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
+ yield* scrollAndCheck(3, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
+
+ // Test configuration_4 has two elements, one above the other, not overlapping,
+ // and the second element is a child of the first. The parent has pan-x, the
+ // child has pan-y, but that means panning horizontally on the parent should
+ // work and panning in any direction on the child should not do anything.
+ yield* scrollAndCheck(4, 75, 75, 50, 50, 50, 0, "parent diagonal scroll");
+ yield* scrollAndCheck(4, 75, 150, 50, 50, 0, 0, "child diagonal scroll");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+ </script>
+</head>
+<body>
+ <div id="scrollframe" style="width: 300px; height: 300px; overflow:scroll">
+ <div id="scrolled_content" style="width: 1000px; height: 1000px; background-color: green">
+ </div>
+ <div id="configuration_1" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue"></div>
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: -100px; left: 50px; background-color: yellow"></div>
+ </div>
+ <div id="configuration_2" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow"></div>
+ </div>
+ </div>
+ <div id="configuration_3" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow; transform: rotate(90deg)"></div>
+ </div>
+ </div>
+ <div id="configuration_4" style="display:none; position: relative; top: -1000px">
+ <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+ <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 125px; background-color: yellow"></div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html b/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html
new file mode 100644
index 000000000..cbd4cd61d
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_touch_action_regions.html
@@ -0,0 +1,246 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test to ensure APZ doesn't always wait for touch-action</title>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript">
+
+function failure(e) {
+ ok(false, "This event listener should not have triggered: " + e.type);
+}
+
+function success(e) {
+ success.triggered = true;
+}
+
+// This helper function provides a way for the child process to synchronously
+// check how many touch events the chrome process main-thread has processed. This
+// function can be called with three values: 'start', 'report', and 'end'.
+// The 'start' invocation sets up the listeners, and should be invoked before
+// the touch events of interest are generated. This should only be called once.
+// This returns true on success, and false on failure.
+// The 'report' invocation can be invoked multiple times, and returns an object
+// (in JSON string format) containing the counters.
+// The 'end' invocation tears down the listeners, and should be invoked once
+// at the end to clean up. Returns true on success, false on failure.
+function chromeTouchEventCounter(operation) {
+ function chromeProcessCounter() {
+ addMessageListener('start', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ if (typeof topWin.eventCounts != 'undefined') {
+ dump('Found pre-existing eventCounts object on the top window!\n');
+ return false;
+ }
+ topWin.eventCounts = { 'touchstart': 0, 'touchmove': 0, 'touchend': 0 };
+ topWin.counter = function(e) {
+ topWin.eventCounts[e.type]++;
+ }
+
+ topWin.addEventListener('touchstart', topWin.counter, { passive: true });
+ topWin.addEventListener('touchmove', topWin.counter, { passive: true });
+ topWin.addEventListener('touchend', topWin.counter, { passive: true });
+
+ return true;
+ });
+
+ addMessageListener('report', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ return JSON.stringify(topWin.eventCounts);
+ });
+
+ addMessageListener('end', function() {
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+ if (typeof topWin.eventCounts == 'undefined') {
+ dump('The eventCounts object was not found on the top window!\n');
+ return false;
+ }
+ topWin.removeEventListener('touchstart', topWin.counter);
+ topWin.removeEventListener('touchmove', topWin.counter);
+ topWin.removeEventListener('touchend', topWin.counter);
+ delete topWin.counter;
+ delete topWin.eventCounts;
+ return true;
+ });
+ }
+
+ if (typeof chromeTouchEventCounter.chromeHelper == 'undefined') {
+ // This is the first time getSnapshot is being called; do initialization
+ chromeTouchEventCounter.chromeHelper = SpecialPowers.loadChromeScript(chromeProcessCounter);
+ SimpleTest.registerCleanupFunction(function() { chromeTouchEventCounter.chromeHelper.destroy() });
+ }
+
+ return chromeTouchEventCounter.chromeHelper.sendSyncMessage(operation, "");
+}
+
+// Simple wrapper that waits until the chrome process has seen |count| instances
+// of the |eventType| event. Returns true on success, and false if 10 seconds
+// go by without the condition being satisfied.
+function waitFor(eventType, count) {
+ var start = Date.now();
+ while (JSON.parse(chromeTouchEventCounter('report'))[eventType] != count) {
+ if (Date.now() - start > 10000) {
+ // It's taking too long, let's abort
+ return false;
+ }
+ }
+ return true;
+}
+
+function* test(testDriver) {
+ // The main part of this test should run completely before the child process'
+ // main-thread deals with the touch event, so check to make sure that happens.
+ document.body.addEventListener('touchstart', failure, { passive: true });
+
+ // What we want here is to synthesize all of the touch events (from this code in
+ // the child process), and have the chrome process generate and process them,
+ // but not allow the events to be dispatched back into the child process until
+ // later. This allows us to ensure that the APZ in the chrome process is not
+ // waiting for the child process to send notifications upon processing the
+ // events. If it were doing so, the APZ would block and this test would fail.
+
+ // In order to actually implement this, we call the synthesize functions with
+ // a async callback in between. The synthesize functions just queue up a
+ // runnable on the child process main thread and return immediately, so with
+ // the async callbacks, the child process main thread queue looks like
+ // this after we're done setting it up:
+ // synthesizeTouchStart
+ // callback testDriver
+ // synthesizeTouchMove
+ // callback testDriver
+ // ...
+ // synthesizeTouchEnd
+ // callback testDriver
+ //
+ // If, after setting up this queue, we yield once, the first synthesization and
+ // callback will run - this will send a synthesization message to the chrome
+ // process, and return control back to us right away. When the chrome process
+ // processes with the synthesized event, it will dispatch the DOM touch event
+ // back to the child process over IPC, which will go into the end of the child
+ // process main thread queue, like so:
+ // synthesizeTouchStart (done)
+ // invoke testDriver (done)
+ // synthesizeTouchMove
+ // invoke testDriver
+ // ...
+ // synthesizeTouchEnd
+ // invoke testDriver
+ // handle DOM touchstart <-- touchstart goes at end of queue
+ //
+ // As we continue yielding one at a time, the synthesizations run, and the
+ // touch events get added to the end of the queue. As we yield, we take
+ // snapshots in the chrome process, to make sure that the APZ has started
+ // scrolling even though we know we haven't yet processed the DOM touch events
+ // in the child process yet.
+ //
+ // Note that the "async callback" we use here is SpecialPowers.executeSoon,
+ // because nothing else does exactly what we want:
+ // - setTimeout(..., 0) does not maintain ordering, because it respects the
+ // time delta provided (i.e. the callback can jump the queue to meet its
+ // deadline).
+ // - SpecialPowers.spinEventLoop and SpecialPowers.executeAfterFlushingMessageQueue
+ // are not e10s friendly, and can get arbitrarily delayed due to IPC
+ // round-trip time.
+ // - SimpleTest.executeSoon has a codepath that delegates to setTimeout, so
+ // is less reliable if it ever decides to switch to that codepath.
+
+ // The other problem we need to deal with is the asynchronicity in the chrome
+ // process. That is, we might request a snapshot before the chrome process has
+ // actually synthesized the event and processed it. To guard against this, we
+ // register a thing in the chrome process that counts the touch events that
+ // have been dispatched, and poll that thing synchronously in order to make
+ // sure we only snapshot after the event in question has been processed.
+ // That's what the chromeTouchEventCounter business is all about. The sync
+ // polling looks bad but in practice only ends up needing to poll once or
+ // twice before the condition is satisfied, and as an extra precaution we add
+ // a time guard so it fails after 10s of polling.
+
+ // So, here we go...
+
+ // Set up the chrome process touch listener
+ ok(chromeTouchEventCounter('start'), "Chrome touch counter registered");
+
+ // Set up the child process events and callbacks
+ var scroller = document.getElementById('scroller');
+ synthesizeNativeTouch(scroller, 10, 110, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ for (var i = 1; i < 10; i++) {
+ synthesizeNativeTouch(scroller, 10, 110 - (i * 10), SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ }
+ synthesizeNativeTouch(scroller, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, null, 0);
+ SpecialPowers.executeSoon(testDriver);
+ ok(true, "Finished setting up event queue");
+
+ // Get our baseline snapshot
+ var rect = rectRelativeToScreen(scroller);
+ var lastSnapshot = getSnapshot(rect);
+ ok(true, "Got baseline snapshot");
+
+ yield; // this will tell the chrome process to synthesize the touchstart event
+ // and then we wait to make sure it got processed:
+ ok(waitFor('touchstart', 1), "Touchstart processed in chrome process");
+
+ // Loop through the touchmove events
+ for (var i = 1; i < 10; i++) {
+ yield;
+ ok(waitFor('touchmove', i), "Touchmove processed in chrome process");
+
+ var snapshot = getSnapshot(rect);
+ if (i == 1) {
+ // The first touchmove is consumed to get us into the panning state, so
+ // no actual panning occurs
+ ok(lastSnapshot == snapshot, "Snapshot 1 was the same as baseline");
+ } else {
+ ok(lastSnapshot != snapshot, "Snapshot " + i + " was different from the previous one");
+ }
+ lastSnapshot = snapshot;
+ }
+
+ // Wait for the touchend as well, just for good form
+ yield;
+ ok(waitFor('touchend', 1), "Touchend processed in chrome process");
+
+ // Clean up the chrome process hooks
+ chromeTouchEventCounter('end');
+
+ // Now we are going to release our grip on the child process main thread,
+ // so that all the DOM events that were queued up can be processed. We
+ // register a touchstart listener to make sure this happens.
+ document.body.removeEventListener('touchstart', failure);
+ document.body.addEventListener('touchstart', success, { passive: true });
+ yield flushApzRepaints(testDriver);
+ ok(success.triggered, "The touchstart event handler was triggered after snapshotting completed");
+ document.body.removeEventListener('touchstart', success);
+}
+
+if (SpecialPowers.isMainProcess()) {
+ // This is probably android, where everything is single-process. The
+ // test structure depends on e10s, so the test won't run properly on
+ // this platform. Skip it
+ ok(true, "Skipping test because it is designed to run from the content process");
+ subtestDone();
+} else {
+ waitUntilApzStable()
+ .then(runContinuation(test))
+ .then(subtestDone);
+}
+
+ </script>
+</head>
+<body>
+ <div id="scroller" style="width: 400px; height: 400px; overflow: scroll; touch-action: pan-y">
+ <div style="width: 200px; height: 200px; background-color: lightgreen;">
+ This is a colored div that will move on the screen as the scroller scrolls.
+ </div>
+ <div style="width: 1000px; height: 1000px; background-color: lightblue">
+ This is a large div to make the scroller scrollable.
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/mochitest.ini b/gfx/layers/apz/test/mochitest/mochitest.ini
new file mode 100644
index 000000000..09e62428c
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+ support-files =
+ apz_test_native_event_utils.js
+ apz_test_utils.js
+ helper_basic_pan.html
+ helper_bug982141.html
+ helper_bug1151663.html
+ helper_bug1162771.html
+ helper_bug1271432.html
+ helper_bug1280013.html
+ helper_bug1285070.html
+ helper_bug1299195.html
+ helper_click.html
+ helper_div_pan.html
+ helper_drag_click.html
+ helper_drag_scroll.html
+ helper_iframe_pan.html
+ helper_iframe1.html
+ helper_iframe2.html
+ helper_long_tap.html
+ helper_scroll_inactive_perspective.html
+ helper_scroll_inactive_zindex.html
+ helper_scroll_on_position_fixed.html
+ helper_scrollto_tap.html
+ helper_subframe_style.css
+ helper_tall.html
+ helper_tap.html
+ helper_tap_fullzoom.html
+ helper_tap_passive.html
+ helper_touch_action.html
+ helper_touch_action_regions.html
+ helper_touch_action_complex.html
+ tags = apz
+[test_bug982141.html]
+[test_bug1151663.html]
+[test_bug1151667.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1253683.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1277814.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_bug1304689.html]
+[test_bug1304689-2.html]
+[test_frame_reconstruction.html]
+[test_group_mouseevents.html]
+ skip-if = (toolkit == 'android') # mouse events not supported on mobile
+[test_group_pointerevents.html]
+[test_group_touchevents.html]
+[test_group_wheelevents.html]
+ skip-if = (toolkit == 'android') # wheel events not supported on mobile
+[test_group_zoom.html]
+ skip-if = (toolkit != 'android') # only android supports zoom
+[test_interrupted_reflow.html]
+[test_layerization.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_inactive_bug1190112.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_inactive_flattened_frame.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_scroll_subframe_scrollbar.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_touch_listeners_impacting_wheel.html]
+ skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
+[test_wheel_scroll.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
+[test_wheel_transactions.html]
+ skip-if = (os == 'android') # wheel events not supported on mobile
diff --git a/gfx/layers/apz/test/mochitest/test_bug1151663.html b/gfx/layers/apz/test/mochitest/test_bug1151663.html
new file mode 100644
index 000000000..10810c6ca
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1151663.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1151663</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Run the actual test in its own window, because it requires that the
+ // root APZC be scrollable. Mochitest pages themselves often run
+ // inside an iframe which means we have no control over the root APZC.
+ var w = null;
+ window.onload = function() {
+ pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
+ w = window.open("helper_bug1151663.html", "_blank");
+ });
+ };
+ }
+
+ function finishTest() {
+ w.close();
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug1151667.html b/gfx/layers/apz/test/mochitest/test_bug1151667.html
new file mode 100644
index 000000000..88facf6e9
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1151667.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151667
+-->
+<head>
+ <title>Test for Bug 1151667</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #subframe {
+ margin-top: 100px;
+ height: 500px;
+ width: 500px;
+ overflow: scroll;
+ }
+ #subframe-content {
+ height: 1000px;
+ width: 500px;
+ /* the background is so that we can see it scroll*/
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ #page-content {
+ height: 5000px;
+ width: 500px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151667">Mozilla Bug 1151667</a>
+<p id="display"></p>
+<div id="subframe">
+ <!-- This makes sure the subframe is scrollable -->
+ <div id="subframe-content"></div>
+</div>
+<!-- This makes sure the page is also scrollable, so it (rather than the subframe)
+ is considered the primary async-scrollable frame, and so the subframe isn't
+ layerized upon page load. -->
+<div id="page-content"></div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function startTest() {
+ var subframe = document.getElementById('subframe');
+ synthesizeNativeWheelAndWaitForScrollEvent(subframe, 100, 150, 0, -10, continueTest);
+}
+
+function continueTest() {
+ var subframe = document.getElementById('subframe');
+ is(subframe.scrollTop > 0, true, "We should have scrolled the subframe down");
+ is(document.documentElement.scrollTop, 0, "We should not have scrolled the page");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+waitUntilApzStable().then(startTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug1253683.html b/gfx/layers/apz/test/mochitest/test_bug1253683.html
new file mode 100644
index 000000000..52c8e4a96
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1253683.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1253683
+-->
+<head>
+ <title>Test to ensure non-scrollable frames don't get layerized</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="container" style="height: 500px; overflow:scroll">
+ <pre id="no_layer" style="background-color: #f5f5f5; margin: 15px; padding: 15px; margin-top: 100px; border: 1px solid #eee; overflow:scroll">sample code here</pre>
+ <div style="height: 5000px">spacer to make the 'container' div the root scrollable element</div>
+ </div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function* test(testDriver) {
+ var container = document.getElementById('container');
+ var no_layer = document.getElementById('no_layer');
+
+ // Check initial state
+ is(container.scrollTop, 0, "Initial scrollY should be 0");
+ ok(!isLayerized('no_layer'), "initially 'no_layer' should not be layerized");
+
+ // Scrolling over outer1 should layerize outer1, but not inner1.
+ yield moveMouseAndScrollWheelOver(no_layer, 10, 10, testDriver, true);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ ok(container.scrollTop > 0, "We should have scrolled the body");
+ ok(!isLayerized('no_layer'), "no_layer should still not be layerized");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Turn off displayport expiry so that we don't miss failures where the
+ // displayport is set and expired before we check for layerization.
+ // Also enable APZ test logging, since we use that data to determine whether
+ // a scroll frame was layerized.
+ pushPrefs([["apz.displayport_expiry_ms", 0],
+ ["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug1277814.html b/gfx/layers/apz/test/mochitest/test_bug1277814.html
new file mode 100644
index 000000000..877286468
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1277814.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1277814
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1277814</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ function* test(testDriver) {
+ // Trigger the buggy scenario
+ var subframe = document.getElementById('bug1277814-div');
+ subframe.classList.add('a');
+
+ // The transform change is animated, so let's step through 1s of animation
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ // Wait for the layer tree with any updated dispatch-to-content region to
+ // get pushed over to the APZ
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Trigger layerization of the subframe by scrolling the wheel over it
+ yield moveMouseAndScrollWheelOver(subframe, 10, 10, testDriver);
+
+ // Give APZ the chance to compute a displayport, and content
+ // to render based on it.
+ yield waitForApzFlushedRepaints(testDriver);
+
+ // Examine the content-side APZ test data
+ var contentTestData = utils.getContentAPZTestData();
+
+ // Test that the scroll frame for the div 'bug1277814-div' appears in
+ // the APZ test data. The bug this test is for causes the displayport
+ // calculation for this scroll frame to go wrong, causing it not to
+ // become layerized.
+ contentTestData = convertTestData(contentTestData);
+ var foundIt = false;
+ for (var seqNo in contentTestData.paints) {
+ var paint = contentTestData.paints[seqNo];
+ for (var scrollId in paint) {
+ var scrollFrame = paint[scrollId];
+ if ('contentDescription' in scrollFrame &&
+ scrollFrame['contentDescription'].includes('bug1277814-div')) {
+ foundIt = true;
+ }
+ }
+ }
+ SimpleTest.ok(foundIt, "expected to find APZ test data for bug1277814-div");
+ }
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ pushPrefs([["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+ }
+ </script>
+ <style>
+ #bug1277814-div
+ {
+ position: absolute;
+ left: 0;
+ top: 0;
+ padding: .5em;
+ overflow: auto;
+ color: white;
+ background: green;
+ max-width: 30em;
+ max-height: 6em;
+ visibility: hidden;
+ transform: scaleY(0);
+ transition: transform .15s ease-out, visibility 0s ease .15s;
+ }
+ #bug1277814-div.a
+ {
+ visibility: visible;
+ transform: scaleY(1);
+ transition: transform .15s ease-out;
+ }
+ </style>
+</head>
+<body>
+ <!-- Use a unique id because we'll be checking for it in the content
+ description logged in the APZ test data -->
+ <div id="bug1277814-div">
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
+ <button>click me</button>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug1304689-2.html b/gfx/layers/apz/test/mochitest/test_bug1304689-2.html
new file mode 100644
index 000000000..356d7bcb3
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1304689-2.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #outer {
+ height: 400px;
+ width: 415px;
+ overflow: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ #outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+ var elm = document.getElementById('outer');
+
+ // Set margins on the element, to ensure it is layerized
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Start a smooth-scroll animation in the compositor and let it go a few
+ // frames, so that there is some "user scrolling" going on (per the comment
+ // in AsyncPanZoomController::NotifyLayersUpdated)
+ elm.scrollTop = 10;
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+
+ // Do another scroll update but also do a frame reconstruction within the same
+ // tick of the refresh driver.
+ elm.scrollTop = 100;
+ elm.classList.add('contentBefore');
+
+ // Now let everything settle and all the animations run out
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ yield flushApzRepaints(testDriver);
+ is(elm.scrollTop, 100, "The scrollTop now should be y=100");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ pushPrefs([["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+ </script>
+</head>
+<body>
+ <div id="outer">
+ <div id="inner">
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug1304689.html b/gfx/layers/apz/test/mochitest/test_bug1304689.html
new file mode 100644
index 000000000..a64f8a34e
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug1304689.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #outer {
+ height: 400px;
+ width: 415px;
+ overflow: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ #outer.instant {
+ scroll-behavior: auto;
+ }
+ #outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ <script type="application/javascript">
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+ var elm = document.getElementById('outer');
+
+ // Set margins on the element, to ensure it is layerized
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Start a smooth-scroll animation in the compositor and let it go a few
+ // frames, so that there is some "user scrolling" going on (per the comment
+ // in AsyncPanZoomController::NotifyLayersUpdated)
+ elm.scrollTop = 10;
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+
+ // Do another scroll update but also do a frame reconstruction within the same
+ // tick of the refresh driver.
+ elm.classList.add('instant');
+ elm.scrollTop = 100;
+ elm.classList.add('contentBefore');
+
+ // Now let everything settle and all the animations run out
+ for (var i = 0; i < 60; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+
+ yield flushApzRepaints(testDriver);
+ is(elm.scrollTop, 100, "The scrollTop now should be y=100");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ pushPrefs([["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+ </script>
+</head>
+<body>
+ <div id="outer">
+ <div id="inner">
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ this is some scrollable text.<br>
+ this is a second line to make the scrolling more obvious.<br>
+ and a third for good measure.<br>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_bug982141.html b/gfx/layers/apz/test/mochitest/test_bug982141.html
new file mode 100644
index 000000000..9984b79ff
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_bug982141.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982141
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 982141</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Run the actual test in its own window, because it requires that the
+ // root APZC not be scrollable. Mochitest pages themselves often run
+ // inside an iframe which means we have no control over the root APZC.
+ var w = null;
+ window.onload = function() {
+ pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
+ w = window.open("helper_bug982141.html", "_blank");
+ });
+ };
+ }
+
+ function finishTest() {
+ w.close();
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_frame_reconstruction.html b/gfx/layers/apz/test/mochitest/test_frame_reconstruction.html
new file mode 100644
index 000000000..589fb2843
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_frame_reconstruction.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1235899
+ -->
+ <head>
+ <title>Test for bug 1235899</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .outer {
+ height: 400px;
+ width: 415px;
+ overflow: hidden;
+ position: relative;
+ }
+ .inner {
+ height: 100%;
+ outline: none;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: relative;
+ scroll-behavior: smooth;
+ }
+ .outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ </head>
+ <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1235899">Mozilla Bug 1235899</a>
+<p id="display"></p>
+<div id="content">
+ <p>You should be able to fling this list without it stopping abruptly</p>
+ <div class="outer">
+ <div class="inner">
+ <ol>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ <li>Some text</li>
+ </ol>
+ </div>
+ </div>
+</div>
+
+<pre id="test">
+<script type="application/javascript;version=1.7">
+function* test(testDriver) {
+ var elm = document.getElementsByClassName('inner')[0];
+ elm.scrollTop = 0;
+ yield flushApzRepaints(testDriver);
+
+ // Take over control of the refresh driver and compositor
+ var utils = SpecialPowers.DOMWindowUtils;
+ utils.advanceTimeAndRefresh(0);
+
+ // Kick off an APZ smooth-scroll to 0,200
+ elm.scrollTo(0, 200);
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
+
+ // Let's do a couple of frames of the animation, and make sure it's going
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ yield flushApzRepaints(testDriver);
+ ok(elm.scrollTop > 0, "APZ animation in progress", "scrollTop is now " + elm.scrollTop);
+ ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
+
+ var frameReconstructionTriggered = 0;
+ // Register the listener that triggers the frame reconstruction
+ elm.onscroll = function() {
+ // Do the reconstruction
+ elm.parentNode.classList.add('contentBefore');
+ frameReconstructionTriggered++;
+ // schedule a thing to undo the changes above
+ setTimeout(function() {
+ elm.parentNode.classList.remove('contentBefore');
+ }, 0);
+ }
+
+ // and do a few more frames of the animation, this should trigger the listener
+ // and the frame reconstruction
+ utils.advanceTimeAndRefresh(16);
+ utils.advanceTimeAndRefresh(16);
+ yield flushApzRepaints(testDriver);
+ ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
+ ok(frameReconstructionTriggered > 0, "Frame reconstruction triggered", "reconstruction triggered " + frameReconstructionTriggered + " times");
+
+ // and now run to completion
+ for (var i = 0; i < 100; i++) {
+ utils.advanceTimeAndRefresh(16);
+ }
+ utils.restoreNormalRefresh();
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
+ yield flushApzRepaints(testDriver);
+
+ is(elm.scrollTop, 200, "Element should have scrolled by 200px");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
+ waitUntilApzStable()
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_group_mouseevents.html b/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
new file mode 100644
index 000000000..dcf71f0cc
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various mouse tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var subtests = [
+ // Sanity test to synthesize a mouse click
+ {'file': 'helper_click.html?dtc=false'},
+ // Same as above, but with a dispatch-to-content region that exercises the
+ // main-thread notification codepaths for mouse events
+ {'file': 'helper_click.html?dtc=true'},
+ // Sanity test for click but with some mouse movement between the down and up
+ {'file': 'helper_drag_click.html'},
+ // Test for dragging on a fake-scrollbar element that scrolls the page
+ {'file': 'helper_drag_scroll.html'}
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_group_pointerevents.html b/gfx/layers/apz/test/mochitest/test_group_pointerevents.html
new file mode 100644
index 000000000..2e8d7c240
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_group_pointerevents.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1285070
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285070</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ var subtests = [
+ {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
+ {'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
+ ];
+
+ if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+ }
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_group_touchevents.html b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
new file mode 100644
index 000000000..bc0261d46
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various touch tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var basic_pan_prefs = [
+ // Dropping the touch slop to 0 makes the tests easier to write because
+ // we can just do a one-pixel drag to get over the pan threshold rather
+ // than having to hard-code some larger value.
+ ["apz.touch_start_tolerance", "0.0"],
+ // The touchstart from the drag can turn into a long-tap if the touch-move
+ // events get held up. Try to prevent that by making long-taps require
+ // a 10 second hold. Note that we also cannot enable chaos mode on this
+ // test for this reason, since chaos mode can cause the long-press timer
+ // to fire sooner than the pref dictates.
+ ["ui.click_hold_context_menus.delay", 10000],
+ // The subtests in this test do touch-drags to pan the page, but we don't
+ // want those pans to turn into fling animations, so we increase the
+ // fling min velocity requirement absurdly high.
+ ["apz.fling_min_velocity_threshold", "10000"],
+ // The helper_div_pan's div gets a displayport on scroll, but if the
+ // test takes too long the displayport can expire before the new scroll
+ // position is synced back to the main thread. So we disable displayport
+ // expiry for these tests.
+ ["apz.displayport_expiry_ms", 0],
+];
+
+var touch_action_prefs = basic_pan_prefs.slice(); // make a copy
+touch_action_prefs.push(["layout.css.touch_action.enabled", true]);
+
+var isWindows = (getPlatform() == "windows");
+
+var subtests = [
+ // Simple tests to exercise basic panning behaviour
+ {'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
+ {'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
+ {'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},
+
+ // Simple test to exercise touch-tapping behaviour
+ {'file': 'helper_tap.html'},
+ // Tapping, but with a full-zoom applied
+ {'file': 'helper_tap_fullzoom.html'},
+
+ // For the following two tests, disable displayport suppression to make sure it
+ // doesn't interfere with the test by scheduling paints non-deterministically.
+ {'file': 'helper_scrollto_tap.html?true', 'prefs': [["apz.paint_skipping.enabled", true]], 'dp_suppression': false},
+ {'file': 'helper_scrollto_tap.html?false', 'prefs': [["apz.paint_skipping.enabled", false]], 'dp_suppression': false},
+
+ // Taps on media elements to make sure the touchend event is delivered
+ // properly. We increase the long-tap timeout to ensure it doesn't get trip
+ // during the tap.
+ // Also this test (on Windows) cannot satisfy the OS requirement of providing
+ // an injected touch event every 100ms, because it waits for a paint between
+ // the touchstart and the touchend, so we have to use the "fake injection"
+ // code instead.
+ {'file': 'helper_bug1162771.html', 'prefs': [["ui.click_hold_context_menus.delay", 10000],
+ ["apz.test.fails_with_native_injection", isWindows]]},
+
+ // As with the previous test, this test cannot inject touch events every 100ms
+ // because it waits for a long-tap, so we have to use the "fake injection" code
+ // instead.
+ {'file': 'helper_long_tap.html', 'prefs': [["apz.test.fails_with_native_injection", isWindows]]},
+
+ // For the following test, we want to make sure APZ doesn't wait for a content
+ // response that is never going to arrive. To detect this we set the content response
+ // timeout to a day, so that the entire test times out and fails if APZ does
+ // end up waiting.
+ {'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
+ ["apz.test.fails_with_native_injection", isWindows]]},
+
+ // Simple test to exercise touch-action CSS property
+ {'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
+ // More complex touch-action tests, with overlapping regions and such
+ {'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
+ // Tests that touch-action CSS properties are handled in APZ without waiting
+ // on the main-thread, when possible
+ {'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
+];
+
+if (isApzEnabled()) {
+ ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
+ if (getPlatform() == "android") {
+ // This has a lot of subtests, and Android emulators are slow.
+ SimpleTest.requestLongerTimeout(2);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_group_wheelevents.html b/gfx/layers/apz/test/mochitest/test_group_wheelevents.html
new file mode 100644
index 000000000..98c36f320
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_group_wheelevents.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various wheel-scrolling tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var prefs = [
+ // turn off smooth scrolling so that we don't have to wait for
+ // APZ animations to finish before sampling the scroll offset
+ ['general.smoothScroll', false],
+ // ensure that any mouse movement will trigger a new wheel transaction,
+ // because in this test we move the mouse a bunch and want to recalculate
+ // the target APZC after each such movement.
+ ['mousewheel.transaction.ignoremovedelay', 0],
+ ['mousewheel.transaction.timeout', 0]
+]
+
+var subtests = [
+ {'file': 'helper_scroll_on_position_fixed.html', 'prefs': prefs},
+ {'file': 'helper_bug1271432.html', 'prefs': prefs},
+ {'file': 'helper_scroll_inactive_perspective.html', 'prefs': prefs},
+ {'file': 'helper_scroll_inactive_zindex.html', 'prefs': prefs}
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_group_zoom.html b/gfx/layers/apz/test/mochitest/test_group_zoom.html
new file mode 100644
index 000000000..4bf9c0bed
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_group_zoom.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Various zoom-related tests that spawn in new windows</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+var prefs = [
+ // We need the APZ paint logging information
+ ["apz.test.logging_enabled", true],
+ // Dropping the touch slop to 0 makes the tests easier to write because
+ // we can just do a one-pixel drag to get over the pan threshold rather
+ // than having to hard-code some larger value.
+ ["apz.touch_start_tolerance", "0.0"],
+ // The subtests in this test do touch-drags to pan the page, but we don't
+ // want those pans to turn into fling animations, so we increase the
+ // fling-stop threshold velocity to absurdly high.
+ ["apz.fling_stopped_threshold", "10000"],
+ // The helper_bug1280013's div gets a displayport on scroll, but if the
+ // test takes too long the displayport can expire before we read the value
+ // out of the test. So we disable displayport expiry for these tests.
+ ["apz.displayport_expiry_ms", 0],
+];
+
+var subtests = [
+ {'file': 'helper_bug1280013.html', 'prefs': prefs},
+];
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish);
+ };
+}
+
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_interrupted_reflow.html b/gfx/layers/apz/test/mochitest/test_interrupted_reflow.html
new file mode 100644
index 000000000..05c5e5478
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_interrupted_reflow.html
@@ -0,0 +1,719 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1292781
+ -->
+ <head>
+ <title>Test for bug 1292781</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .outer {
+ height: 400px;
+ width: 415px;
+ overflow: hidden;
+ position: relative;
+ }
+ .inner {
+ height: 100%;
+ outline: none;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ position: relative;
+ }
+ .inner div:nth-child(even) {
+ background-color: lightblue;
+ }
+ .inner div:nth-child(odd) {
+ background-color: lightgreen;
+ }
+ .outer.contentBefore::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ </head>
+ <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292781">Mozilla Bug 1292781</a>
+<p id="display"></p>
+<div id="content">
+ <p>The frame reconstruction should not leave this scrollframe in a bad state</p>
+ <div class="outer">
+ <div class="inner">
+ this is the top of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is near the top of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is near the bottom of the scrollframe.
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ <div>this is a box</div>
+ this is the bottom of the scrollframe.
+ </div>
+ </div>
+</div>
+
+<pre id="test">
+<script type="text/javascript">
+
+// Returns a list of async scroll offsets that the |inner| element had, one for
+// each paint.
+function getAsyncScrollOffsets(aPaintsToIgnore) {
+ var offsets = [];
+ var compositorTestData = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData();
+ var buckets = compositorTestData.paints.slice(aPaintsToIgnore);
+ ok(buckets.length >= 3, "Expected at least three paints in the compositor test data");
+ var childIsLayerized = false;
+ for (var i = 0; i < buckets.length; ++i) {
+ var apzcTree = buildApzcTree(convertScrollFrameData(buckets[i].scrollFrames));
+ var rcd = findRcdNode(apzcTree);
+ if (rcd == null) {
+ continue;
+ }
+ if (rcd.children.length > 0) {
+ // The child may not be layerized in the first few paints, but once it is
+ // layerized, it should stay layerized.
+ childIsLayerized = true;
+ }
+ if (!childIsLayerized) {
+ continue;
+ }
+
+ ok(rcd.children.length == 1, "Root content APZC has exactly one child");
+ var scroll = rcd.children[0].asyncScrollOffset;
+ var pieces = scroll.replace(/[()\s]+/g, '').split(',');
+ is(pieces.length, 2, "expected string of form (x,y)");
+ offsets.push({ x: parseInt(pieces[0]),
+ y: parseInt(pieces[1]) });
+ }
+ return offsets;
+}
+
+function* test(testDriver) {
+ var utils = SpecialPowers.DOMWindowUtils;
+
+ // The APZ test data accumulates whenever a test turns it on. We just want
+ // the data for this test, so we check how many frames are already recorded
+ // and discard those later.
+ var framesToSkip = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData().paints.length;
+
+ var elm = document.getElementsByClassName('inner')[0];
+ // Set a zero-margin displayport to ensure that the element is async-scrollable
+ // otherwise on Fennec it is not
+ utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, 0);
+
+ var maxScroll = elm.scrollTopMax;
+ elm.scrollTop = maxScroll;
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take control of the refresh driver
+ utils.advanceTimeAndRefresh(0);
+
+ // Force the next reflow to get interrupted
+ utils.forceReflowInterrupt();
+
+ // Make a change that triggers frame reconstruction, and then tick the refresh
+ // driver so that layout processes the pending restyles and then runs an
+ // interruptible reflow. That reflow *will* be interrupted (because of the flag
+ // we set above), and we should end up with a transient 0,0 scroll offset
+ // being sent to the compositor.
+ elm.parentNode.classList.add('contentBefore');
+ utils.advanceTimeAndRefresh(0);
+ // On android, and maybe non-e10s platforms generally, we need to manually
+ // kick the paint to send the layer transaction to the compositor.
+ yield waitForAllPaints(function() { setTimeout(testDriver, 0) });
+
+ // Read the main-thread scroll offset; although this is temporarily 0,0 that
+ // temporary value is never exposed to content - instead reading this value
+ // will finish doing the interrupted reflow from above and then report the
+ // correct scroll offset.
+ is(elm.scrollTop, maxScroll, "Main-thread scroll position was restored");
+
+ // .. and now flush everything to make sure the state gets pushed over to the
+ // compositor and APZ as well.
+ utils.restoreNormalRefresh();
+ yield waitForApzFlushedRepaints(testDriver);
+
+ // Now we pull the compositor data and check it. What we expect to see is that
+ // the scroll position goes to maxScroll, then drops to 0 and then goes back
+ // to maxScroll. This test is specifically testing that last bit - that it
+ // properly gets restored from 0 to maxScroll.
+ // The one hitch is that on Android this page is loaded with some amount of
+ // zoom, and the async scroll is in ParentLayerPixel coordinates, so it will
+ // not match maxScroll exactly. Since we can't reliably compute what that
+ // ParentLayer scroll will be, we just make sure the async scroll is nonzero
+ // and use the first value we encounter to verify that it got restored properly.
+ // The other alternative is to spawn this test into a new window with 1.0 zoom
+ // but I'm tired of doing that for pretty much every test.
+ var state = 0;
+ var asyncScrollOffsets = getAsyncScrollOffsets(framesToSkip);
+ dump("Got scroll offsets: " + JSON.stringify(asyncScrollOffsets) + "\n");
+ var maxScrollParentLayerPixels = maxScroll;
+ while (asyncScrollOffsets.length > 0) {
+ let offset = asyncScrollOffsets.shift();
+ switch (state) {
+ // 0 is the initial state, the scroll offset might be zero but should
+ // become non-zero from when we set scrollTop to scrollTopMax
+ case 0:
+ if (offset.y == 0) {
+ break;
+ }
+ if (getPlatform() == "android") {
+ ok(offset.y > 0, "Async scroll y of scrollframe is " + offset.y);
+ maxScrollParentLayerPixels = offset.y;
+ } else {
+ is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe is " + offset.y);
+ }
+ state = 1;
+ break;
+
+ // state 1 starts out at maxScrollParentLayerPixels, should drop to 0
+ // because of the interrupted reflow putting the scroll into a transient
+ // zero state
+ case 1:
+ if (offset.y == maxScrollParentLayerPixels) {
+ break;
+ }
+ is(offset.y, 0, "Async scroll position was temporarily 0");
+ state = 2;
+ break;
+
+ // state 2 starts out the transient 0 scroll offset, and we expect the
+ // scroll position to get restored back to maxScrollParentLayerPixels
+ case 2:
+ if (offset.y == 0) {
+ break;
+ }
+ is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe restored to " + offset.y);
+ state = 3;
+ break;
+
+ // Terminal state. The scroll position should stay at maxScrollParentLayerPixels
+ case 3:
+ is(offset.y, maxScrollParentLayerPixels, "Scroll position maintained");
+ break;
+ }
+ }
+ is(state, 3, "The scroll position did drop to 0 and then get restored properly");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ pushPrefs([["apz.test.logging_enabled", true],
+ ["apz.displayport_expiry_ms", 0]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_layerization.html b/gfx/layers/apz/test/mochitest/test_layerization.html
new file mode 100644
index 000000000..c74b181bd
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_layerization.html
@@ -0,0 +1,214 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
+-->
+<head>
+ <title>Test for layerization</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
+ <style>
+ #container {
+ display: flex;
+ overflow: scroll;
+ height: 500px;
+ }
+ .outer-frame {
+ height: 500px;
+ overflow: scroll;
+ flex-basis: 100%;
+ background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
+ }
+ #container-content {
+ height: 200%;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a>
+<p id="display"></p>
+<div id="container">
+ <div id="outer1" class="outer-frame">
+ <div id="inner1" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </div>
+ <div id="outer2" class="outer-frame">
+ <div id="inner2" class="inner-frame">
+ <div class="inner-content"></div>
+ </div>
+ </div>
+ <iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe>
+ <iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe>
+<!-- The container-content div ensures 'container' is scrollable, so the
+ optimization that layerizes the primary async-scrollable frame on page
+ load layerizes it rather than its child subframes. -->
+ <div id="container-content"></div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+// Scroll the mouse wheel over |element|.
+function scrollWheelOver(element, waitForScroll, testDriver) {
+ moveMouseAndScrollWheelOver(element, 10, 10, testDriver, waitForScroll);
+}
+
+const DISPLAYPORT_EXPIRY = 100;
+
+// This helper function produces another helper function, which, when invoked,
+// invokes the provided testDriver argument in a setTimeout 0. This is really
+// just useful in cases when there are no paints pending, because then
+// waitForAllPaints will invoke its callback synchronously. If we did
+// waitForAllPaints(testDriver) that might cause reentrancy into the testDriver
+// which is bad. This function works around that.
+function asyncWrapper(testDriver) {
+ return function() {
+ setTimeout(testDriver, 0);
+ };
+}
+
+function* test(testDriver) {
+ // Initially, nothing should be layerized.
+ ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
+ ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
+ ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
+ ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
+ ok(!isLayerized('outer3'), "initially 'outer3' should not be layerized");
+ ok(!isLayerized('inner3'), "initially 'inner3' should not be layerized");
+ ok(!isLayerized('outer4'), "initially 'outer4' should not be layerized");
+ ok(!isLayerized('inner4'), "initially 'inner4' should not be layerized");
+
+ // Scrolling over outer1 should layerize outer1, but not inner1.
+ yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
+ ok(isLayerized('outer1'), "scrolling 'outer1' should cause it to be layerized");
+ ok(!isLayerized('inner1'), "scrolling 'outer1' should not cause 'inner1' to be layerized");
+
+ // Scrolling over inner2 should layerize both outer2 and inner2.
+ yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
+ ok(isLayerized('inner2'), "scrolling 'inner2' should cause it to be layerized");
+ ok(isLayerized('outer2'), "scrolling 'inner2' should also cause 'outer2' to be layerized");
+
+ // The second half of the test repeats the same checks as the first half,
+ // but with an iframe as the outer scrollable frame.
+
+ // Scrolling over outer3 should layerize outer3, but not inner3.
+ yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement, true, testDriver);
+ ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
+ ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");
+
+ // Scrolling over outer4 should layerize both outer4 and inner4.
+ yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'), true, testDriver);
+ ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
+ ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
+
+ // Now we enable displayport expiry, and verify that things are still
+ // layerized as they were before.
+ yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, testDriver);
+ ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
+ ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
+ ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
+ ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
+ ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
+ ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
+ ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
+ ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");
+
+ // Now we trigger a scroll on some of the things still layerized, so that
+ // the displayport expiry gets triggered.
+
+ // Expire displayport with scrolling on outer1
+ yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
+ ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");
+
+ // Expire displayport with scrolling on inner2
+ yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Once the expiry elapses, it will trigger expiry on outer2, so we check
+ // both, one at a time.
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");
+
+ // Scroll on inner3. inner3 isn't layerized, and this will cause it to
+ // get layerized, but it will also trigger displayport expiration for inner3
+ // which will eventually trigger displayport expiration on inner3 and outer3.
+ // Note that the displayport expiration might actually happen before the wheel
+ // input is processed in the compositor (see bug 1246480 comment 3), and so
+ // we make sure not to wait for a scroll event here, since it may never fire.
+ // However, if we do get a scroll event while waiting for the expiry, we need
+ // to restart the expiry timer because the displayport expiry got reset. There's
+ // no good way that I can think of to deterministically avoid doing this.
+ let inner3 = document.getElementById('outer3').contentDocument.getElementById('inner3');
+ yield scrollWheelOver(inner3, false, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ var timerId = setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ var timeoutResetter = function() {
+ ok(true, "Got a scroll event; resetting timer...");
+ clearTimeout(timerId);
+ setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ // by not updating timerId we ensure that this listener resets the timeout
+ // at most once.
+ };
+ inner3.addEventListener('scroll', timeoutResetter, false);
+ yield; // wait for the setTimeout to elapse
+ inner3.removeEventListener('scroll', timeoutResetter, false);
+
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");
+
+ // Scroll outer4 and wait for the expiry. It should NOT get expired because
+ // inner4 is still layerized
+ yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement, true, testDriver);
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+ // Wait for the expiry to elapse
+ yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
+ yield waitForAllPaints(asyncWrapper(testDriver));
+ ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
+ ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
+ SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)
+
+ // Disable smooth scrolling, because it results in long-running scroll
+ // animations that can result in a 'scroll' event triggered by an earlier
+ // wheel event as corresponding to a later wheel event.
+ // Also enable APZ test logging, since we use that data to determine whether
+ // a scroll frame was layerized.
+ pushPrefs([["general.smoothScroll", false],
+ ["apz.displayport_expiry_ms", 0],
+ ["apz.test.logging_enabled", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html b/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
new file mode 100644
index 000000000..3349ef1ab
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
@@ -0,0 +1,541 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling flattened inactive frames</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+p {
+ width:200px;
+ height:200px;
+ border:solid 1px black;
+ overflow:auto;
+}
+</style>
+</head>
+<body>
+<div id="iframe-body" style="overflow: auto; height: 1000px">
+<hr>
+<hr>
+<hr>
+<p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p id="subframe">
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p><p>
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+
+</p>
+</div>
+<script clss="testbody" type="text/javascript;version=1.7">
+function ScrollTops() {
+ this.outerScrollTop = document.getElementById('iframe-body').scrollTop;
+ this.innerScrollTop = document.getElementById('subframe').scrollTop;
+}
+
+var DefaultEvent = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0, deltaY: 1,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+};
+
+function test() {
+ var subframe = document.getElementById('subframe');
+ var oldpos = new ScrollTops();
+ sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
+ ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
+ doOuterScroll(subframe, newpos);
+ });
+}
+
+function doOuterScroll(subframe, oldpos) {
+ var outer = document.getElementById('iframe-body');
+ sendWheelAndPaint(outer, 20, 5, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop != newpos.outerScrollTop, "viewport should have scrolled");
+ ok(oldpos.innerScrollTop == newpos.innerScrollTop, "subframe should not have scrolled");
+ doInnerScrollAgain(subframe, newpos);
+ });
+}
+
+function doInnerScrollAgain(subframe, oldpos) {
+ sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
+ var newpos = new ScrollTops();
+ ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
+ ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 0],
+ ['mousewheel.transaction.ignoremovedelay', 0]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html b/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
new file mode 100644
index 000000000..51e16aab9
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling flattened inactive frames</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="container" style="height: 300px; width: 600px; overflow: auto; background: yellow">
+ <div id="outer" style="height: 400px; width: 500px; overflow: auto; background: black">
+ <div id="inner" style="mix-blend-mode: screen; height: 800px; overflow: auto; background: purple">
+ </div>
+ </div>
+</div>
+<script class="testbody" type="text/javascript;version=1.7">
+function test() {
+ var container = document.getElementById('container');
+ var outer = document.getElementById('outer');
+ var inner = document.getElementById('inner');
+ var outerScrollTop = outer.scrollTop;
+ var containerScrollTop = container.scrollTop;
+ var event = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0,
+ deltaY: 10,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 10,
+ };
+ sendWheelAndPaint(inner, 20, 30, event, function () {
+ ok(container.scrollTop == containerScrollTop, "container scrollframe should not have scrolled");
+ ok(outer.scrollTop > outerScrollTop, "nested scrollframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 1000000]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html b/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
new file mode 100644
index 000000000..4d9da8c2c
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test scrolling subframe scrollbars</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+p {
+ width:200px;
+ height:200px;
+ border:solid 1px black;
+}
+</style>
+</head>
+<body>
+<p id="subframe">
+1 <br>
+2 <br>
+3 <br>
+4 <br>
+5 <br>
+6 <br>
+7 <br>
+8 <br>
+9 <br>
+10 <br>
+11 <br>
+12 <br>
+13 <br>
+14 <br>
+15 <br>
+16 <br>
+17 <br>
+18 <br>
+19 <br>
+20 <br>
+21 <br>
+22 <br>
+23 <br>
+24 <br>
+25 <br>
+26 <br>
+27 <br>
+28 <br>
+29 <br>
+30 <br>
+31 <br>
+32 <br>
+33 <br>
+34 <br>
+35 <br>
+36 <br>
+37 <br>
+38 <br>
+39 <br>
+40 <br>
+</p>
+<script clss="testbody" type="text/javascript;version=1.7">
+
+var DefaultEvent = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0, deltaY: 1,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+};
+
+var ScrollbarWidth = 0;
+
+function test() {
+ var subframe = document.getElementById('subframe');
+ var oldClientWidth = subframe.clientWidth;
+
+ subframe.style.overflow = 'auto';
+ subframe.getBoundingClientRect();
+
+ waitForAllPaintsFlushed(function () {
+ ScrollbarWidth = oldClientWidth - subframe.clientWidth;
+ if (!ScrollbarWidth) {
+ // Probably we have overlay scrollbars - abort the test.
+ ok(true, "overlay scrollbars - skipping test");
+ SimpleTest.finish();
+ return;
+ }
+
+ ok(subframe.scrollHeight > subframe.clientHeight, "subframe should have scrollable content");
+ testScrolling(subframe);
+ });
+}
+
+function testScrolling(subframe) {
+ // Send a wheel event roughly to where we think the trackbar is. We pick a
+ // point at the bottom, in the middle of the trackbar, where the slider is
+ // unlikely to be (since it starts at the top).
+ var posX = subframe.clientWidth + (ScrollbarWidth / 2);
+ var posY = subframe.clientHeight - 20;
+
+ var oldScrollTop = subframe.scrollTop;
+
+ sendWheelAndPaint(subframe, posX, posY, DefaultEvent, function () {
+ ok(subframe.scrollTop > oldScrollTop, "subframe should have scrolled");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+pushPrefs([['general.smoothScroll', false],
+ ['mousewheel.transaction.timeout', 0],
+ ['mousewheel.transaction.ignoremovedelay', 0]])
+.then(waitUntilApzStable)
+.then(test);
+
+</script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_smoothness.html b/gfx/layers/apz/test/mochitest/test_smoothness.html
new file mode 100644
index 000000000..88373957a
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_smoothness.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+ <title>Test Frame Uniformity While Scrolling</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+
+ <style>
+ #content {
+ height: 5000px;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ </style>
+ <script type="text/javascript">
+ var scrollEvents = 100;
+ var i = 0;
+ var testPref = "gfx.vsync.collect-scroll-transforms";
+ // Scroll points
+ var x = 100;
+ var y = 150;
+
+ SimpleTest.waitForExplicitFinish();
+ var utils = _getDOMWindowUtils(window);
+
+ function sendScrollEvent(aRafTimestamp) {
+ var scrollDiv = document.getElementById("content");
+
+ if (i < scrollEvents) {
+ i++;
+ // Scroll diff
+ var dx = 0;
+ var dy = -10; // Negative to scroll down
+ synthesizeNativeWheelAndWaitForWheelEvent(scrollDiv, x, y, dx, dy);
+ window.requestAnimationFrame(sendScrollEvent);
+ } else {
+ // Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5
+ // w/o silk + e10s + apz, I get up to 7. Lower is better.
+ // Windows, I get ~3. Values are not valid w/o hardware vsync
+ var uniformities = _getDOMWindowUtils().getFrameUniformityTestData();
+ for (var j = 0; j < uniformities.layerUniformities.length; j++) {
+ var layerResult = uniformities.layerUniformities[j];
+ var layerAddr = layerResult.layerAddress;
+ var uniformity = layerResult.frameUniformity;
+ var msg = "Layer: " + layerAddr.toString(16) + " Uniformity: " + uniformity;
+ SimpleTest.ok((uniformity >= 0) && (uniformity < 4.0), msg);
+ }
+ SimpleTest.finish();
+ }
+ }
+
+ function startTest() {
+ window.requestAnimationFrame(sendScrollEvent);
+ }
+
+ window.onload = function() {
+ var apzEnabled = SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled");
+ if (!apzEnabled) {
+ SimpleTest.ok(true, "APZ not enabled, skipping test");
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv({
+ "set" : [
+ [testPref, true]
+ ]
+ }, startTest);
+ }
+ </script>
+</head>
+
+<body>
+ <div id="content">
+ </div>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html b/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html
new file mode 100644
index 000000000..913269a67
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1203140
+-->
+<head>
+ <title>Test for Bug 1203140</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203140">Mozilla Bug 1203140</a>
+<p id="display"></p>
+<div id="content" style="overflow-y:scroll; height: 400px">
+ <p>The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.</p>
+ <div id="box" style="width: 200px; height: 200px; background-color: blue"></div>
+ <div style="height: 1000px; width: 10px">Div to make 'content' scrollable</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes
+
+function takeSnapshots(e) {
+ // Grab some snapshots, and make sure some of them are different (i.e. check
+ // the page is scrolling in the compositor, concurrently with this wheel
+ // listener running).
+ // Note that we want this function to take less time than the content response
+ // timeout, otherwise the scrolling will start even if we haven't returned,
+ // and that would invalidate purpose of the test.
+ var start = Date.now();
+ var lastSnapshot = null;
+ var success = false;
+
+ // Get the position of the 'content' div relative to the screen
+ var rect = rectRelativeToScreen(document.getElementById('content'));
+
+ for (var i = 0; i < 10; i++) {
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(16);
+ var snapshot = getSnapshot(rect);
+ //dump("Took snapshot " + snapshot + "\n"); // this might help with debugging
+
+ if (lastSnapshot && lastSnapshot != snapshot) {
+ ok(true, "Found some different pixels in snapshot " + i + " compared to previous");
+ success = true;
+ }
+ lastSnapshot = snapshot;
+ }
+ ok(success, "Found some snapshots that were different");
+ ok((Date.now() - start) < kResponseTimeoutMs, "Snapshotting ran quickly enough");
+
+ // Until now, no scroll events will have been dispatched to content. That's
+ // because scroll events are dispatched on the main thread, which we've been
+ // hogging with the code above. At this point we restore the normal refresh
+ // behaviour and let the main thread go back to C++ code, so the scroll events
+ // fire and we unwind from the main test continuation.
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+}
+
+function* test(testDriver) {
+ var box = document.getElementById('box');
+
+ // Ensure the div is layerized by scrolling it
+ yield moveMouseAndScrollWheelOver(box, 10, 10, testDriver);
+
+ box.addEventListener('touchstart', function(e) {
+ ok(false, "This should never be run");
+ }, false);
+ box.addEventListener('wheel', takeSnapshots, { capture: false, passive: true });
+
+ // Let the event regions and layerization propagate to the APZ
+ yield waitForAllPaints(function() {
+ flushApzRepaints(testDriver);
+ });
+
+ // Take over control of the refresh driver and compositor
+ var utils = SpecialPowers.DOMWindowUtils;
+ utils.advanceTimeAndRefresh(0);
+
+ // Trigger an APZ scroll using a wheel event. If APZ is waiting for a
+ // content response, it will wait for takeSnapshots to finish running before
+ // it starts scrolling, which will cause the checks in takeSnapshots to fail.
+ yield synthesizeNativeMouseMoveAndWaitForMoveEvent(box, 10, 10, testDriver);
+ yield synthesizeNativeWheelAndWaitForScrollEvent(box, 10, 10, 0, -50, testDriver);
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ // Disable touch events, so that APZ knows not to wait for touch listeners.
+ // Also explicitly set the content response timeout, so we know how long it
+ // is (see comment in takeSnapshots).
+ // Finally, enable smooth scrolling, so that the wheel-scroll we do as part
+ // of the test triggers an APZ animation rather than doing an instant scroll.
+ // Note that this pref doesn't work for the synthesized wheel events on OS X,
+ // those are hard-coded to be instant scrolls.
+ pushPrefs([["dom.w3c_touch_events.enabled", 0],
+ ["apz.content_response_timeout", kResponseTimeoutMs],
+ ["general.smoothscroll", true]])
+ .then(waitUntilApzStable)
+ .then(runContinuation(test))
+ .then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_wheel_scroll.html b/gfx/layers/apz/test/mochitest/test_wheel_scroll.html
new file mode 100644
index 000000000..479478d42
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_wheel_scroll.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
+-->
+<head>
+ <title>Test for Bug 1013412</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #content {
+ height: 800px;
+ overflow: scroll;
+ }
+
+ #scroller {
+ height: 2000px;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+
+ #scrollbox {
+ margin-top: 200px;
+ width: 500px;
+ height: 500px;
+ border-radius: 250px;
+ box-shadow: inset 0 0 0 60px #555;
+ background: #777;
+ }
+
+ #circle {
+ position: relative;
+ left: 240px;
+ top: 20px;
+ border: 10px solid white;
+ border-radius: 10px;
+ width: 0px;
+ height: 0px;
+ transform-origin: 10px 230px;
+ will-change: transform;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
+<p id="display"></p>
+<div id="content">
+ <p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
+ <div id="scroller">
+ <div id="scrollbox">
+ <div id="circle"></div>
+ </div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+var rotation = 0;
+var rotationAdjusted = false;
+
+var incrementForMode = function (mode) {
+ switch (mode) {
+ case WheelEvent.DOM_DELTA_PIXEL: return 1;
+ case WheelEvent.DOM_DELTA_LINE: return 15;
+ case WheelEvent.DOM_DELTA_PAGE: return 400;
+ }
+ return 0;
+};
+
+document.getElementById("scrollbox").addEventListener("wheel", function (e) {
+ rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
+ document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
+ rotationAdjusted = true;
+ e.preventDefault();
+});
+
+function* test(testDriver) {
+ var content = document.getElementById('content');
+ for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
+ yield synthesizeNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5, testDriver);
+ }
+ var scrollbox = document.getElementById('scrollbox');
+ is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
+ is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
+ is(rotationAdjusted, true, "The rotation should have been adjusted");
+}
+
+SimpleTest.testInChaosMode();
+SimpleTest.waitForExplicitFinish();
+
+// If we allow smooth scrolling the "smooth" scrolling may cause the page to
+// glide past the scrollbox (which is supposed to stop the scrolling) and so
+// we might end up at the bottom of the page.
+pushPrefs([["general.smoothScroll", false]])
+.then(waitUntilApzStable)
+.then(runContinuation(test))
+.then(SimpleTest.finish);
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/test_wheel_transactions.html b/gfx/layers/apz/test/mochitest/test_wheel_transactions.html
new file mode 100644
index 000000000..e00e992cd
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_wheel_transactions.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
+-->
+<head>
+ <title>Test for Bug 1175585</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #outer-frame {
+ height: 500px;
+ overflow: scroll;
+ background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
+ }
+ #inner-frame {
+ margin-top: 25%;
+ height: 200%;
+ width: 75%;
+ overflow: scroll;
+ }
+ #inner-content {
+ height: 200%;
+ width: 200%;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175585">APZ wheel transactions test</a>
+<p id="display"></p>
+<div id="outer-frame">
+ <div id="inner-frame">
+ <div id="inner-content"></div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+function scrollWheelOver(element, deltaY, testDriver) {
+ synthesizeNativeWheelAndWaitForScrollEvent(element, 10, 10, 0, deltaY, testDriver);
+}
+
+function* test(testDriver) {
+ var outer = document.getElementById('outer-frame');
+ var inner = document.getElementById('inner-frame');
+ var innerContent = document.getElementById('inner-content');
+
+ // Register a wheel event listener that records the target of
+ // the last wheel event, so that we can make assertions about it.
+ var lastWheelTarget;
+ var wheelTargetRecorder = function(e) { lastWheelTarget = e.target; };
+ window.addEventListener("wheel", wheelTargetRecorder);
+
+ // Scroll |outer| to the bottom.
+ while (outer.scrollTop < outer.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ }
+
+ // Verify that this has brought |inner| under the wheel.
+ is(lastWheelTarget, innerContent, "'inner-content' should have been brought under the wheel");
+ window.removeEventListener("wheel", wheelTargetRecorder);
+
+ // Immediately after, scroll it back up a bit.
+ yield scrollWheelOver(outer, 10, testDriver);
+
+ // Check that it was |outer| that scrolled back, and |inner| didn't
+ // scroll at all, as all the above scrolls should be in the same
+ // transaction.
+ ok(outer.scrollTop < outer.scrollTopMax, "'outer' should have scrolled back a bit");
+ is(inner.scrollTop, 0, "'inner' should not have scrolled");
+
+ // The next part of the test is related to the transaction timeout.
+ // Turn it down a bit so waiting for the timeout to elapse doesn't
+ // slow down the test harness too much.
+ var timeout = 5;
+ yield SpecialPowers.pushPrefEnv({"set": [["mousewheel.transaction.timeout", timeout]]}, testDriver);
+ SimpleTest.requestFlakyTimeout("we are testing code that measures actual elapsed time between two events");
+
+ // Scroll up a bit more. It's still |outer| scrolling because
+ // |inner| is still scrolled all the way to the top.
+ yield scrollWheelOver(outer, 10, testDriver);
+
+ // Wait for the transaction timeout to elapse.
+ // timeout * 5 is used to make it less likely that the timeout is less than
+ // the system timestamp resolution
+ yield window.setTimeout(testDriver, timeout * 5);
+
+ // Now scroll down. The transaction having timed out, the event
+ // should pick up a new target, and that should be |inner|.
+ yield scrollWheelOver(outer, -10, testDriver);
+ ok(inner.scrollTop > 0, "'inner' should have been scrolled");
+
+ // Finally, test scroll handoff after a timeout.
+
+ // Continue scrolling |inner| down to the bottom.
+ var prevScrollTop = inner.scrollTop;
+ while (inner.scrollTop < inner.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ // Avoid a failure getting us into an infinite loop.
+ ok(inner.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
+ prevScrollTop = inner.scrollTop;
+ }
+
+ // Wait for the transaction timeout to elapse.
+ // timeout * 5 is used to make it less likely that the timeout is less than
+ // the system timestamp resolution
+ yield window.setTimeout(testDriver, timeout * 5);
+
+ // Continued downward scrolling should scroll |outer| to the bottom.
+ prevScrollTop = outer.scrollTop;
+ while (outer.scrollTop < outer.scrollTopMax) {
+ yield scrollWheelOver(outer, -10, testDriver);
+ // Avoid a failure getting us into an infinite loop.
+ ok(outer.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
+ prevScrollTop = outer.scrollTop;
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// Disable smooth scrolling because it makes the test flaky (we don't have a good
+// way of detecting when the scrolling is finished).
+pushPrefs([["general.smoothScroll", false]])
+.then(waitUntilApzStable)
+.then(runContinuation(test))
+.then(SimpleTest.finish);
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
new file mode 100644
index 000000000..0e2698b86
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
new file mode 100644
index 000000000..ee2524a16
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(-450,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
new file mode 100644
index 000000000..2f3d94639
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="-449" reftest-async-scroll-y="0"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(-1,0) is to activate the right arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(-1,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-h.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-h.html
new file mode 100644
index 000000000..1eca19246
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-h.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="449" reftest-async-scroll-y="0"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,0) is to activate the left arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,0); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 10px; background: white"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
new file mode 100644
index 000000000..9ac5485bc
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
new file mode 100644
index 000000000..94fb501ba
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
new file mode 100644
index 000000000..9a2eb8818
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-v.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-v.html
new file mode 100644
index 000000000..56fe23c28
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-v.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 10px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html
new file mode 100644
index 000000000..564697b37
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,8000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html
new file mode 100644
index 000000000..78cb0332c
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<body onload="scrollTo(-450,8000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
new file mode 100644
index 000000000..397d1cf9b
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="-440" reftest-async-scroll-y="7999"><head>
+<meta name="viewport" content="width=device-width">
+<style> html { direction: rtl; } </style>
+</head>
+<!-- Doing scrollTo(-10,1) is to activate the right/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(-10,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html
new file mode 100644
index 000000000..a1d1527dd
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="449" reftest-async-scroll-y="7999"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html
new file mode 100644
index 000000000..5ed970f76
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html
new file mode 100644
index 000000000..09be51a79
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="224" reftest-async-scroll-y="4999"
+ reftest-async-zoom="2.0"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 4500px; height: 10000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html
new file mode 100644
index 000000000..5ed970f76
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 9000px; height: 20000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html
new file mode 100644
index 000000000..abe822c21
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait"
+ reftest-async-scroll
+ reftest-async-scroll-x="899" reftest-async-scroll-y="19999"
+ reftest-async-zoom="0.5"><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
+ for non-overlay scrollbar environments -->
+<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
+<div style="width: 18000px; height: 40000px; background: white;"></div>
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html b/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html
new file mode 100644
index 000000000..3db9f2969
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html
@@ -0,0 +1,27 @@
+<html>
+<script>
+ function run() {
+ document.body.classList.toggle('noscroll');
+ document.getElementById('spacer').style.height = '100%';
+ // Scroll to the very end, including any fractional pixels
+ document.body.scrollTop = document.body.scrollTopMax + 1;
+ }
+</script>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background-color: green;
+ }
+
+ .noscroll {
+ overflow: hidden;
+ height: 100%;
+ }
+</style>
+<body onload="run()">
+ <div id="spacer" style="height: 5000px">
+ This is the top of the page.
+ </div>
+ This is the bottom of the page.
+</body>
diff --git a/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html b/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html
new file mode 100644
index 000000000..479363f3f
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html
@@ -0,0 +1,53 @@
+<html class="reftest-wait">
+<!--
+For bug 1266833; syncing the scroll offset to APZ properly when the scroll
+position is clamped to a smaller value during a frame reconstruction.
+-->
+<script>
+ function run() {
+ document.body.scrollTop = document.body.scrollTopMax;
+
+ // Let the scroll position propagate to APZ before we do the frame
+ // reconstruction. Ideally we would wait for flushApzRepaints here but
+ // we don't have access to DOMWindowUtils in a reftest, so we just wait
+ // 100ms to approximate it. With bug 1266833 fixed, this test should
+ // never fail regardless of what this timeout value is.
+ setTimeout(frameReconstruction, 100);
+ }
+
+ function frameReconstruction() {
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '100%';
+ document.documentElement.classList.remove('reftest-wait');
+ }
+</script>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background-color: green;
+ }
+
+ .noscroll {
+ overflow: hidden;
+ height: 100%;
+ }
+
+ /* Toggling this on and off triggers a frame reconstruction on the <body> */
+ html.reconstruct-body::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+</style>
+<body onload="setTimeout(run, 0)">
+ <div id="spacer" style="height: 5000px">
+ This is the top of the page.
+ </div>
+ This is the bottom of the page.
+</body>
diff --git a/gfx/layers/apz/test/reftest/initial-scale-1-ref.html b/gfx/layers/apz/test/reftest/initial-scale-1-ref.html
new file mode 100644
index 000000000..dc99712d3
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/initial-scale-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="width=device-width">
+</head>
+<body>
+This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
+with something a little more sane.
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/initial-scale-1.html b/gfx/layers/apz/test/reftest/initial-scale-1.html
new file mode 100644
index 000000000..45bed0809
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/initial-scale-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="initial-scale=0; width=device-width">
+</head>
+<body>
+This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
+with something a little more sane.
+</body>
+</html>
+
diff --git a/gfx/layers/apz/test/reftest/reftest-stylo.list b/gfx/layers/apz/test/reftest/reftest-stylo.list
new file mode 100644
index 000000000..cc2c76827
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/reftest-stylo.list
@@ -0,0 +1,20 @@
+# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
+# The following tests test the async positioning of the scrollbars.
+# Basic root-frame scrollbar with async scrolling
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v.html async-scrollbar-1-v.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-h.html async-scrollbar-1-h.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-vh.html async-scrollbar-1-vh.html
+skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl.html
+skip-if(!asyncPan) fuzzy-if(Android,13,8) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl.html
+skip-if(!asyncPan) fuzzy-if(Android,8,10) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl.html
+
+# Different async zoom levels. Since the scrollthumb gets async-scaled in the
+# compositor, the border-radius ends of the scrollthumb are going to be a little
+# off, hence the fuzzy-if clauses.
+skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1.html
+skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2.html
+
+# Meta-viewport tag support
+skip-if(!asyncZoom) == initial-scale-1.html initial-scale-1.html
+
+skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping.html
diff --git a/gfx/layers/apz/test/reftest/reftest.list b/gfx/layers/apz/test/reftest/reftest.list
new file mode 100644
index 000000000..4ab29420c
--- /dev/null
+++ b/gfx/layers/apz/test/reftest/reftest.list
@@ -0,0 +1,19 @@
+# The following tests test the async positioning of the scrollbars.
+# Basic root-frame scrollbar with async scrolling
+fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html
+fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
+fuzzy-if(Android,3,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
+fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
+fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
+fuzzy-if(Android,3,7) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
+
+# Different async zoom levels. Since the scrollthumb gets async-scaled in the
+# compositor, the border-radius ends of the scrollthumb are going to be a little
+# off, hence the fuzzy-if clauses.
+fuzzy-if(Android,54,18) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
+fuzzy-if(Android,45,21) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
+
+# Meta-viewport tag support
+skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html
+
+skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping-ref.html
diff --git a/gfx/layers/apz/testutil/APZTestData.cpp b/gfx/layers/apz/testutil/APZTestData.cpp
new file mode 100644
index 000000000..3c9440b64
--- /dev/null
+++ b/gfx/layers/apz/testutil/APZTestData.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZTestData.h"
+#include "mozilla/dom/APZTestDataBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace layers {
+
+struct APZTestDataToJSConverter {
+ template <typename Key, typename Value, typename KeyValuePair>
+ static void ConvertMap(const std::map<Key, Value>& aFrom,
+ dom::Sequence<KeyValuePair>& aOutTo,
+ void (*aElementConverter)(const Key&, const Value&, KeyValuePair&)) {
+ for (auto it = aFrom.begin(); it != aFrom.end(); ++it) {
+ aOutTo.AppendElement(fallible);
+ aElementConverter(it->first, it->second, aOutTo.LastElement());
+ }
+ }
+
+ static void ConvertAPZTestData(const APZTestData& aFrom,
+ dom::APZTestData& aOutTo) {
+ ConvertMap(aFrom.mPaints, aOutTo.mPaints.Construct(), ConvertBucket);
+ ConvertMap(aFrom.mRepaintRequests, aOutTo.mRepaintRequests.Construct(), ConvertBucket);
+ }
+
+ static void ConvertBucket(const SequenceNumber& aKey,
+ const APZTestData::Bucket& aValue,
+ dom::APZBucket& aOutKeyValuePair) {
+ aOutKeyValuePair.mSequenceNumber.Construct() = aKey;
+ ConvertMap(aValue, aOutKeyValuePair.mScrollFrames.Construct(), ConvertScrollFrameData);
+ }
+
+ static void ConvertScrollFrameData(const APZTestData::ViewID& aKey,
+ const APZTestData::ScrollFrameData& aValue,
+ dom::ScrollFrameData& aOutKeyValuePair) {
+ aOutKeyValuePair.mScrollId.Construct() = aKey;
+ ConvertMap(aValue, aOutKeyValuePair.mEntries.Construct(), ConvertEntry);
+ }
+
+ static void ConvertEntry(const std::string& aKey,
+ const std::string& aValue,
+ dom::ScrollFrameDataEntry& aOutKeyValuePair) {
+ ConvertString(aKey, aOutKeyValuePair.mKey.Construct());
+ ConvertString(aValue, aOutKeyValuePair.mValue.Construct());
+ }
+
+ static void ConvertString(const std::string& aFrom, nsString& aOutTo) {
+ aOutTo = NS_ConvertUTF8toUTF16(aFrom.c_str(), aFrom.size());
+ }
+};
+
+bool
+APZTestData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const
+{
+ dom::APZTestData result;
+ APZTestDataToJSConverter::ConvertAPZTestData(*this, result);
+ return dom::ToJSValue(aContext, result, aOutValue);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/testutil/APZTestData.h b/gfx/layers/apz/testutil/APZTestData.h
new file mode 100644
index 000000000..c0cc35b5a
--- /dev/null
+++ b/gfx/layers/apz/testutil/APZTestData.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZTestData_h
+#define mozilla_layers_APZTestData_h
+
+#include <map>
+
+#include "FrameMetrics.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "mozilla/Assertions.h" // for MOZ_ASSERT
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/ToString.h" // for ToString
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace layers {
+
+typedef uint32_t SequenceNumber;
+
+/**
+ * This structure is used to store information logged by various gecko
+ * components for later examination by test code.
+ * It consists of a bucket for every paint (initiated on the client side),
+ * and every repaint request (initiated on the compositor side by
+ * AsyncPanZoomController::RequestContentRepait), which are identified by
+ * sequence numbers, and within that, a set of arbitrary string key/value
+ * pairs for every scrollable frame, identified by a scroll id.
+ * There are two instances of this data structure for every content thread:
+ * one on the client side and one of the compositor side.
+ */
+// TODO(botond):
+// - Improve warnings/asserts.
+// - Add ability to associate a repaint request triggered during a layers update
+// with the sequence number of the paint that caused the layers update.
+class APZTestData {
+ typedef FrameMetrics::ViewID ViewID;
+ friend struct IPC::ParamTraits<APZTestData>;
+ friend struct APZTestDataToJSConverter;
+public:
+ void StartNewPaint(SequenceNumber aSequenceNumber) {
+ // We should never get more than one paint with the same sequence number.
+ MOZ_ASSERT(mPaints.find(aSequenceNumber) == mPaints.end());
+ mPaints.insert(DataStore::value_type(aSequenceNumber, Bucket()));
+ }
+ void LogTestDataForPaint(SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ LogTestDataImpl(mPaints, aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ void StartNewRepaintRequest(SequenceNumber aSequenceNumber) {
+ typedef std::pair<DataStore::iterator, bool> InsertResultT;
+ DebugOnly<InsertResultT> insertResult = mRepaintRequests.insert(DataStore::value_type(aSequenceNumber, Bucket()));
+ MOZ_ASSERT(((InsertResultT&)insertResult).second, "Already have a repaint request with this sequence number");
+ }
+ void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ LogTestDataImpl(mRepaintRequests, aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Convert this object to a JS representation.
+ bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const;
+
+ // Use dummy derived structures wrapping the tyepdefs to work around a type
+ // name length limit in MSVC.
+ typedef std::map<std::string, std::string> ScrollFrameDataBase;
+ struct ScrollFrameData : ScrollFrameDataBase {};
+ typedef std::map<ViewID, ScrollFrameData> BucketBase;
+ struct Bucket : BucketBase {};
+ typedef std::map<SequenceNumber, Bucket> DataStoreBase;
+ struct DataStore : DataStoreBase {};
+private:
+ DataStore mPaints;
+ DataStore mRepaintRequests;
+
+ void LogTestDataImpl(DataStore& aDataStore,
+ SequenceNumber aSequenceNumber,
+ ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) {
+ auto bucketIterator = aDataStore.find(aSequenceNumber);
+ if (bucketIterator == aDataStore.end()) {
+ MOZ_ASSERT(false, "LogTestDataImpl called with nonexistent sequence number");
+ return;
+ }
+ Bucket& bucket = bucketIterator->second;
+ ScrollFrameData& scrollFrameData = bucket[aScrollId]; // create if doesn't exist
+ MOZ_ASSERT(scrollFrameData.find(aKey) == scrollFrameData.end()
+ || scrollFrameData[aKey] == aValue);
+ scrollFrameData.insert(ScrollFrameData::value_type(aKey, aValue));
+ }
+};
+
+// A helper class for logging data for a paint.
+class APZPaintLogHelper {
+public:
+ APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber)
+ : mTestData(aTestData),
+ mPaintSequenceNumber(aPaintSequenceNumber)
+ {}
+
+ template <typename Value>
+ void LogTestData(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const Value& aValue) const {
+ if (mTestData) { // avoid stringifying if mTestData == nullptr
+ LogTestData(aScrollId, aKey, ToString(aValue));
+ }
+ }
+
+ void LogTestData(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue) const {
+ if (mTestData) {
+ mTestData->LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
+ }
+ }
+private:
+ APZTestData* mTestData;
+ SequenceNumber mPaintSequenceNumber;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData>
+{
+ typedef mozilla::layers::APZTestData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mPaints);
+ WriteParam(aMsg, aParam.mRepaintRequests);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mPaints) &&
+ ReadParam(aMsg, aIter, &aResult->mRepaintRequests));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::ScrollFrameData>
+ : ParamTraits<mozilla::layers::APZTestData::ScrollFrameDataBase> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::Bucket>
+ : ParamTraits<mozilla::layers::APZTestData::BucketBase> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::DataStore>
+ : ParamTraits<mozilla::layers::APZTestData::DataStoreBase> {};
+
+} // namespace IPC
+
+
+#endif /* mozilla_layers_APZTestData_h */
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp
new file mode 100644
index 000000000..3f33a59e4
--- /dev/null
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -0,0 +1,942 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZCCallbackHelper.h"
+
+#include "TouchActionHelper.h"
+#include "gfxPlatform.h" // For gfxPlatform::UseTiling
+#include "gfxPrefs.h"
+#include "LayersLogging.h" // For Stringify
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/TouchEvents.h"
+#include "nsContentUtils.h"
+#include "nsContainerFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsRefreshDriver.h"
+#include "nsString.h"
+#include "nsView.h"
+#include "Layers.h"
+
+#define APZCCH_LOG(...)
+// #define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+using dom::TabParent;
+
+uint64_t APZCCallbackHelper::sLastTargetAPZCNotificationInputBlock = uint64_t(-1);
+
+void
+APZCCallbackHelper::AdjustDisplayPortForScrollDelta(
+ mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset)
+{
+ // Correct the display-port by the difference between the requested scroll
+ // offset and the resulting scroll offset after setting the requested value.
+ ScreenPoint shift =
+ (aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
+ aFrameMetrics.DisplayportPixelsPerCSSPixel();
+ ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
+ margins.left -= shift.x;
+ margins.right += shift.x;
+ margins.top -= shift.y;
+ margins.bottom += shift.y;
+ aFrameMetrics.SetDisplayPortMargins(margins);
+}
+
+static void
+RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
+{
+ ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
+ margins.right = margins.left = margins.LeftRight() / 2;
+ margins.top = margins.bottom = margins.TopBottom() / 2;
+ aFrameMetrics.SetDisplayPortMargins(margins);
+}
+
+static CSSPoint
+ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut)
+{
+ aSuccessOut = false;
+ CSSPoint targetScrollPosition = aMetrics.GetScrollOffset();
+
+ if (!aFrame) {
+ return targetScrollPosition;
+ }
+
+ CSSPoint geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
+
+ // If the repaint request was triggered due to a previous main-thread scroll
+ // offset update sent to the APZ, then we don't need to do another scroll here
+ // and we can just return.
+ if (!aMetrics.GetScrollOffsetUpdated()) {
+ return geckoScrollPosition;
+ }
+
+ // If the frame is overflow:hidden on a particular axis, we don't want to allow
+ // user-driven scroll on that axis. Simply set the scroll position on that axis
+ // to whatever it already is. Note that this will leave the APZ's async scroll
+ // position out of sync with the gecko scroll position, but APZ can deal with that
+ // (by design). Note also that when we run into this case, even if both axes
+ // have overflow:hidden, we want to set aSuccessOut to true, so that the displayport
+ // follows the async scroll position rather than the gecko scroll position.
+ if (aFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
+ targetScrollPosition.y = geckoScrollPosition.y;
+ }
+ if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
+ targetScrollPosition.x = geckoScrollPosition.x;
+ }
+
+ // If the scrollable frame is currently in the middle of an async or smooth
+ // scroll then we don't want to interrupt it (see bug 961280).
+ // Also if the scrollable frame got a scroll request from a higher priority origin
+ // since the last layers update, then we don't want to push our scroll request
+ // because we'll clobber that one, which is bad.
+ bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
+ if (!scrollInProgress) {
+ aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
+ geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
+ aSuccessOut = true;
+ }
+ // Return the final scroll position after setting it so that anything that relies
+ // on it can have an accurate value. Note that even if we set it above re-querying it
+ // is a good idea because it may have gotten clamped or rounded.
+ return geckoScrollPosition;
+}
+
+/**
+ * Scroll the scroll frame associated with |aContent| to the scroll position
+ * requested in |aMetrics|.
+ * The scroll offset in |aMetrics| is updated to reflect the actual scroll
+ * position.
+ * The displayport stored in |aMetrics| and the callback-transform stored on
+ * the content are updated to reflect any difference between the requested
+ * and actual scroll positions.
+ */
+static void
+ScrollFrame(nsIContent* aContent,
+ FrameMetrics& aMetrics)
+{
+ // Scroll the window to the desired spot
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+ if (sf) {
+ sf->ResetScrollInfoIfGeneration(aMetrics.GetScrollGeneration());
+ sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer());
+ }
+ bool scrollUpdated = false;
+ CSSPoint apzScrollOffset = aMetrics.GetScrollOffset();
+ CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics, scrollUpdated);
+
+ if (scrollUpdated) {
+ if (aMetrics.IsScrollInfoLayer()) {
+ // In cases where the APZ scroll offset is different from the content scroll
+ // offset, we want to interpret the margins as relative to the APZ scroll
+ // offset except when the frame is not scrollable by APZ. Therefore, if the
+ // layer is a scroll info layer, we leave the margins as-is and they will
+ // be interpreted as relative to the content scroll offset.
+ if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
+ frame->SchedulePaint();
+ }
+ } else {
+ // Correct the display port due to the difference between mScrollOffset and the
+ // actual scroll offset.
+ APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
+ }
+ } else {
+ // For whatever reason we couldn't update the scroll offset on the scroll frame,
+ // which means the data APZ used for its displayport calculation is stale. Fall
+ // back to a sane default behaviour. Note that we don't tile-align the recentered
+ // displayport because tile-alignment depends on the scroll position, and the
+ // scroll position here is out of our control. See bug 966507 comment 21 for a
+ // more detailed explanation.
+ RecenterDisplayPort(aMetrics);
+ }
+
+ aMetrics.SetScrollOffset(actualScrollOffset);
+
+ // APZ transforms inputs assuming we applied the exact scroll offset it
+ // requested (|apzScrollOffset|). Since we may not have, record the difference
+ // between what APZ asked for and what we actually applied, and apply it to
+ // input events to compensate.
+ // Note that if the main-thread had a change in its scroll position, we don't
+ // want to record that difference here, because it can be large and throw off
+ // input events by a large amount. It is also going to be transient, because
+ // any main-thread scroll position change will be synced to APZ and we will
+ // get another repaint request when APZ confirms. In the interval while this
+ // is happening we can just leave the callback transform as it was.
+ bool mainThreadScrollChanged =
+ sf && sf->CurrentScrollGeneration() != aMetrics.GetScrollGeneration() && nsLayoutUtils::CanScrollOriginClobberApz(sf->LastScrollOrigin());
+ if (aContent && !mainThreadScrollChanged) {
+ CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset;
+ aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta),
+ nsINode::DeleteProperty<CSSPoint>);
+ }
+}
+
+static void
+SetDisplayPortMargins(nsIPresShell* aPresShell,
+ nsIContent* aContent,
+ const FrameMetrics& aMetrics)
+{
+ if (!aContent) {
+ return;
+ }
+
+ bool hadDisplayPort = nsLayoutUtils::HasDisplayPort(aContent);
+ ScreenMargin margins = aMetrics.GetDisplayPortMargins();
+ nsLayoutUtils::SetDisplayPortMargins(aContent, aPresShell, margins, 0);
+ if (!hadDisplayPort) {
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ aContent->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::Repaint);
+ }
+
+ CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels();
+ nsRect base(0, 0,
+ baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
+ baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
+ nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
+}
+
+static already_AddRefed<nsIPresShell>
+GetPresShell(const nsIContent* aContent)
+{
+ nsCOMPtr<nsIPresShell> result;
+ if (nsIDocument* doc = aContent->GetComposedDoc()) {
+ result = doc->GetShell();
+ }
+ return result.forget();
+}
+
+static void
+SetPaintRequestTime(nsIContent* aContent, const TimeStamp& aPaintRequestTime)
+{
+ aContent->SetProperty(nsGkAtoms::paintRequestTime,
+ new TimeStamp(aPaintRequestTime),
+ nsINode::DeleteProperty<TimeStamp>);
+}
+
+void
+APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics)
+{
+ if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
+ return;
+ }
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
+ if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) {
+ return;
+ }
+
+ MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+
+ if (gfxPrefs::APZAllowZooming()) {
+ // If zooming is disabled then we don't really want to let APZ fiddle
+ // with these things. In theory setting the resolution here should be a
+ // no-op, but setting the SPCSPS is bad because it can cause a stale value
+ // to be returned by window.innerWidth/innerHeight (see bug 1187792).
+
+ float presShellResolution = shell->GetResolution();
+
+ // If the pres shell resolution has changed on the content side side
+ // the time this repaint request was fired, consider this request out of date
+ // and drop it; setting a zoom based on the out-of-date resolution can have
+ // the effect of getting us stuck with the stale resolution.
+ if (!FuzzyEqualsMultiplicative(presShellResolution, aMetrics.GetPresShellResolution())) {
+ return;
+ }
+
+ // The pres shell resolution is updated by the the async zoom since the
+ // last paint.
+ presShellResolution = aMetrics.GetPresShellResolution()
+ * aMetrics.GetAsyncZoom().scale;
+ shell->SetResolutionAndScaleTo(presShellResolution);
+ }
+
+ // Do this as late as possible since scrolling can flush layout. It also
+ // adjusts the display port margins, so do it before we set those.
+ ScrollFrame(content, aMetrics);
+
+ SetDisplayPortMargins(shell, content, aMetrics);
+ SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
+}
+
+void
+APZCCallbackHelper::UpdateSubFrame(FrameMetrics& aMetrics)
+{
+ if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
+ return;
+ }
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+
+ // We don't currently support zooming for subframes, so nothing extra
+ // needs to be done beyond the tasks common to this and UpdateRootFrame.
+ ScrollFrame(content, aMetrics);
+ if (nsCOMPtr<nsIPresShell> shell = GetPresShell(content)) {
+ SetDisplayPortMargins(shell, content, aMetrics);
+ }
+ SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
+}
+
+bool
+APZCCallbackHelper::GetOrCreateScrollIdentifiers(nsIContent* aContent,
+ uint32_t* aPresShellIdOut,
+ FrameMetrics::ViewID* aViewIdOut)
+{
+ if (!aContent) {
+ return false;
+ }
+ *aViewIdOut = nsLayoutUtils::FindOrCreateIDFor(aContent);
+ if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
+ *aPresShellIdOut = shell->GetPresShellId();
+ return true;
+ }
+ return false;
+}
+
+void
+APZCCallbackHelper::InitializeRootDisplayport(nsIPresShell* aPresShell)
+{
+ // Create a view-id and set a zero-margin displayport for the root element
+ // of the root document in the chrome process. This ensures that the scroll
+ // frame for this element gets an APZC, which in turn ensures that all content
+ // in the chrome processes is covered by an APZC.
+ // The displayport is zero-margin because this element is generally not
+ // actually scrollable (if it is, APZC will set proper margins when it's
+ // scrolled).
+ if (!aPresShell) {
+ return;
+ }
+
+ MOZ_ASSERT(aPresShell->GetDocument());
+ nsIContent* content = aPresShell->GetDocument()->GetDocumentElement();
+ if (!content) {
+ return;
+ }
+
+ uint32_t presShellId;
+ FrameMetrics::ViewID viewId;
+ if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
+ // Note that the base rect that goes with these margins is set in
+ // nsRootBoxFrame::BuildDisplayList.
+ nsLayoutUtils::SetDisplayPortMargins(content, aPresShell, ScreenMargin(), 0,
+ nsLayoutUtils::RepaintMode::DoNotRepaint);
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ content->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::DoNotRepaint);
+ }
+}
+
+nsPresContext*
+APZCCallbackHelper::GetPresContextForContent(nsIContent* aContent)
+{
+ nsIDocument* doc = aContent->GetComposedDoc();
+ if (!doc) {
+ return nullptr;
+ }
+ nsIPresShell* shell = doc->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ return shell->GetPresContext();
+}
+
+nsIPresShell*
+APZCCallbackHelper::GetRootContentDocumentPresShellForContent(nsIContent* aContent)
+{
+ nsPresContext* context = GetPresContextForContent(aContent);
+ if (!context) {
+ return nullptr;
+ }
+ context = context->GetToplevelContentDocumentPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ return context->PresShell();
+}
+
+static nsIPresShell*
+GetRootDocumentPresShell(nsIContent* aContent)
+{
+ nsIDocument* doc = aContent->GetComposedDoc();
+ if (!doc) {
+ return nullptr;
+ }
+ nsIPresShell* shell = doc->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ nsPresContext* context = shell->GetPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ context = context->GetRootPresContext();
+ if (!context) {
+ return nullptr;
+ }
+ return context->PresShell();
+}
+
+CSSPoint
+APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
+ const ScrollableLayerGuid& aGuid)
+{
+ CSSPoint input = aInput;
+ if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) {
+ return input;
+ }
+ nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
+ if (!content) {
+ return input;
+ }
+
+ // First, scale inversely by the root content document's pres shell
+ // resolution to cancel the scale-to-resolution transform that the
+ // compositor adds to the layer with the pres shell resolution. The points
+ // sent to Gecko by APZ don't have this transform unapplied (unlike other
+ // compositor-side transforms) because APZ doesn't know about it.
+ if (nsIPresShell* shell = GetRootDocumentPresShell(content)) {
+ input = input / shell->GetResolution();
+ }
+
+ // This represents any resolution on the Root Content Document (RCD)
+ // that's not on the Root Document (RD). That is, on platforms where
+ // RCD == RD, it's 1, and on platforms where RCD != RD, it's the RCD
+ // resolution. 'input' has this resolution applied, but the scroll
+ // delta retrieved below do not, so we need to apply them to the
+ // delta before adding the delta to 'input'. (Technically, deltas
+ // from scroll frames outside the RCD would already have this
+ // resolution applied, but we don't have such scroll frames in
+ // practice.)
+ float nonRootResolution = 1.0f;
+ if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
+ nonRootResolution = shell->GetCumulativeNonRootScaleResolution();
+ }
+ // Now apply the callback-transform. This is only approximately correct,
+ // see the comment on GetCumulativeApzCallbackTransform for details.
+ CSSPoint transform = nsLayoutUtils::GetCumulativeApzCallbackTransform(content->GetPrimaryFrame());
+ return input + transform * nonRootResolution;
+}
+
+LayoutDeviceIntPoint
+APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale)
+{
+ LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
+ point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
+ return LayoutDeviceIntPoint::Round(point);
+}
+
+void
+APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale)
+{
+ if (aEvent.AsTouchEvent()) {
+ WidgetTouchEvent& event = *(aEvent.AsTouchEvent());
+ for (size_t i = 0; i < event.mTouches.Length(); i++) {
+ event.mTouches[i]->mRefPoint = ApplyCallbackTransform(
+ event.mTouches[i]->mRefPoint, aGuid, aScale);
+ }
+ } else {
+ aEvent.mRefPoint = ApplyCallbackTransform(aEvent.mRefPoint, aGuid, aScale);
+ }
+}
+
+nsEventStatus
+APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent)
+{
+ nsEventStatus status = nsEventStatus_eConsumeNoDefault;
+ if (aEvent.mWidget) {
+ aEvent.mWidget->DispatchEvent(&aEvent, status);
+ }
+ return status;
+}
+
+nsEventStatus
+APZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessage aMsg,
+ uint64_t aTime,
+ const LayoutDevicePoint& aRefPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget)
+{
+ MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown ||
+ aMsg == eMouseUp || aMsg == eMouseLongTap);
+
+ WidgetMouseEvent event(true, aMsg, aWidget,
+ WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+ event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
+ event.mTime = aTime;
+ event.button = WidgetMouseEvent::eLeftButton;
+ event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+ if (aMsg == eMouseLongTap) {
+ event.mFlags.mOnlyChromeDispatch = true;
+ }
+ event.mIgnoreRootScrollFrame = true;
+ if (aMsg != eMouseMove) {
+ event.mClickCount = aClickCount;
+ }
+ event.mModifiers = aModifiers;
+ // Real touch events will generate corresponding pointer events. We set
+ // convertToPointer to false to prevent the synthesized mouse events generate
+ // pointer events again.
+ event.convertToPointer = false;
+ return DispatchWidgetEvent(event);
+}
+
+bool
+APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const nsString& aType,
+ const CSSPoint& aPoint,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame,
+ unsigned short aInputSourceArg)
+{
+ NS_ENSURE_TRUE(aPresShell, true);
+
+ bool defaultPrevented = false;
+ nsContentUtils::SendMouseEvent(aPresShell, aType, aPoint.x, aPoint.y,
+ aButton, nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED, aClickCount,
+ aModifiers, aIgnoreRootScrollFrame, 0, aInputSourceArg, false,
+ &defaultPrevented, false, /* aIsWidgetEventSynthesized = */ false);
+ return defaultPrevented;
+}
+
+
+void
+APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget)
+{
+ if (aWidget->Destroyed()) {
+ return;
+ }
+ APZCCH_LOG("Dispatching single-tap component events to %s\n",
+ Stringify(aPoint).c_str());
+ int time = 0;
+ DispatchSynthesizedMouseEvent(eMouseMove, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseDown, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseUp, time, aPoint, aModifiers, aClickCount, aWidget);
+}
+
+static dom::Element*
+GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
+{
+ if (!aScrollableFrame) {
+ return nullptr;
+ }
+ nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
+ if (!scrolledFrame) {
+ return nullptr;
+ }
+ // |scrolledFrame| should at this point be the root content frame of the
+ // nearest ancestor scrollable frame. The element corresponding to this
+ // frame should be the one with the displayport set on it, so find that
+ // element and return it.
+ nsIContent* content = scrolledFrame->GetContent();
+ MOZ_ASSERT(content->IsElement()); // roc says this must be true
+ return content->AsElement();
+}
+
+
+static dom::Element*
+GetRootDocumentElementFor(nsIWidget* aWidget)
+{
+ // This returns the root element that ChromeProcessController sets the
+ // displayport on during initialization.
+ if (nsView* view = nsView::GetViewFor(aWidget)) {
+ if (nsIPresShell* shell = view->GetPresShell()) {
+ MOZ_ASSERT(shell->GetDocument());
+ return shell->GetDocument()->GetDocumentElement();
+ }
+ }
+ return nullptr;
+}
+
+static nsIFrame*
+UpdateRootFrameForTouchTargetDocument(nsIFrame* aRootFrame)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+ // Re-target so that the hit test is performed relative to the frame for the
+ // Root Content Document instead of the Root Document which are different in
+ // Android. See bug 1229752 comment 16 for an explanation of why this is necessary.
+ if (nsIDocument* doc = aRootFrame->PresContext()->PresShell()->GetTouchEventTargetDocument()) {
+ if (nsIPresShell* shell = doc->GetShell()) {
+ if (nsIFrame* frame = shell->GetRootFrame()) {
+ return frame;
+ }
+ }
+ }
+#endif
+ return aRootFrame;
+}
+
+// Determine the scrollable target frame for the given point and add it to
+// the target list. If the frame doesn't have a displayport, set one.
+// Return whether or not a displayport was set.
+static bool
+PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
+ const ScrollableLayerGuid& aGuid,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aRefPoint,
+ nsTArray<ScrollableLayerGuid>* aTargets)
+{
+ ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
+ nsPoint point =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
+ nsIFrame* target =
+ nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+ nsIScrollableFrame* scrollAncestor = target
+ ? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
+ : aRootFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
+
+ // Assuming that if there's no scrollAncestor, there's already a displayPort.
+ nsCOMPtr<dom::Element> dpElement = scrollAncestor
+ ? GetDisplayportElementFor(scrollAncestor)
+ : GetRootDocumentElementFor(aWidget);
+
+ nsAutoString dpElementDesc;
+ if (dpElement) {
+ dpElement->Describe(dpElementDesc);
+ }
+ APZCCH_LOG("For event at %s found scrollable element %p (%s)\n",
+ Stringify(aRefPoint).c_str(), dpElement.get(),
+ NS_LossyConvertUTF16toASCII(dpElementDesc).get());
+
+ bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+ dpElement, &(guid.mPresShellId), &(guid.mScrollId));
+ aTargets->AppendElement(guid);
+
+ if (!guidIsValid || nsLayoutUtils::HasDisplayPort(dpElement)) {
+ return false;
+ }
+
+ if (!scrollAncestor) {
+ MOZ_ASSERT(false); // If you hit this, please file a bug with STR.
+
+ // Attempt some sort of graceful handling based on a theory as to why we
+ // reach this point...
+ // If we get here, the document element is non-null, valid, but doesn't have
+ // a displayport. It's possible that the init code in ChromeProcessController
+ // failed for some reason, or the document element got swapped out at some
+ // later time. In this case let's try to set a displayport on the document
+ // element again and bail out on this operation.
+ APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n",
+ aWidget, dpElement.get());
+ APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresContext()->PresShell());
+ return false;
+ }
+
+ APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
+ bool activated = nsLayoutUtils::CalculateAndSetDisplayPortMargins(
+ scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
+ if (!activated) {
+ return false;
+ }
+
+ nsIFrame* frame = do_QueryFrame(scrollAncestor);
+ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(frame,
+ nsLayoutUtils::RepaintMode::Repaint);
+
+ return true;
+}
+
+static void
+SendLayersDependentApzcTargetConfirmation(nsIPresShell* aShell, uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ LayerManager* lm = aShell->GetLayerManager();
+ if (!lm) {
+ return;
+ }
+
+ LayerTransactionChild* shadow = lm->AsShadowForwarder()->GetShadowManager();
+ if (!shadow) {
+ return;
+ }
+
+ shadow->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
+}
+
+class DisplayportSetListener : public nsAPostRefreshObserver {
+public:
+ DisplayportSetListener(nsIPresShell* aPresShell,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+ : mPresShell(aPresShell)
+ , mInputBlockId(aInputBlockId)
+ , mTargets(aTargets)
+ {
+ }
+
+ virtual ~DisplayportSetListener()
+ {
+ }
+
+ void DidRefresh() override {
+ if (!mPresShell) {
+ MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
+ return;
+ }
+
+ APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
+ SendLayersDependentApzcTargetConfirmation(mPresShell, mInputBlockId, Move(mTargets));
+
+ if (!mPresShell->RemovePostRefreshObserver(this)) {
+ MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
+ // Graceful handling, just in case...
+ mPresShell = nullptr;
+ return;
+ }
+
+ delete this;
+ }
+
+private:
+ RefPtr<nsIPresShell> mPresShell;
+ uint64_t mInputBlockId;
+ nsTArray<ScrollableLayerGuid> mTargets;
+};
+
+// Sends a SetTarget notification for APZC, given one or more previous
+// calls to PrepareForAPZCSetTargetNotification().
+static void
+SendSetTargetAPZCNotificationHelper(nsIWidget* aWidget,
+ nsIPresShell* aShell,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets,
+ bool aWaitForRefresh)
+{
+ bool waitForRefresh = aWaitForRefresh;
+ if (waitForRefresh) {
+ APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n");
+ waitForRefresh = aShell->AddPostRefreshObserver(
+ new DisplayportSetListener(aShell, aInputBlockId, Move(aTargets)));
+ }
+ if (!waitForRefresh) {
+ APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
+ aWidget->SetConfirmedTargetAPZC(aInputBlockId, aTargets);
+ } else {
+ APZCCH_LOG("Successfully registered post-refresh observer\n");
+ }
+}
+
+void
+APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetGUIEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ if (!aWidget || !aDocument) {
+ return;
+ }
+ if (aInputBlockId == sLastTargetAPZCNotificationInputBlock) {
+ // We have already confirmed the target APZC for a previous event of this
+ // input block. If we activated a scroll frame for this input block,
+ // sending another target APZC confirmation would be harmful, as it might
+ // race the original confirmation (which needs to go through a layers
+ // transaction).
+ APZCCH_LOG("Not resending target APZC confirmation for input block %" PRIu64 "\n", aInputBlockId);
+ return;
+ }
+ sLastTargetAPZCNotificationInputBlock = aInputBlockId;
+ if (nsIPresShell* shell = aDocument->GetShell()) {
+ if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+ rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
+ bool waitForRefresh = false;
+ nsTArray<ScrollableLayerGuid> targets;
+
+ if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
+ for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
+ waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets);
+ }
+ } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
+ waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, wheelEvent->mRefPoint, &targets);
+ } else if (const WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent()) {
+ waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+ rootFrame, mouseEvent->mRefPoint, &targets);
+ }
+ // TODO: Do other types of events need to be handled?
+
+ if (!targets.IsEmpty()) {
+ SendSetTargetAPZCNotificationHelper(
+ aWidget,
+ shell,
+ aInputBlockId,
+ Move(targets),
+ waitForRefresh);
+ }
+ }
+ }
+}
+
+void
+APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
+ nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetTouchEvent& aEvent,
+ uint64_t aInputBlockId,
+ const SetAllowedTouchBehaviorCallback& aCallback)
+{
+ if (nsIPresShell* shell = aDocument->GetShell()) {
+ if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+ rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
+
+ nsTArray<TouchBehaviorFlags> flags;
+ for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ flags.AppendElement(
+ TouchActionHelper::GetAllowedTouchBehavior(aWidget,
+ rootFrame, aEvent.mTouches[i]->mRefPoint));
+ }
+ aCallback(aInputBlockId, Move(flags));
+ }
+ }
+}
+
+void
+APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+{
+ nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
+ if (!targetContent) {
+ return;
+ }
+ nsCOMPtr<nsIDocument> ownerDoc = targetContent->OwnerDoc();
+ if (!ownerDoc) {
+ return;
+ }
+
+ nsContentUtils::DispatchTrustedEvent(
+ ownerDoc, targetContent,
+ aEvent,
+ true, true);
+}
+
+void
+APZCCallbackHelper::NotifyFlushComplete(nsIPresShell* aShell)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // In some cases, flushing the APZ state to the main thread doesn't actually
+ // trigger a flush and repaint (this is an intentional optimization - the stuff
+ // visible to the user is still correct). However, reftests update their
+ // snapshot based on invalidation events that are emitted during paints,
+ // so we ensure that we kick off a paint when an APZ flush is done. Note that
+ // only chrome/testing code can trigger this behaviour.
+ if (aShell && aShell->GetRootFrame()) {
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ MOZ_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
+}
+
+static int32_t sActiveSuppressDisplayport = 0;
+static bool sDisplayPortSuppressionRespected = true;
+
+void
+APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell)
+{
+ if (aEnabled) {
+ sActiveSuppressDisplayport++;
+ } else {
+ bool isSuppressed = IsDisplayportSuppressed();
+ sActiveSuppressDisplayport--;
+ if (isSuppressed && !IsDisplayportSuppressed() &&
+ aShell && aShell->GetRootFrame()) {
+ // We unsuppressed the displayport, trigger a paint
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+ }
+
+ MOZ_ASSERT(sActiveSuppressDisplayport >= 0);
+}
+
+void
+APZCCallbackHelper::RespectDisplayPortSuppression(bool aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell)
+{
+ bool isSuppressed = IsDisplayportSuppressed();
+ sDisplayPortSuppressionRespected = aEnabled;
+ if (isSuppressed && !IsDisplayportSuppressed() &&
+ aShell && aShell->GetRootFrame()) {
+ // We unsuppressed the displayport, trigger a paint
+ aShell->GetRootFrame()->SchedulePaint();
+ }
+}
+
+bool
+APZCCallbackHelper::IsDisplayportSuppressed()
+{
+ return sDisplayPortSuppressionRespected
+ && sActiveSuppressDisplayport > 0;
+}
+
+/* static */ bool
+APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
+{
+ return aFrame->IsProcessingAsyncScroll()
+ || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
+ || aFrame->LastSmoothScrollOrigin();
+}
+
+/* static */ void
+APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers,
+ nsIWidget* aWidget)
+{
+ EventMessage msg;
+ switch (aType) {
+ case PinchGestureInput::PINCHGESTURE_START:
+ msg = eMagnifyGestureStart;
+ break;
+ case PinchGestureInput::PINCHGESTURE_SCALE:
+ msg = eMagnifyGestureUpdate;
+ break;
+ case PinchGestureInput::PINCHGESTURE_END:
+ msg = eMagnifyGesture;
+ break;
+ case PinchGestureInput::PINCHGESTURE_SENTINEL:
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid gesture type");
+ return;
+ }
+
+ WidgetSimpleGestureEvent event(true, msg, aWidget);
+ event.mDelta = aSpanChange;
+ event.mModifiers = aModifiers;
+ DispatchWidgetEvent(event);
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h
new file mode 100644
index 000000000..8e888805c
--- /dev/null
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCCallbackHelper_h
+#define mozilla_layers_APZCCallbackHelper_h
+
+#include "FrameMetrics.h"
+#include "InputData.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
+#include "mozilla/layers/APZUtils.h"
+#include "nsIDOMWindowUtils.h"
+
+class nsIContent;
+class nsIDocument;
+class nsIPresShell;
+class nsIScrollableFrame;
+class nsIWidget;
+template<class T> struct already_AddRefed;
+template<class T> class nsCOMPtr;
+
+namespace mozilla {
+namespace layers {
+
+typedef function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
+ SetAllowedTouchBehaviorCallback;
+
+/* This class contains some helper methods that facilitate implementing the
+ GeckoContentController callback interface required by the AsyncPanZoomController.
+ Since different platforms need to implement this interface in similar-but-
+ not-quite-the-same ways, this utility class provides some helpful methods
+ to hold code that can be shared across the different platform implementations.
+ */
+class APZCCallbackHelper
+{
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
+
+public:
+ /* Applies the scroll and zoom parameters from the given FrameMetrics object
+ to the root frame for the given metrics' scrollId. If tiled thebes layers
+ are enabled, this will align the displayport to tile boundaries. Setting
+ the scroll position can cause some small adjustments to be made to the
+ actual scroll position. aMetrics' display port and scroll position will
+ be updated with any modifications made. */
+ static void UpdateRootFrame(FrameMetrics& aMetrics);
+
+ /* Applies the scroll parameters from the given FrameMetrics object to the
+ subframe corresponding to given metrics' scrollId. If tiled thebes
+ layers are enabled, this will align the displayport to tile boundaries.
+ Setting the scroll position can cause some small adjustments to be made
+ to the actual scroll position. aMetrics' display port and scroll position
+ will be updated with any modifications made. */
+ static void UpdateSubFrame(FrameMetrics& aMetrics);
+
+ /* Get the presShellId and view ID for the given content element.
+ * If the view ID does not exist, one is created.
+ * The pres shell ID should generally already exist; if it doesn't for some
+ * reason, false is returned. */
+ static bool GetOrCreateScrollIdentifiers(nsIContent* aContent,
+ uint32_t* aPresShellIdOut,
+ FrameMetrics::ViewID* aViewIdOut);
+
+ /* Initialize a zero-margin displayport on the root document element of the
+ given presShell. */
+ static void InitializeRootDisplayport(nsIPresShell* aPresShell);
+
+ /* Get the pres context associated with the document enclosing |aContent|. */
+ static nsPresContext* GetPresContextForContent(nsIContent* aContent);
+
+ /* Get the pres shell associated with the root content document enclosing |aContent|. */
+ static nsIPresShell* GetRootContentDocumentPresShellForContent(nsIContent* aContent);
+
+ /* Apply an "input transform" to the given |aInput| and return the transformed value.
+ The input transform applied is the one for the content element corresponding to
+ |aGuid|; this is populated in a previous call to UpdateCallbackTransform. See that
+ method's documentations for details.
+ This method additionally adjusts |aInput| by inversely scaling by the provided
+ pres shell resolution, to cancel out a compositor-side transform (added in
+ bug 1076241) that APZ doesn't unapply. */
+ static CSSPoint ApplyCallbackTransform(const CSSPoint& aInput,
+ const ScrollableLayerGuid& aGuid);
+
+ /* Same as above, but operates on LayoutDeviceIntPoint.
+ Requires an additonal |aScale| parameter to convert between CSS and
+ LayoutDevice space. */
+ static mozilla::LayoutDeviceIntPoint
+ ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale);
+
+ /* Convenience function for applying a callback transform to all refpoints
+ * in the input event. */
+ static void ApplyCallbackTransform(WidgetEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const CSSToLayoutDeviceScale& aScale);
+
+ /* Dispatch a widget event via the widget stored in the event, if any.
+ * In a child process, allows the TabParent event-capture mechanism to
+ * intercept the event. */
+ static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
+
+ /* Synthesize a mouse event with the given parameters, and dispatch it
+ * via the given widget. */
+ static nsEventStatus DispatchSynthesizedMouseEvent(EventMessage aMsg,
+ uint64_t aTime,
+ const LayoutDevicePoint& aRefPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget);
+
+ /* Dispatch a mouse event with the given parameters.
+ * Return whether or not any listeners have called preventDefault on the event. */
+ static bool DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const nsString& aType,
+ const CSSPoint& aPoint,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame,
+ unsigned short aInputSourceArg);
+
+ /* Fire a single-tap event at the given point. The event is dispatched
+ * via the given widget. */
+ static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsIWidget* aWidget);
+
+ /* Perform hit-testing on the touch points of |aEvent| to determine
+ * which scrollable frames they target. If any of these frames don't have
+ * a displayport, set one.
+ *
+ * If any displayports need to be set, the actual notification to APZ is
+ * sent to the compositor, which will then post a message back to APZ's
+ * controller thread. Otherwise, the provided widget's SetConfirmedTargetAPZC
+ * method is invoked immediately.
+ */
+ static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetGUIEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+
+ /* Figure out the allowed touch behaviors of each touch point in |aEvent|
+ * and send that information to the provided callback. */
+ static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget,
+ nsIDocument* aDocument,
+ const WidgetTouchEvent& aEvent,
+ uint64_t aInputBlockId,
+ const SetAllowedTouchBehaviorCallback& aCallback);
+
+ /* Notify content of a mouse scroll testing event. */
+ static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
+
+ /* Notify content that the repaint flush is complete. */
+ static void NotifyFlushComplete(nsIPresShell* aShell);
+
+ /* Temporarily ignore the Displayport for better paint performance. If at
+ * all possible, pass in a presShell if you have one at the call site, we
+ * use it to trigger a repaint once suppression is disabled. Without that
+ * the displayport may get left at the suppressed size for an extended
+ * period of time and result in unnecessary checkerboarding (see bug
+ * 1255054). */
+ static void SuppressDisplayport(const bool& aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell);
+
+ /* Whether or not displayport suppression should be turned on. Note that
+ * this only affects the return value of |IsDisplayportSuppressed()|, and
+ * doesn't change the value of the internal counter. As with
+ * SuppressDisplayport, this function should be passed a presShell to trigger
+ * a repaint if suppression is being turned off.
+ */
+ static void RespectDisplayPortSuppression(bool aEnabled,
+ const nsCOMPtr<nsIPresShell>& aShell);
+
+ /* Whether or not the displayport is currently suppressed. */
+ static bool IsDisplayportSuppressed();
+
+ static void
+ AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
+ const CSSPoint& aActualScrollOffset);
+
+ /*
+ * Check if the scrollable frame is currently in the middle of an async
+ * or smooth scroll. We want to discard certain scroll input if this is
+ * true to prevent clobbering higher priority origins.
+ */
+ static bool
+ IsScrollInProgress(nsIScrollableFrame* aFrame);
+
+ /* Notify content of the progress of a pinch gesture that APZ won't do
+ * zooming for (because the apz.allow_zooming pref is false). This function
+ * will dispatch appropriate WidgetSimpleGestureEvent events to gecko.
+ */
+ static void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers,
+ nsIWidget* aWidget);
+private:
+ static uint64_t sLastTargetAPZCNotificationInputBlock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZCCallbackHelper_h */
diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp
new file mode 100644
index 000000000..20a41eed5
--- /dev/null
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZEventState.h"
+
+#include "ActiveElementManager.h"
+#include "APZCCallbackHelper.h"
+#include "gfxPrefs.h"
+#include "LayersLogging.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Move.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "nsCOMPtr.h"
+#include "nsDocShell.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIScrollableFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "nsITimer.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIWidget.h"
+#include "nsLayoutUtils.h"
+#include "nsQueryFrame.h"
+#include "TouchManager.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsLayoutUtils.h"
+#include "nsIScrollableFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "mozilla/TouchEvents.h"
+
+#define APZES_LOG(...)
+// #define APZES_LOG(...) printf_stderr("APZES: " __VA_ARGS__)
+
+// Static helper functions
+namespace {
+
+int32_t
+WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
+{
+ int32_t result = 0;
+ if (aModifiers & mozilla::MODIFIER_SHIFT) {
+ result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
+ }
+ if (aModifiers & mozilla::MODIFIER_CONTROL) {
+ result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
+ }
+ if (aModifiers & mozilla::MODIFIER_ALT) {
+ result |= nsIDOMWindowUtils::MODIFIER_ALT;
+ }
+ if (aModifiers & mozilla::MODIFIER_META) {
+ result |= nsIDOMWindowUtils::MODIFIER_META;
+ }
+ if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
+ result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
+ }
+ if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_FN) {
+ result |= nsIDOMWindowUtils::MODIFIER_FN;
+ }
+ if (aModifiers & mozilla::MODIFIER_FNLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_FNLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_SYMBOL) {
+ result |= nsIDOMWindowUtils::MODIFIER_SYMBOL;
+ }
+ if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
+ result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
+ }
+ if (aModifiers & mozilla::MODIFIER_OS) {
+ result |= nsIDOMWindowUtils::MODIFIER_OS;
+ }
+ return result;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace layers {
+
+static int32_t sActiveDurationMs = 10;
+static bool sActiveDurationMsSet = false;
+
+APZEventState::APZEventState(nsIWidget* aWidget,
+ ContentReceivedInputBlockCallback&& aCallback)
+ : mWidget(nullptr) // initialized in constructor body
+ , mActiveElementManager(new ActiveElementManager())
+ , mContentReceivedInputBlockCallback(Move(aCallback))
+ , mPendingTouchPreventedResponse(false)
+ , mPendingTouchPreventedBlockId(0)
+ , mEndTouchIsClick(false)
+ , mTouchEndCancelled(false)
+ , mLastTouchIdentifier(0)
+{
+ nsresult rv;
+ mWidget = do_GetWeakReference(aWidget, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "APZEventState constructed with a widget that"
+ " does not support weak references. APZ will NOT work!");
+
+ if (!sActiveDurationMsSet) {
+ Preferences::AddIntVarCache(&sActiveDurationMs,
+ "ui.touch_activation.duration_ms",
+ sActiveDurationMs);
+ sActiveDurationMsSet = true;
+ }
+}
+
+APZEventState::~APZEventState()
+{}
+
+class DelayedFireSingleTapEvent final : public nsITimerCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ DelayedFireSingleTapEvent(nsWeakPtr aWidget,
+ LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ int32_t aClickCount,
+ nsITimer* aTimer)
+ : mWidget(aWidget)
+ , mPoint(aPoint)
+ , mModifiers(aModifiers)
+ , mClickCount(aClickCount)
+ // Hold the reference count until we are called back.
+ , mTimer(aTimer)
+ {
+ }
+
+ NS_IMETHOD Notify(nsITimer*) override
+ {
+ if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
+ APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, mClickCount, widget);
+ }
+ mTimer = nullptr;
+ return NS_OK;
+ }
+
+ void ClearTimer() {
+ mTimer = nullptr;
+ }
+
+private:
+ ~DelayedFireSingleTapEvent()
+ {
+ }
+
+ nsWeakPtr mWidget;
+ LayoutDevicePoint mPoint;
+ Modifiers mModifiers;
+ int32_t mClickCount;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
+
+void
+APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount)
+{
+ APZES_LOG("Handling single tap at %s on %s with %d\n",
+ Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return;
+ }
+
+ if (mTouchEndCancelled) {
+ return;
+ }
+
+ LayoutDevicePoint ldPoint = aPoint * aScale;
+ if (!mActiveElementManager->ActiveElementUsesStyle()) {
+ // If the active element isn't visually affected by the :active style, we
+ // have no need to wait the extra sActiveDurationMs to make the activation
+ // visually obvious to the user.
+ APZCCallbackHelper::FireSingleTapEvent(ldPoint, aModifiers, aClickCount, widget);
+ return;
+ }
+
+ APZES_LOG("Active element uses style, scheduling timer for click event\n");
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ RefPtr<DelayedFireSingleTapEvent> callback =
+ new DelayedFireSingleTapEvent(mWidget, ldPoint, aModifiers, aClickCount, timer);
+ nsresult rv = timer->InitWithCallback(callback,
+ sActiveDurationMs,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_FAILED(rv)) {
+ // Make |callback| not hold the timer, so they will both be destructed when
+ // we leave the scope of this function.
+ callback->ClearTimer();
+ }
+}
+
+bool
+APZEventState::FireContextmenuEvents(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const nsCOMPtr<nsIWidget>& aWidget)
+{
+ // Converting the modifiers to DOM format for the DispatchMouseEvent call
+ // is the most useless thing ever because nsDOMWindowUtils::SendMouseEvent
+ // just converts them back to widget format, but that API has many callers,
+ // including in JS code, so it's not trivial to change.
+ bool eventHandled =
+ APZCCallbackHelper::DispatchMouseEvent(aPresShell, NS_LITERAL_STRING("contextmenu"),
+ aPoint, 2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
+ nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+
+ APZES_LOG("Contextmenu event handled: %d\n", eventHandled);
+ if (eventHandled) {
+ // If the contextmenu event was handled then we're showing a contextmenu,
+ // and so we should remove any activation
+ mActiveElementManager->ClearActivation();
+#ifndef XP_WIN
+ } else {
+ // If the contextmenu wasn't consumed, fire the eMouseLongTap event.
+ nsEventStatus status = APZCCallbackHelper::DispatchSynthesizedMouseEvent(
+ eMouseLongTap, /*time*/ 0, aPoint * aScale, aModifiers,
+ /*clickCount*/ 1, aWidget);
+ eventHandled = (status == nsEventStatus_eConsumeNoDefault);
+ APZES_LOG("eMouseLongTap event handled: %d\n", eventHandled);
+#endif
+ }
+
+ return eventHandled;
+}
+
+void
+APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return;
+ }
+
+ SendPendingTouchPreventedResponse(false);
+
+#ifdef XP_WIN
+ // On Windows, we fire the contextmenu events when the user lifts their
+ // finger, in keeping with the platform convention. This happens in the
+ // ProcessLongTapUp function. However, we still fire the eMouseLongTap event
+ // at this time, because things like text selection or dragging may want
+ // to know about it.
+ nsEventStatus status = APZCCallbackHelper::DispatchSynthesizedMouseEvent(
+ eMouseLongTap, /*time*/ 0, aPoint * aScale, aModifiers, /*clickCount*/ 1,
+ widget);
+
+ bool eventHandled = (status == nsEventStatus_eConsumeNoDefault);
+#else
+ bool eventHandled = FireContextmenuEvents(aPresShell, aPoint, aScale,
+ aModifiers, widget);
+#endif
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
+
+ if (eventHandled) {
+ // Also send a touchcancel to content, so that listeners that might be
+ // waiting for a touchend don't trigger.
+ WidgetTouchEvent cancelTouchEvent(true, eTouchCancel, widget.get());
+ cancelTouchEvent.mModifiers = aModifiers;
+ auto ldPoint = LayoutDeviceIntPoint::Round(aPoint * aScale);
+ cancelTouchEvent.mTouches.AppendElement(new mozilla::dom::Touch(mLastTouchIdentifier,
+ ldPoint, LayoutDeviceIntPoint(), 0, 0));
+ APZCCallbackHelper::DispatchWidgetEvent(cancelTouchEvent);
+ }
+}
+
+void
+APZEventState::ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers)
+{
+#ifdef XP_WIN
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ FireContextmenuEvents(aPresShell, aPoint, aScale, aModifiers, widget);
+ }
+#endif
+}
+
+void
+APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse,
+ nsEventStatus aContentResponse)
+{
+ if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) {
+ mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget());
+ mLastTouchIdentifier = aEvent.mTouches[0]->Identifier();
+ }
+
+ bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault;
+ bool sentContentResponse = false;
+ APZES_LOG("Handling event type %d\n", aEvent.mMessage);
+ switch (aEvent.mMessage) {
+ case eTouchStart: {
+ mTouchEndCancelled = false;
+ sentContentResponse = SendPendingTouchPreventedResponse(false);
+ // sentContentResponse can be true here if we get two TOUCH_STARTs in a row
+ // and just responded to the first one.
+
+ // We're about to send a response back to APZ, but we should only do it
+ // for events that went through APZ (which should be all of them).
+ MOZ_ASSERT(aEvent.mFlags.mHandledByAPZ);
+
+ if (isTouchPrevented) {
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented);
+ sentContentResponse = true;
+ } else {
+ APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n",
+ aInputBlockId, Stringify(aGuid).c_str());
+ mPendingTouchPreventedResponse = true;
+ mPendingTouchPreventedGuid = aGuid;
+ mPendingTouchPreventedBlockId = aInputBlockId;
+ }
+ break;
+ }
+
+ case eTouchEnd:
+ if (isTouchPrevented) {
+ mTouchEndCancelled = true;
+ mEndTouchIsClick = false;
+ }
+ MOZ_FALLTHROUGH;
+ case eTouchCancel:
+ mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
+ MOZ_FALLTHROUGH;
+ case eTouchMove: {
+ if (mPendingTouchPreventedResponse) {
+ MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
+ }
+ sentContentResponse = SendPendingTouchPreventedResponse(isTouchPrevented);
+ break;
+ }
+
+ default:
+ NS_WARNING("Unknown touch event type");
+ }
+
+ if (sentContentResponse &&
+ aApzResponse == nsEventStatus_eConsumeDoDefault &&
+ gfxPrefs::PointerEventsEnabled()) {
+ WidgetTouchEvent cancelEvent(aEvent);
+ cancelEvent.mMessage = eTouchCancel;
+ cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
+ for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
+ if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
+ touch->convertToPointer = true;
+ }
+ }
+ nsEventStatus status;
+ cancelEvent.mWidget->DispatchEvent(&cancelEvent, status);
+ }
+}
+
+void
+APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // If this event starts a swipe, indicate that it shouldn't result in a
+ // scroll by setting defaultPrevented to true.
+ bool defaultPrevented = aEvent.DefaultPrevented() || aEvent.TriggersSwipe();
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
+}
+
+void
+APZEventState::ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // If we get here and the drag block has not been confirmed by the code in
+ // nsSliderFrame, then no scrollbar reacted to the event thus APZC will
+ // ignore this drag block. We can send defaultPrevented as either true or
+ // false, it doesn't matter, because APZ won't have the scrollbar metrics
+ // anyway, and will know to drop the block.
+ bool defaultPrevented = false;
+ mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
+}
+
+void
+APZEventState::ProcessAPZStateChange(ViewID aViewId,
+ APZStateChange aChange,
+ int aArg)
+{
+ switch (aChange)
+ {
+ case APZStateChange::eTransformBegin:
+ {
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+ if (sf) {
+ sf->SetTransformingByAPZ(true);
+ }
+ nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+ if (scrollbarMediator) {
+ scrollbarMediator->ScrollbarActivityStarted();
+ }
+
+ nsIContent* content = nsLayoutUtils::FindContentFor(aViewId);
+ nsIDocument* doc = content ? content->GetComposedDoc() : nullptr;
+ nsCOMPtr<nsIDocShell> docshell(doc ? doc->GetDocShell() : nullptr);
+ if (docshell && sf) {
+ nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+ nsdocshell->NotifyAsyncPanZoomStarted();
+ }
+ break;
+ }
+ case APZStateChange::eTransformEnd:
+ {
+ nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+ if (sf) {
+ sf->SetTransformingByAPZ(false);
+ }
+ nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+ if (scrollbarMediator) {
+ scrollbarMediator->ScrollbarActivityStopped();
+ }
+
+ nsIContent* content = nsLayoutUtils::FindContentFor(aViewId);
+ nsIDocument* doc = content ? content->GetComposedDoc() : nullptr;
+ nsCOMPtr<nsIDocShell> docshell(doc ? doc->GetDocShell() : nullptr);
+ if (docshell && sf) {
+ nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+ nsdocshell->NotifyAsyncPanZoomStopped();
+ }
+ break;
+ }
+ case APZStateChange::eStartTouch:
+ {
+ mActiveElementManager->HandleTouchStart(aArg);
+ break;
+ }
+ case APZStateChange::eStartPanning:
+ {
+ // The user started to pan, so we don't want anything to be :active.
+ mActiveElementManager->ClearActivation();
+ break;
+ }
+ case APZStateChange::eEndTouch:
+ {
+ mEndTouchIsClick = aArg;
+ mActiveElementManager->HandleTouchEnd();
+ break;
+ }
+ case APZStateChange::eSentinel:
+ // Should never happen, but we want this case branch to stop the compiler
+ // whining about unhandled values.
+ MOZ_ASSERT(false);
+ break;
+ }
+}
+
+void
+APZEventState::ProcessClusterHit()
+{
+ // If we hit a cluster of links then we shouldn't activate any of them,
+ // as we will be showing the zoomed view. (This is only called on Fennec).
+#ifndef MOZ_WIDGET_ANDROID
+ MOZ_ASSERT(false);
+#endif
+ mActiveElementManager->ClearActivation();
+}
+
+bool
+APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
+{
+ if (mPendingTouchPreventedResponse) {
+ APZES_LOG("Sending response %d for pending guid: %s\n", aPreventDefault,
+ Stringify(mPendingTouchPreventedGuid).c_str());
+ mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
+ mPendingTouchPreventedBlockId, aPreventDefault);
+ mPendingTouchPreventedResponse = false;
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<nsIWidget>
+APZEventState::GetWidget() const
+{
+ nsCOMPtr<nsIWidget> result = do_QueryReferent(mWidget);
+ return result.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/APZEventState.h b/gfx/layers/apz/util/APZEventState.h
new file mode 100644
index 000000000..44188eaa7
--- /dev/null
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZEventState_h
+#define mozilla_layers_APZEventState_h
+
+#include <stdint.h>
+
+#include "FrameMetrics.h" // for ScrollableLayerGuid
+#include "Units.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/Function.h"
+#include "mozilla/layers/GeckoContentController.h" // for APZStateChange
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
+
+template <class> class nsCOMPtr;
+class nsIDocument;
+class nsIPresShell;
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ActiveElementManager;
+
+typedef function<void(const ScrollableLayerGuid&,
+ uint64_t /* input block id */,
+ bool /* prevent default */)>
+ ContentReceivedInputBlockCallback;
+
+/**
+ * A content-side component that keeps track of state for handling APZ
+ * gestures and sending APZ notifications.
+ */
+class APZEventState {
+ typedef GeckoContentController::APZStateChange APZStateChange;
+ typedef FrameMetrics::ViewID ViewID;
+public:
+ APZEventState(nsIWidget* aWidget,
+ ContentReceivedInputBlockCallback&& aCallback);
+
+ NS_INLINE_DECL_REFCOUNTING(APZEventState);
+
+ void ProcessSingleTap(const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount);
+ void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers);
+ void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse,
+ nsEventStatus aContentResponse);
+ void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessMouseEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+ void ProcessAPZStateChange(ViewID aViewId,
+ APZStateChange aChange,
+ int aArg);
+ void ProcessClusterHit();
+private:
+ ~APZEventState();
+ bool SendPendingTouchPreventedResponse(bool aPreventDefault);
+ bool FireContextmenuEvents(const nsCOMPtr<nsIPresShell>& aPresShell,
+ const CSSPoint& aPoint,
+ const CSSToLayoutDeviceScale& aScale,
+ Modifiers aModifiers,
+ const nsCOMPtr<nsIWidget>& aWidget);
+ already_AddRefed<nsIWidget> GetWidget() const;
+private:
+ nsWeakPtr mWidget;
+ RefPtr<ActiveElementManager> mActiveElementManager;
+ ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
+ bool mPendingTouchPreventedResponse;
+ ScrollableLayerGuid mPendingTouchPreventedGuid;
+ uint64_t mPendingTouchPreventedBlockId;
+ bool mEndTouchIsClick;
+ bool mTouchEndCancelled;
+ int32_t mLastTouchIdentifier;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZEventState_h */
diff --git a/gfx/layers/apz/util/APZThreadUtils.cpp b/gfx/layers/apz/util/APZThreadUtils.cpp
new file mode 100644
index 000000000..46f67d010
--- /dev/null
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZThreadUtils.h"
+
+#include "mozilla/layers/Compositor.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+static bool sThreadAssertionsEnabled = true;
+static MessageLoop* sControllerThread;
+
+/*static*/ void
+APZThreadUtils::SetThreadAssertionsEnabled(bool aEnabled) {
+ sThreadAssertionsEnabled = aEnabled;
+}
+
+/*static*/ bool
+APZThreadUtils::GetThreadAssertionsEnabled() {
+ return sThreadAssertionsEnabled;
+}
+
+/*static*/ void
+APZThreadUtils::SetControllerThread(MessageLoop* aLoop)
+{
+ // We must either be setting the initial controller thread, or removing it,
+ // or re-using an existing controller thread.
+ MOZ_ASSERT(!sControllerThread || !aLoop || sControllerThread == aLoop);
+ sControllerThread = aLoop;
+}
+
+/*static*/ void
+APZThreadUtils::AssertOnControllerThread() {
+ if (!GetThreadAssertionsEnabled()) {
+ return;
+ }
+
+ MOZ_ASSERT(sControllerThread == MessageLoop::current());
+}
+
+/*static*/ void
+APZThreadUtils::AssertOnCompositorThread()
+{
+ if (GetThreadAssertionsEnabled()) {
+ Compositor::AssertOnCompositorThread();
+ }
+}
+
+/*static*/ void
+APZThreadUtils::RunOnControllerThread(already_AddRefed<Runnable> aTask)
+{
+ RefPtr<Runnable> task = aTask;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // This is needed while nsWindow::ConfigureAPZControllerThread is not propper
+ // implemented.
+ if (AndroidBridge::IsJavaUiThread()) {
+ task->Run();
+ } else {
+ AndroidBridge::Bridge()->PostTaskToUiThread(task.forget(), 0);
+ }
+#else
+ if (!sControllerThread) {
+ // Could happen on startup
+ NS_WARNING("Dropping task posted to controller thread");
+ return;
+ }
+
+ if (sControllerThread == MessageLoop::current()) {
+ task->Run();
+ } else {
+ sControllerThread->PostTask(task.forget());
+ }
+#endif
+}
+
+/*static*/ bool
+APZThreadUtils::IsControllerThread()
+{
+#ifdef MOZ_WIDGET_ANDROID
+ return AndroidBridge::IsJavaUiThread();
+#else
+ return sControllerThread == MessageLoop::current();
+#endif
+}
+
+NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/APZThreadUtils.h b/gfx/layers/apz/util/APZThreadUtils.h
new file mode 100644
index 000000000..4b9b2c0d0
--- /dev/null
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZThreadUtils_h
+#define mozilla_layers_APZThreadUtils_h
+
+#include "base/message_loop.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+
+class Runnable;
+
+namespace layers {
+
+class APZThreadUtils
+{
+public:
+ /**
+ * In the gtest environment everything runs on one thread, so we
+ * shouldn't assert that we're on a particular thread. This enables
+ * that behaviour.
+ */
+ static void SetThreadAssertionsEnabled(bool aEnabled);
+ static bool GetThreadAssertionsEnabled();
+
+ /**
+ * Set the controller thread.
+ */
+ static void SetControllerThread(MessageLoop* aLoop);
+
+ /**
+ * This can be used to assert that the current thread is the
+ * controller/UI thread (on which input events are received).
+ * This does nothing if thread assertions are disabled.
+ */
+ static void AssertOnControllerThread();
+
+ /**
+ * This can be used to assert that the current thread is the
+ * compositor thread (which applies the async transform).
+ * This does nothing if thread assertions are disabled.
+ */
+ static void AssertOnCompositorThread();
+
+ /**
+ * Run the given task on the APZ "controller thread" for this platform. If
+ * this function is called from the controller thread itself then the task is
+ * run immediately without getting queued.
+ */
+ static void RunOnControllerThread(already_AddRefed<Runnable> aTask);
+
+ /**
+ * Returns true if currently on APZ "controller thread".
+ */
+ static bool IsControllerThread();
+};
+
+// A base class for GenericTimerCallback<Function>.
+// This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
+// template.
+class GenericTimerCallbackBase : public nsITimerCallback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+ virtual ~GenericTimerCallbackBase() {}
+};
+
+// An nsITimerCallback implementation that can be used with any function
+// object that's callable with no arguments.
+template <typename Function>
+class GenericTimerCallback final : public GenericTimerCallbackBase
+{
+public:
+ explicit GenericTimerCallback(const Function& aFunction) : mFunction(aFunction) {}
+
+ NS_IMETHOD Notify(nsITimer*) override
+ {
+ mFunction();
+ return NS_OK;
+ }
+private:
+ Function mFunction;
+};
+
+// Convenience function for constructing a GenericTimerCallback.
+// Returns a raw pointer, suitable for passing directly as an argument to
+// nsITimer::InitWithCallback(). The intention is to enable the following
+// terse inline usage:
+// timer->InitWithCallback(NewTimerCallback([](){ ... }), delay);
+template <typename Function>
+GenericTimerCallback<Function>* NewTimerCallback(const Function& aFunction)
+{
+ return new GenericTimerCallback<Function>(aFunction);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_APZThreadUtils_h */
diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp
new file mode 100644
index 000000000..20d34aa2b
--- /dev/null
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ActiveElementManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/StyleSetHandle.h"
+#include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/Preferences.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "mozilla/dom/Element.h"
+#include "nsIDocument.h"
+#include "nsStyleSet.h"
+
+#define AEM_LOG(...)
+// #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+static int32_t sActivationDelayMs = 100;
+static bool sActivationDelayMsSet = false;
+
+ActiveElementManager::ActiveElementManager()
+ : mCanBePan(false),
+ mCanBePanSet(false),
+ mSetActiveTask(nullptr),
+ mActiveElementUsesStyle(false)
+{
+ if (!sActivationDelayMsSet) {
+ Preferences::AddIntVarCache(&sActivationDelayMs,
+ "ui.touch_activation.delay_ms",
+ sActivationDelayMs);
+ sActivationDelayMsSet = true;
+ }
+}
+
+ActiveElementManager::~ActiveElementManager() {}
+
+void
+ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget)
+{
+ if (mTarget) {
+ // Multiple fingers on screen (since HandleTouchEnd clears mTarget).
+ AEM_LOG("Multiple fingers on-screen, clearing target element\n");
+ CancelTask();
+ ResetActive();
+ ResetTouchBlockState();
+ return;
+ }
+
+ mTarget = do_QueryInterface(aTarget);
+ AEM_LOG("Setting target element to %p\n", mTarget.get());
+ TriggerElementActivation();
+}
+
+void
+ActiveElementManager::HandleTouchStart(bool aCanBePan)
+{
+ AEM_LOG("Touch start, aCanBePan: %d\n", aCanBePan);
+ if (mCanBePanSet) {
+ // Multiple fingers on screen (since HandleTouchEnd clears mCanBePanSet).
+ AEM_LOG("Multiple fingers on-screen, clearing touch block state\n");
+ CancelTask();
+ ResetActive();
+ ResetTouchBlockState();
+ return;
+ }
+
+ mCanBePan = aCanBePan;
+ mCanBePanSet = true;
+ TriggerElementActivation();
+}
+
+void
+ActiveElementManager::TriggerElementActivation()
+{
+ // Both HandleTouchStart() and SetTargetElement() call this. They can be
+ // called in either order. One will set mCanBePanSet, and the other, mTarget.
+ // We want to actually trigger the activation once both are set.
+ if (!(mTarget && mCanBePanSet)) {
+ return;
+ }
+
+ // If the touch cannot be a pan, make mTarget :active right away.
+ // Otherwise, wait a bit to see if the user will pan or not.
+ if (!mCanBePan) {
+ SetActive(mTarget);
+ } else {
+ CancelTask(); // this is only needed because of bug 1169802. Fixing that
+ // bug properly should make this unnecessary.
+ MOZ_ASSERT(mSetActiveTask == nullptr);
+
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<nsCOMPtr<dom::Element>>(this,
+ &ActiveElementManager::SetActiveTask,
+ mTarget);
+ mSetActiveTask = task;
+ MessageLoop::current()->PostDelayedTask(task.forget(), sActivationDelayMs);
+ AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask);
+ }
+}
+
+void
+ActiveElementManager::ClearActivation()
+{
+ AEM_LOG("Clearing element activation\n");
+ CancelTask();
+ ResetActive();
+}
+
+void
+ActiveElementManager::HandleTouchEndEvent(bool aWasClick)
+{
+ AEM_LOG("Touch end event, aWasClick: %d\n", aWasClick);
+
+ // If the touch was a click, make mTarget :active right away.
+ // nsEventStateManager will reset the active element when processing
+ // the mouse-down event generated by the click.
+ CancelTask();
+ if (aWasClick) {
+ SetActive(mTarget);
+ } else {
+ // We might reach here if mCanBePan was false on touch-start and
+ // so we set the element active right away. Now it turns out the
+ // action was not a click so we need to reset the active element.
+ ResetActive();
+ }
+
+ ResetTouchBlockState();
+}
+
+void
+ActiveElementManager::HandleTouchEnd()
+{
+ AEM_LOG("Touch end, clearing pan state\n");
+ mCanBePanSet = false;
+}
+
+bool
+ActiveElementManager::ActiveElementUsesStyle() const
+{
+ return mActiveElementUsesStyle;
+}
+
+static nsPresContext*
+GetPresContextFor(nsIContent* aContent)
+{
+ if (!aContent) {
+ return nullptr;
+ }
+ nsIPresShell* shell = aContent->OwnerDoc()->GetShell();
+ if (!shell) {
+ return nullptr;
+ }
+ return shell->GetPresContext();
+}
+
+static bool
+ElementHasActiveStyle(dom::Element* aElement)
+{
+ nsPresContext* pc = GetPresContextFor(aElement);
+ if (!pc) {
+ return false;
+ }
+ StyleSetHandle styleSet = pc->StyleSet();
+ for (dom::Element* e = aElement; e; e = e->GetParentElement()) {
+ if (styleSet->HasStateDependentStyle(e, NS_EVENT_STATE_ACTIVE)) {
+ AEM_LOG("Element %p's style is dependent on the active state\n", e);
+ return true;
+ }
+ }
+ AEM_LOG("Element %p doesn't use active styles\n", aElement);
+ return false;
+}
+
+void
+ActiveElementManager::SetActive(dom::Element* aTarget)
+{
+ AEM_LOG("Setting active %p\n", aTarget);
+
+ if (nsPresContext* pc = GetPresContextFor(aTarget)) {
+ pc->EventStateManager()->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE);
+ mActiveElementUsesStyle = ElementHasActiveStyle(aTarget);
+ }
+}
+
+void
+ActiveElementManager::ResetActive()
+{
+ AEM_LOG("Resetting active from %p\n", mTarget.get());
+
+ // Clear the :active flag from mTarget by setting it on the document root.
+ if (mTarget) {
+ dom::Element* root = mTarget->OwnerDoc()->GetDocumentElement();
+ if (root) {
+ AEM_LOG("Found root %p, making active\n", root);
+ SetActive(root);
+ }
+ }
+}
+
+void
+ActiveElementManager::ResetTouchBlockState()
+{
+ mTarget = nullptr;
+ mCanBePanSet = false;
+}
+
+void
+ActiveElementManager::SetActiveTask(const nsCOMPtr<dom::Element>& aTarget)
+{
+ AEM_LOG("mSetActiveTask %p running\n", mSetActiveTask);
+
+ // This gets called from mSetActiveTask's Run() method. The message loop
+ // deletes the task right after running it, so we need to null out
+ // mSetActiveTask to make sure we're not left with a dangling pointer.
+ mSetActiveTask = nullptr;
+ SetActive(aTarget);
+}
+
+void
+ActiveElementManager::CancelTask()
+{
+ AEM_LOG("Cancelling task %p\n", mSetActiveTask);
+
+ if (mSetActiveTask) {
+ mSetActiveTask->Cancel();
+ mSetActiveTask = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/ActiveElementManager.h b/gfx/layers/apz/util/ActiveElementManager.h
new file mode 100644
index 000000000..83d0cb29f
--- /dev/null
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ActiveElementManager_h
+#define mozilla_layers_ActiveElementManager_h
+
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace dom {
+class Element;
+class EventTarget;
+} // namespace dom
+
+namespace layers {
+
+/**
+ * Manages setting and clearing the ':active' CSS pseudostate in the presence
+ * of touch input.
+ */
+class ActiveElementManager {
+ ~ActiveElementManager();
+public:
+ NS_INLINE_DECL_REFCOUNTING(ActiveElementManager)
+
+ ActiveElementManager();
+
+ /**
+ * Specify the target of a touch. Typically this should be called right
+ * after HandleTouchStart(), but in cases where the APZ needs to wait for
+ * a content response the HandleTouchStart() may be delayed, in which case
+ * this function can be called first.
+ * |aTarget| may be nullptr.
+ */
+ void SetTargetElement(dom::EventTarget* aTarget);
+ /**
+ * Handle a touch-start state notification from APZ. This notification
+ * may be delayed until after touch listeners have responded to the APZ.
+ * @param aCanBePan whether the touch can be a pan
+ */
+ void HandleTouchStart(bool aCanBePan);
+ /**
+ * Clear the active element.
+ */
+ void ClearActivation();
+ /**
+ * Handle a touch-end or touch-cancel event.
+ * @param aWasClick whether the touch was a click
+ */
+ void HandleTouchEndEvent(bool aWasClick);
+ /**
+ * Handle a touch-end state notification from APZ. This notification may be
+ * delayed until after touch listeners have responded to the APZ.
+ */
+ void HandleTouchEnd();
+ /**
+ * @return true iff the currently active element (or one of its ancestors)
+ * actually had a style for the :active pseudo-class. The currently active
+ * element is the root element if no other elements are active.
+ */
+ bool ActiveElementUsesStyle() const;
+private:
+ /**
+ * The target of the first touch point in the current touch block.
+ */
+ nsCOMPtr<dom::Element> mTarget;
+ /**
+ * Whether the current touch block can be a pan. Set in HandleTouchStart().
+ */
+ bool mCanBePan;
+ /**
+ * Whether mCanBePan has been set for the current touch block.
+ * We need to keep track of this to allow HandleTouchStart() and
+ * SetTargetElement() to be called in either order.
+ */
+ bool mCanBePanSet;
+ /**
+ * A task for calling SetActive() after a timeout.
+ */
+ RefPtr<CancelableRunnable> mSetActiveTask;
+ /**
+ * See ActiveElementUsesStyle() documentation.
+ */
+ bool mActiveElementUsesStyle;
+
+ // Helpers
+ void TriggerElementActivation();
+ void SetActive(dom::Element* aTarget);
+ void ResetActive();
+ void ResetTouchBlockState();
+ void SetActiveTask(const nsCOMPtr<dom::Element>& aTarget);
+ void CancelTask();
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ActiveElementManager_h */
diff --git a/gfx/layers/apz/util/CheckerboardReportService.cpp b/gfx/layers/apz/util/CheckerboardReportService.cpp
new file mode 100644
index 000000000..0924fe92d
--- /dev/null
+++ b/gfx/layers/apz/util/CheckerboardReportService.cpp
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CheckerboardReportService.h"
+
+#include "gfxPrefs.h" // for gfxPrefs
+#include "jsapi.h" // for JS_Now
+#include "MainThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/Assertions.h" // for MOZ_ASSERT
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/Unused.h"
+#include "mozilla/dom/CheckerboardReportServiceBinding.h" // for dom::CheckerboardReports
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "nsContentUtils.h" // for nsContentUtils
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace layers {
+
+/*static*/ StaticRefPtr<CheckerboardEventStorage> CheckerboardEventStorage::sInstance;
+
+/*static*/ already_AddRefed<CheckerboardEventStorage>
+CheckerboardEventStorage::GetInstance()
+{
+ // The instance in the parent process does all the work, so if this is getting
+ // called in the child process something is likely wrong.
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sInstance) {
+ sInstance = new CheckerboardEventStorage();
+ ClearOnShutdown(&sInstance);
+ }
+ RefPtr<CheckerboardEventStorage> instance = sInstance.get();
+ return instance.forget();
+}
+
+void
+CheckerboardEventStorage::Report(uint32_t aSeverity, const std::string& aLog)
+{
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> task = NS_NewRunnableFunction([aSeverity, aLog] () -> void {
+ CheckerboardEventStorage::Report(aSeverity, aLog);
+ });
+ NS_DispatchToMainThread(task.forget());
+ return;
+ }
+
+ if (XRE_IsGPUProcess()) {
+ if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
+ nsCString log(aLog.c_str());
+ Unused << gpu->SendReportCheckerboard(aSeverity, log);
+ }
+ return;
+ }
+
+ RefPtr<CheckerboardEventStorage> storage = GetInstance();
+ storage->ReportCheckerboard(aSeverity, aLog);
+}
+
+void
+CheckerboardEventStorage::ReportCheckerboard(uint32_t aSeverity, const std::string& aLog)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (aSeverity == 0) {
+ // This code assumes all checkerboard reports have a nonzero severity.
+ return;
+ }
+
+ CheckerboardReport severe(aSeverity, JS_Now(), aLog);
+ CheckerboardReport recent;
+
+ // First look in the "severe" reports to see if the new one belongs in that
+ // list.
+ for (int i = 0; i < SEVERITY_MAX_INDEX; i++) {
+ if (mCheckerboardReports[i].mSeverity >= severe.mSeverity) {
+ continue;
+ }
+ // The new one deserves to be in the "severe" list. Take the one getting
+ // bumped off the list, and put it in |recent| for possible insertion into
+ // the recents list.
+ recent = mCheckerboardReports[SEVERITY_MAX_INDEX - 1];
+
+ // Shuffle the severe list down, insert the new one.
+ for (int j = SEVERITY_MAX_INDEX - 1; j > i; j--) {
+ mCheckerboardReports[j] = mCheckerboardReports[j - 1];
+ }
+ mCheckerboardReports[i] = severe;
+ severe.mSeverity = 0; // mark |severe| as inserted
+ break;
+ }
+
+ // If |severe.mSeverity| is nonzero, the incoming report didn't get inserted
+ // into the severe list; put it into |recent| for insertion into the recent
+ // list.
+ if (severe.mSeverity) {
+ MOZ_ASSERT(recent.mSeverity == 0, "recent should be empty here");
+ recent = severe;
+ } // else |recent| may hold a report that got knocked out of the severe list.
+
+ if (recent.mSeverity == 0) {
+ // Nothing to be inserted into the recent list.
+ return;
+ }
+
+ // If it wasn't in the "severe" list, add it to the "recent" list.
+ for (int i = SEVERITY_MAX_INDEX; i < RECENT_MAX_INDEX; i++) {
+ if (mCheckerboardReports[i].mTimestamp >= recent.mTimestamp) {
+ continue;
+ }
+ // |recent| needs to be inserted at |i|. Shuffle the remaining ones down
+ // and insert it.
+ for (int j = RECENT_MAX_INDEX - 1; j > i; j--) {
+ mCheckerboardReports[j] = mCheckerboardReports[j - 1];
+ }
+ mCheckerboardReports[i] = recent;
+ break;
+ }
+}
+
+void
+CheckerboardEventStorage::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ for (int i = 0; i < RECENT_MAX_INDEX; i++) {
+ CheckerboardReport& r = mCheckerboardReports[i];
+ if (r.mSeverity == 0) {
+ continue;
+ }
+ dom::CheckerboardReport report;
+ report.mSeverity.Construct() = r.mSeverity;
+ report.mTimestamp.Construct() = r.mTimestamp / 1000; // micros to millis
+ report.mLog.Construct() = NS_ConvertUTF8toUTF16(r.mLog.c_str(), r.mLog.size());
+ report.mReason.Construct() = (i < SEVERITY_MAX_INDEX)
+ ? dom::CheckerboardReason::Severe
+ : dom::CheckerboardReason::Recent;
+ aOutReports.AppendElement(report);
+ }
+}
+
+} // namespace layers
+
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CheckerboardReportService, mParent)
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CheckerboardReportService, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CheckerboardReportService, Release)
+
+/*static*/ bool
+CheckerboardReportService::IsEnabled(JSContext* aCtx, JSObject* aGlobal)
+{
+ // Only allow this in the parent process
+ if (!XRE_IsParentProcess()) {
+ return false;
+ }
+ // Allow privileged code or about:checkerboard (unprivileged) to access this.
+ return nsContentUtils::IsCallerChrome()
+ || nsContentUtils::IsSpecificAboutPage(aGlobal, "about:checkerboard");
+}
+
+/*static*/ already_AddRefed<CheckerboardReportService>
+CheckerboardReportService::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv)
+{
+ RefPtr<CheckerboardReportService> ces = new CheckerboardReportService(aGlobal.GetAsSupports());
+ return ces.forget();
+}
+
+CheckerboardReportService::CheckerboardReportService(nsISupports* aParent)
+ : mParent(aParent)
+{
+}
+
+JSObject*
+CheckerboardReportService::WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto)
+{
+ return CheckerboardReportServiceBinding::Wrap(aCtx, this, aGivenProto);
+}
+
+nsISupports*
+CheckerboardReportService::GetParentObject()
+{
+ return mParent;
+}
+
+void
+CheckerboardReportService::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
+{
+ RefPtr<mozilla::layers::CheckerboardEventStorage> instance =
+ mozilla::layers::CheckerboardEventStorage::GetInstance();
+ MOZ_ASSERT(instance);
+ instance->GetReports(aOutReports);
+}
+
+bool
+CheckerboardReportService::IsRecordingEnabled() const
+{
+ return gfxPrefs::APZRecordCheckerboarding();
+}
+
+void
+CheckerboardReportService::SetRecordingEnabled(bool aEnabled)
+{
+ gfxPrefs::SetAPZRecordCheckerboarding(aEnabled);
+}
+
+void
+CheckerboardReportService::FlushActiveReports()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
+ if (gpu && gpu->NotifyGpuObservers("APZ:FlushActiveCheckerboard")) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc);
+ if (obsSvc) {
+ obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/CheckerboardReportService.h b/gfx/layers/apz/util/CheckerboardReportService.h
new file mode 100644
index 000000000..743b29825
--- /dev/null
+++ b/gfx/layers/apz/util/CheckerboardReportService.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CheckerboardReportService_h
+#define mozilla_dom_CheckerboardReportService_h
+
+#include <string>
+
+#include "js/TypeDecls.h" // for JSContext, JSObject
+#include "mozilla/ErrorResult.h" // for ErrorResult
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsWrapperCache.h" // for nsWrapperCache
+
+namespace mozilla {
+
+namespace dom {
+struct CheckerboardReport;
+}
+
+namespace layers {
+
+// CheckerboardEventStorage is a singleton that stores info on checkerboard
+// events, so that they can be accessed from about:checkerboard and visualized.
+// Note that this class is NOT threadsafe, and all methods must be called on
+// the main thread.
+class CheckerboardEventStorage
+{
+ NS_INLINE_DECL_REFCOUNTING(CheckerboardEventStorage)
+
+public:
+ /**
+ * Get the singleton instance.
+ */
+ static already_AddRefed<CheckerboardEventStorage> GetInstance();
+
+ /**
+ * Get the stored checkerboard reports.
+ */
+ void GetReports(nsTArray<dom::CheckerboardReport>& aOutReports);
+
+ /**
+ * Save a checkerboard event log, optionally dropping older ones that were
+ * less severe or less recent. Zero-severity reports may be ignored entirely.
+ */
+ static void Report(uint32_t aSeverity, const std::string& aLog);
+
+private:
+ /* Stuff for refcounted singleton */
+ CheckerboardEventStorage() {}
+ virtual ~CheckerboardEventStorage() {}
+
+ static StaticRefPtr<CheckerboardEventStorage> sInstance;
+
+ void ReportCheckerboard(uint32_t aSeverity, const std::string& aLog);
+
+private:
+ /**
+ * Struct that this class uses internally to store a checkerboard report.
+ */
+ struct CheckerboardReport {
+ uint32_t mSeverity; // if 0, this report is empty
+ int64_t mTimestamp; // microseconds since epoch, as from JS_Now()
+ std::string mLog;
+
+ CheckerboardReport()
+ : mSeverity(0)
+ , mTimestamp(0)
+ {}
+
+ CheckerboardReport(uint32_t aSeverity, int64_t aTimestamp,
+ const std::string& aLog)
+ : mSeverity(aSeverity)
+ , mTimestamp(aTimestamp)
+ , mLog(aLog)
+ {}
+ };
+
+ // The first 5 (indices 0-4) are the most severe ones in decreasing order
+ // of severity; the next 5 (indices 5-9) are the most recent ones that are
+ // not already in the "severe" list.
+ static const int SEVERITY_MAX_INDEX = 5;
+ static const int RECENT_MAX_INDEX = 10;
+ CheckerboardReport mCheckerboardReports[RECENT_MAX_INDEX];
+};
+
+} // namespace layers
+
+namespace dom {
+
+class GlobalObject;
+
+/**
+ * CheckerboardReportService is a wrapper object that allows access to the
+ * stuff in CheckerboardEventStorage (above). We need this wrapper for proper
+ * garbage/cycle collection, since this can be accessed from JS.
+ */
+class CheckerboardReportService : public nsWrapperCache
+{
+public:
+ /**
+ * Check if the given page is allowed to access this object via the WebIDL
+ * bindings. It only returns true if the page is about:checkerboard.
+ */
+ static bool IsEnabled(JSContext* aCtx, JSObject* aGlobal);
+
+ /*
+ * Other standard WebIDL binding glue.
+ */
+
+ static already_AddRefed<CheckerboardReportService>
+ Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv);
+
+ explicit CheckerboardReportService(nsISupports* aSupports);
+
+ JSObject* WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto) override;
+
+ nsISupports* GetParentObject();
+
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CheckerboardReportService)
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CheckerboardReportService)
+
+public:
+ /*
+ * The methods exposed via the webidl.
+ */
+ void GetReports(nsTArray<dom::CheckerboardReport>& aOutReports);
+ bool IsRecordingEnabled() const;
+ void SetRecordingEnabled(bool aEnabled);
+ void FlushActiveReports();
+
+private:
+ virtual ~CheckerboardReportService() {}
+
+ nsCOMPtr<nsISupports> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_layers_CheckerboardReportService_h */
diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp
new file mode 100644
index 000000000..ac8b3824f
--- /dev/null
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ChromeProcessController.h"
+
+#include "MainThreadUtils.h" // for NS_IsMainThread()
+#include "base/message_loop.h" // for MessageLoop
+#include "mozilla/dom/Element.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/DoubleTapToZoom.h"
+#include "nsIDocument.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIPresShell.h"
+#include "nsLayoutUtils.h"
+#include "nsView.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+
+ChromeProcessController::ChromeProcessController(nsIWidget* aWidget,
+ APZEventState* aAPZEventState,
+ IAPZCTreeManager* aAPZCTreeManager)
+ : mWidget(aWidget)
+ , mAPZEventState(aAPZEventState)
+ , mAPZCTreeManager(aAPZCTreeManager)
+ , mUILoop(MessageLoop::current())
+{
+ // Otherwise we're initializing mUILoop incorrectly.
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aAPZEventState);
+ MOZ_ASSERT(aAPZCTreeManager);
+
+ mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::InitializeRoot));
+}
+
+ChromeProcessController::~ChromeProcessController() {}
+
+void
+ChromeProcessController::InitializeRoot()
+{
+ APZCCallbackHelper::InitializeRootDisplayport(GetPresShell());
+}
+
+void
+ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ FrameMetrics metrics = aFrameMetrics;
+ if (metrics.IsRootContent()) {
+ APZCCallbackHelper::UpdateRootFrame(metrics);
+ } else {
+ APZCCallbackHelper::UpdateSubFrame(metrics);
+ }
+}
+
+void
+ChromeProcessController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
+{
+ MessageLoop::current()->PostDelayedTask(Move(aTask), aDelayMs);
+}
+
+bool
+ChromeProcessController::IsRepaintThread()
+{
+ return NS_IsMainThread();
+}
+
+void
+ChromeProcessController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ NS_DispatchToMainThread(Move(aTask));
+}
+
+void
+ChromeProcessController::Destroy()
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod(this, &ChromeProcessController::Destroy));
+ return;
+ }
+
+ MOZ_ASSERT(MessageLoop::current() == mUILoop);
+ mWidget = nullptr;
+ mAPZEventState = nullptr;
+}
+
+nsIPresShell*
+ChromeProcessController::GetPresShell() const
+{
+ if (!mWidget) {
+ return nullptr;
+ }
+ if (nsView* view = nsView::GetViewFor(mWidget)) {
+ return view->GetPresShell();
+ }
+ return nullptr;
+}
+
+nsIDocument*
+ChromeProcessController::GetRootDocument() const
+{
+ if (nsIPresShell* presShell = GetPresShell()) {
+ return presShell->GetDocument();
+ }
+ return nullptr;
+}
+
+nsIDocument*
+ChromeProcessController::GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const
+{
+ nsIContent* content = nsLayoutUtils::FindContentFor(aScrollId);
+ if (!content) {
+ return nullptr;
+ }
+ nsIPresShell* presShell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content);
+ if (presShell) {
+ return presShell->GetDocument();
+ }
+ return nullptr;
+}
+
+void
+ChromeProcessController::HandleDoubleTap(const mozilla::CSSPoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid)
+{
+ MOZ_ASSERT(MessageLoop::current() == mUILoop);
+
+ nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
+ if (!document.get()) {
+ return;
+ }
+
+ // CalculateRectToZoomTo performs a hit test on the frame associated with the
+ // Root Content Document. Unfortunately that frame does not know about the
+ // resolution of the document and so we must remove it before calculating
+ // the zoomToRect.
+ nsIPresShell* presShell = document->GetShell();
+ const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
+ CSSPoint point(aPoint.x / resolution, aPoint.y / resolution);
+ CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
+
+ uint32_t presShellId;
+ FrameMetrics::ViewID viewId;
+ if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+ document->GetDocumentElement(), &presShellId, &viewId)) {
+ mAPZCTreeManager->ZoomToRect(
+ ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
+ }
+}
+
+void
+ChromeProcessController::HandleTap(TapType aType,
+ const mozilla::LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t>(this,
+ &ChromeProcessController::HandleTap,
+ aType, aPoint, aModifiers, aGuid, aInputBlockId));
+ return;
+ }
+
+ if (!mAPZEventState) {
+ return;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ if (!presShell) {
+ return;
+ }
+ if (!presShell->GetPresContext()) {
+ return;
+ }
+ CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
+ CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
+
+ switch (aType) {
+ case TapType::eSingleTap:
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
+ break;
+ case TapType::eDoubleTap:
+ HandleDoubleTap(point, aModifiers, aGuid);
+ break;
+ case TapType::eSecondTap:
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
+ break;
+ case TapType::eLongTap:
+ mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
+ aInputBlockId);
+ break;
+ case TapType::eLongTapUp:
+ mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+ break;
+ case TapType::eSentinel:
+ // Should never happen, but we need to handle this case branch for the
+ // compiler to be happy.
+ MOZ_ASSERT(false);
+ break;
+ }
+}
+
+void
+ChromeProcessController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <PinchGestureInput::PinchGestureType,
+ ScrollableLayerGuid,
+ LayoutDeviceCoord,
+ Modifiers>(this,
+ &ChromeProcessController::NotifyPinchGesture,
+ aType, aGuid, aSpanChange, aModifiers));
+ return;
+ }
+
+ if (mWidget) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mWidget.get());
+ }
+}
+
+void
+ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <ScrollableLayerGuid,
+ APZStateChange,
+ int>(this, &ChromeProcessController::NotifyAPZStateChange,
+ aGuid, aChange, aArg));
+ return;
+ }
+
+ if (!mAPZEventState) {
+ return;
+ }
+
+ mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg);
+}
+
+void
+ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
+{
+ if (MessageLoop::current() != mUILoop) {
+ mUILoop->PostTask(NewRunnableMethod
+ <FrameMetrics::ViewID,
+ nsString>(this, &ChromeProcessController::NotifyMozMouseScrollEvent,
+ aScrollId, aEvent));
+ return;
+ }
+
+ APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
+}
+
+void
+ChromeProcessController::NotifyFlushComplete()
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
+}
diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h
new file mode 100644
index 000000000..9a43297d4
--- /dev/null
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ChromeProcessController_h
+#define mozilla_layers_ChromeProcessController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class nsIDOMWindowUtils;
+class nsIDocument;
+class nsIPresShell;
+class nsIWidget;
+
+class MessageLoop;
+
+namespace mozilla {
+
+namespace layers {
+
+class IAPZCTreeManager;
+class APZEventState;
+
+/**
+ * ChromeProcessController is a GeckoContentController attached to the root of
+ * a compositor's layer tree. It's used directly by APZ by default, and remoted
+ * using PAPZ if there is a gpu process.
+ *
+ * If ChromeProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
+class ChromeProcessController : public mozilla::layers::GeckoContentController
+{
+protected:
+ typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
+
+public:
+ explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState, IAPZCTreeManager* aAPZCTreeManager);
+ ~ChromeProcessController();
+ virtual void Destroy() override;
+
+ // GeckoContentController interface
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
+ virtual bool IsRepaintThread() override;
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+ virtual void HandleTap(TapType aType,
+ const mozilla::LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+ virtual void NotifyFlushComplete() override;
+private:
+ nsCOMPtr<nsIWidget> mWidget;
+ RefPtr<APZEventState> mAPZEventState;
+ RefPtr<IAPZCTreeManager> mAPZCTreeManager;
+ MessageLoop* mUILoop;
+
+ void InitializeRoot();
+ nsIPresShell* GetPresShell() const;
+ nsIDocument* GetRootDocument() const;
+ nsIDocument* GetRootContentDocument(const FrameMetrics::ViewID& aScrollId) const;
+ void HandleDoubleTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ChromeProcessController_h */
diff --git a/gfx/layers/apz/util/ContentProcessController.cpp b/gfx/layers/apz/util/ContentProcessController.cpp
new file mode 100644
index 000000000..eccd4179f
--- /dev/null
+++ b/gfx/layers/apz/util/ContentProcessController.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContentProcessController.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZChild.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * There are cases where we try to create the APZChild before the corresponding
+ * TabChild has been created, we use an observer for the "tab-child-created"
+ * topic to set the TabChild in the APZChild when it has been created.
+ */
+class TabChildCreatedObserver : public nsIObserver
+{
+public:
+ TabChildCreatedObserver(ContentProcessController* aController, const dom::TabId& aTabId)
+ : mController(aController),
+ mTabId(aTabId)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ virtual ~TabChildCreatedObserver()
+ {}
+
+ // TabChildCreatedObserver is owned by mController, and mController outlives its
+ // TabChildCreatedObserver, so the raw pointer is fine.
+ ContentProcessController* mController;
+ dom::TabId mTabId;
+};
+
+NS_IMPL_ISUPPORTS(TabChildCreatedObserver, nsIObserver)
+
+NS_IMETHODIMP
+TabChildCreatedObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(strcmp(aTopic, "tab-child-created") == 0);
+
+ nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(aSubject));
+ NS_ENSURE_TRUE(tabChild, NS_ERROR_FAILURE);
+
+ dom::TabChild* browser = static_cast<dom::TabChild*>(tabChild.get());
+
+ if (browser->GetTabId() == mTabId) {
+ mController->SetBrowser(browser);
+ }
+ return NS_OK;
+}
+
+APZChild*
+ContentProcessController::Create(const dom::TabId& aTabId)
+{
+ RefPtr<dom::TabChild> browser = dom::TabChild::FindTabChild(aTabId);
+
+ ContentProcessController* controller = new ContentProcessController();
+
+ nsAutoPtr<APZChild> apz(new APZChild(controller));
+
+ if (browser) {
+
+ controller->SetBrowser(browser);
+
+ } else {
+
+ RefPtr<TabChildCreatedObserver> observer =
+ new TabChildCreatedObserver(controller, aTabId);
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os ||
+ NS_FAILED(os->AddObserver(observer, "tab-child-created", false))) {
+ return nullptr;
+ }
+ controller->SetObserver(observer);
+
+ }
+
+ return apz.forget();
+}
+
+ContentProcessController::ContentProcessController()
+ : mBrowser(nullptr)
+{
+}
+ContentProcessController::~ContentProcessController()
+{
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->RemoveObserver(mObserver, "tab-child-created");
+ }
+}
+
+void
+ContentProcessController::SetObserver(nsIObserver* aObserver)
+{
+ MOZ_ASSERT(!mBrowser);
+ mObserver = aObserver;
+}
+
+void
+ContentProcessController::SetBrowser(dom::TabChild* aBrowser)
+{
+ MOZ_ASSERT(!mBrowser);
+ mBrowser = aBrowser;
+
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->RemoveObserver(mObserver, "tab-child-created");
+ mObserver = nullptr;
+ }
+}
+void
+ContentProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ if (mBrowser) {
+ mBrowser->UpdateFrame(aFrameMetrics);
+ }
+}
+
+void
+ContentProcessController::HandleTap(
+ TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ // This should never get called
+ MOZ_ASSERT(false);
+}
+
+void
+ContentProcessController::NotifyPinchGesture(
+ PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ // This should never get called
+ MOZ_ASSERT_UNREACHABLE("Unexpected message to content process");
+}
+
+void
+ContentProcessController::NotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (mBrowser) {
+ mBrowser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg);
+ }
+}
+
+void
+ContentProcessController::NotifyMozMouseScrollEvent(
+ const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ if (mBrowser) {
+ APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void
+ContentProcessController::NotifyFlushComplete()
+{
+ if (mBrowser) {
+ nsCOMPtr<nsIPresShell> shell;
+ if (nsCOMPtr<nsIDocument> doc = mBrowser->GetDocument()) {
+ shell = doc->GetShell();
+ }
+ APZCCallbackHelper::NotifyFlushComplete(shell.get());
+ }
+}
+
+void
+ContentProcessController::PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs)
+{
+ MOZ_ASSERT_UNREACHABLE("ContentProcessController should only be used remotely.");
+}
+
+bool
+ContentProcessController::IsRepaintThread()
+{
+ return NS_IsMainThread();
+}
+
+void
+ContentProcessController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ NS_DispatchToMainThread(Move(aTask));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/ContentProcessController.h b/gfx/layers/apz/util/ContentProcessController.h
new file mode 100644
index 000000000..07d113c9e
--- /dev/null
+++ b/gfx/layers/apz/util/ContentProcessController.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ContentProcessController_h
+#define mozilla_layers_ContentProcessController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+
+class nsIObserver;
+
+namespace mozilla {
+
+namespace dom {
+class TabChild;
+} // namespace dom
+
+namespace layers {
+
+class APZChild;
+
+/**
+ * ContentProcessController is a GeckoContentController for a TabChild, and is always
+ * remoted using PAPZ/APZChild.
+ *
+ * ContentProcessController is created in ContentChild when a layer tree id has
+ * been allocated for a PBrowser that lives in that content process, and is destroyed
+ * when the Destroy message is received, or when the tab dies.
+ *
+ * If ContentProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
+class ContentProcessController final
+ : public GeckoContentController
+{
+public:
+ ~ContentProcessController();
+
+ static APZChild* Create(const dom::TabId& aTabId);
+
+ // ContentProcessController
+
+ void SetBrowser(dom::TabChild* aBrowser);
+
+ // GeckoContentController
+
+ void RequestContentRepaint(const FrameMetrics& frame) override;
+
+ void HandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+
+ void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+
+ void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ void NotifyFlushComplete() override;
+
+ void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
+
+ bool IsRepaintThread() override;
+
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+private:
+ ContentProcessController();
+
+ void SetObserver(nsIObserver* aObserver);
+
+ RefPtr<dom::TabChild> mBrowser;
+ RefPtr<nsIObserver> mObserver;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_ContentProcessController_h
diff --git a/gfx/layers/apz/util/DoubleTapToZoom.cpp b/gfx/layers/apz/util/DoubleTapToZoom.cpp
new file mode 100644
index 000000000..62dd5feaa
--- /dev/null
+++ b/gfx/layers/apz/util/DoubleTapToZoom.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DoubleTapToZoom.h"
+
+#include <algorithm> // for std::min, std::max
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/dom/Element.h"
+#include "nsCOMPtr.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMHTMLLIElement.h"
+#include "nsIDOMHTMLQuoteElement.h"
+#include "nsIDOMWindow.h"
+#include "nsIFrame.h"
+#include "nsIFrameInlines.h"
+#include "nsIPresShell.h"
+#include "nsLayoutUtils.h"
+#include "nsStyleConsts.h"
+
+namespace mozilla {
+namespace layers {
+
+// Returns the DOM element found at |aPoint|, interpreted as being relative to
+// the root frame of |aShell|. If the point is inside a subdocument, returns
+// an element inside the subdocument, rather than the subdocument element
+// (and does so recursively).
+// The implementation was adapted from nsDocument::ElementFromPoint(), with
+// the notable exception that we don't pass nsLayoutUtils::IGNORE_CROSS_DOC
+// to GetFrameForPoint(), so as to get the behaviour described above in the
+// presence of subdocuments.
+static already_AddRefed<dom::Element>
+ElementFromPoint(const nsCOMPtr<nsIPresShell>& aShell,
+ const CSSPoint& aPoint)
+{
+ if (nsIFrame* rootFrame = aShell->GetRootFrame()) {
+ if (nsIFrame* frame = nsLayoutUtils::GetFrameForPoint(rootFrame,
+ CSSPoint::ToAppUnits(aPoint),
+ nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+ nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME)) {
+ while (frame && (!frame->GetContent() || frame->GetContent()->IsInAnonymousSubtree())) {
+ frame = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
+ }
+ nsIContent* content = frame->GetContent();
+ if (content && !content->IsElement()) {
+ content = content->GetParent();
+ }
+ if (content) {
+ nsCOMPtr<dom::Element> result = content->AsElement();
+ return result.forget();
+ }
+ }
+ }
+ return nullptr;
+}
+
+static bool
+ShouldZoomToElement(const nsCOMPtr<dom::Element>& aElement) {
+ if (nsIFrame* frame = aElement->GetPrimaryFrame()) {
+ if (frame->GetDisplay() == StyleDisplay::Inline) {
+ return false;
+ }
+ }
+ if (aElement->IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::q)) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+IsRectZoomedIn(const CSSRect& aRect, const CSSRect& aCompositedArea)
+{
+ // This functions checks to see if the area of the rect visible in the
+ // composition bounds (i.e. the overlapArea variable below) is approximately
+ // the max area of the rect we can show.
+ CSSRect overlap = aCompositedArea.Intersect(aRect);
+ float overlapArea = overlap.width * overlap.height;
+ float availHeight = std::min(aRect.width * aCompositedArea.height / aCompositedArea.width,
+ aRect.height);
+ float showing = overlapArea / (aRect.width * availHeight);
+ float ratioW = aRect.width / aCompositedArea.width;
+ float ratioH = aRect.height / aCompositedArea.height;
+
+ return showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9);
+}
+
+CSSRect
+CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument,
+ const CSSPoint& aPoint)
+{
+ // Ensure the layout information we get is up-to-date.
+ aRootContentDocument->FlushPendingNotifications(Flush_Layout);
+
+ // An empty rect as return value is interpreted as "zoom out".
+ const CSSRect zoomOut;
+
+ nsCOMPtr<nsIPresShell> shell = aRootContentDocument->GetShell();
+ if (!shell) {
+ return zoomOut;
+ }
+
+ nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable();
+ if (!rootScrollFrame) {
+ return zoomOut;
+ }
+
+ nsCOMPtr<dom::Element> element = ElementFromPoint(shell, aPoint);
+ if (!element) {
+ return zoomOut;
+ }
+
+ while (element && !ShouldZoomToElement(element)) {
+ element = element->GetParentElement();
+ }
+
+ if (!element) {
+ return zoomOut;
+ }
+
+ FrameMetrics metrics = nsLayoutUtils::CalculateBasicFrameMetrics(rootScrollFrame);
+ CSSRect compositedArea(metrics.GetScrollOffset(), metrics.CalculateCompositedSizeInCssPixels());
+ const CSSCoord margin = 15;
+ CSSRect rect = nsLayoutUtils::GetBoundingContentRect(element, rootScrollFrame);
+
+ // If the element is taller than the visible area of the page scale
+ // the height of the |rect| so that it has the same aspect ratio as
+ // the root frame. The clipped |rect| is centered on the y value of
+ // the touch point. This allows tall narrow elements to be zoomed.
+ if (!rect.IsEmpty() && compositedArea.width > 0.0f) {
+ const float widthRatio = rect.width / compositedArea.width;
+ float targetHeight = compositedArea.height * widthRatio;
+ if (widthRatio < 0.9 && targetHeight < rect.height) {
+ const CSSPoint scrollPoint = CSSPoint::FromAppUnits(rootScrollFrame->GetScrollPosition());
+ float newY = aPoint.y + scrollPoint.y - (targetHeight * 0.5f);
+ if ((newY + targetHeight) > (rect.y + rect.height)) {
+ rect.y += rect.height - targetHeight;
+ } else if (newY > rect.y) {
+ rect.y = newY;
+ }
+ rect.height = targetHeight;
+ }
+ }
+
+ rect = CSSRect(std::max(metrics.GetScrollableRect().x, rect.x - margin),
+ rect.y,
+ rect.width + 2 * margin,
+ rect.height);
+ // Constrict the rect to the screen's right edge
+ rect.width = std::min(rect.width, metrics.GetScrollableRect().XMost() - rect.x);
+
+ // If the rect is already taking up most of the visible area and is
+ // stretching the width of the page, then we want to zoom out instead.
+ if (IsRectZoomedIn(rect, compositedArea)) {
+ return zoomOut;
+ }
+
+ CSSRect rounded(rect);
+ rounded.Round();
+
+ // If the block we're zooming to is really tall, and the user double-tapped
+ // more than a screenful of height from the top of it, then adjust the
+ // y-coordinate so that we center the actual point the user double-tapped
+ // upon. This prevents flying to the top of the page when double-tapping
+ // to zoom in (bug 761721). The 1.2 multiplier is just a little fuzz to
+ // compensate for 'rect' including horizontal margins but not vertical ones.
+ CSSCoord cssTapY = metrics.GetScrollOffset().y + aPoint.y;
+ if ((rect.height > rounded.height) && (cssTapY > rounded.y + (rounded.height * 1.2))) {
+ rounded.y = cssTapY - (rounded.height / 2);
+ }
+
+ return rounded;
+}
+
+}
+}
diff --git a/gfx/layers/apz/util/DoubleTapToZoom.h b/gfx/layers/apz/util/DoubleTapToZoom.h
new file mode 100644
index 000000000..7b8723865
--- /dev/null
+++ b/gfx/layers/apz/util/DoubleTapToZoom.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_DoubleTapToZoom_h
+#define mozilla_layers_DoubleTapToZoom_h
+
+#include "Units.h"
+
+class nsIDocument;
+template<class T> class nsCOMPtr;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * For a double tap at |aPoint|, return the rect to which the browser
+ * should zoom in response, or an empty rect if the browser should zoom out.
+ * |aDocument| should be the root content document for the content that was
+ * tapped.
+ */
+CSSRect CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument,
+ const CSSPoint& aPoint);
+
+}
+}
+
+#endif /* mozilla_layers_DoubleTapToZoom_h */
diff --git a/gfx/layers/apz/util/InputAPZContext.cpp b/gfx/layers/apz/util/InputAPZContext.cpp
new file mode 100644
index 000000000..af5bd9f0f
--- /dev/null
+++ b/gfx/layers/apz/util/InputAPZContext.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputAPZContext.h"
+
+namespace mozilla {
+namespace layers {
+
+ScrollableLayerGuid InputAPZContext::sGuid;
+uint64_t InputAPZContext::sBlockId = 0;
+nsEventStatus InputAPZContext::sApzResponse = nsEventStatus_eIgnore;
+bool InputAPZContext::sRoutedToChildProcess = false;
+
+/*static*/ ScrollableLayerGuid
+InputAPZContext::GetTargetLayerGuid()
+{
+ return sGuid;
+}
+
+/*static*/ uint64_t
+InputAPZContext::GetInputBlockId()
+{
+ return sBlockId;
+}
+
+/*static*/ nsEventStatus
+InputAPZContext::GetApzResponse()
+{
+ return sApzResponse;
+}
+
+/*static*/ void
+InputAPZContext::SetRoutedToChildProcess()
+{
+ sRoutedToChildProcess = true;
+}
+
+InputAPZContext::InputAPZContext(const ScrollableLayerGuid& aGuid,
+ const uint64_t& aBlockId,
+ const nsEventStatus& aApzResponse)
+ : mOldGuid(sGuid)
+ , mOldBlockId(sBlockId)
+ , mOldApzResponse(sApzResponse)
+ , mOldRoutedToChildProcess(sRoutedToChildProcess)
+{
+ sGuid = aGuid;
+ sBlockId = aBlockId;
+ sApzResponse = aApzResponse;
+ sRoutedToChildProcess = false;
+}
+
+InputAPZContext::~InputAPZContext()
+{
+ sGuid = mOldGuid;
+ sBlockId = mOldBlockId;
+ sApzResponse = mOldApzResponse;
+ sRoutedToChildProcess = mOldRoutedToChildProcess;
+}
+
+bool
+InputAPZContext::WasRoutedToChildProcess()
+{
+ return sRoutedToChildProcess;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/InputAPZContext.h b/gfx/layers/apz/util/InputAPZContext.h
new file mode 100644
index 000000000..0f232e1cb
--- /dev/null
+++ b/gfx/layers/apz/util/InputAPZContext.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_InputAPZContext_h
+#define mozilla_layers_InputAPZContext_h
+
+#include "FrameMetrics.h"
+#include "mozilla/EventForwards.h"
+
+namespace mozilla {
+namespace layers {
+
+// InputAPZContext is used to communicate the ScrollableLayerGuid,
+// input block ID, APZ response from nsIWidget to RenderFrameParent.
+// It is conceptually attached to any WidgetInputEvent
+// that has been processed by APZ directly from a widget.
+class MOZ_STACK_CLASS InputAPZContext
+{
+private:
+ static ScrollableLayerGuid sGuid;
+ static uint64_t sBlockId;
+ static nsEventStatus sApzResponse;
+ static bool sRoutedToChildProcess;
+
+public:
+ static ScrollableLayerGuid GetTargetLayerGuid();
+ static uint64_t GetInputBlockId();
+ static nsEventStatus GetApzResponse();
+ static void SetRoutedToChildProcess();
+
+ InputAPZContext(const ScrollableLayerGuid& aGuid,
+ const uint64_t& aBlockId,
+ const nsEventStatus& aApzResponse);
+ ~InputAPZContext();
+
+ bool WasRoutedToChildProcess();
+
+private:
+ ScrollableLayerGuid mOldGuid;
+ uint64_t mOldBlockId;
+ nsEventStatus mOldApzResponse;
+ bool mOldRoutedToChildProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_InputAPZContext_h */
diff --git a/gfx/layers/apz/util/ScrollInputMethods.h b/gfx/layers/apz/util/ScrollInputMethods.h
new file mode 100644
index 000000000..ba599cd8b
--- /dev/null
+++ b/gfx/layers/apz/util/ScrollInputMethods.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ScrollInputMethods_h
+#define mozilla_layers_ScrollInputMethods_h
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * An enumeration that lists various input methods used to trigger scrolling.
+ * Used as the values for the SCROLL_INPUT_METHODS telemetry histogram.
+ */
+enum class ScrollInputMethod {
+
+ // === Driven by APZ ===
+
+ ApzTouch, // touch events
+ ApzWheelPixel, // wheel events, pixel scrolling mode
+ ApzWheelLine, // wheel events, line scrolling mode
+ ApzWheelPage, // wheel events, page scrolling mode
+ ApzPanGesture, // pan gesture events (generally triggered by trackpad)
+ ApzScrollbarDrag, // dragging the scrollbar
+
+ // === Driven by the main thread ===
+
+ // Keyboard
+ MainThreadScrollLine, // line scrolling
+ // (generally triggered by up/down arrow keys)
+ MainThreadScrollCharacter, // character scrolling
+ // (generally triggered by left/right arrow keys)
+ MainThreadScrollPage, // page scrolling
+ // (generally triggered by PageUp/PageDown keys)
+ MainThreadCompleteScroll, // scrolling to the end of the scroll range
+ // (generally triggered by Home/End keys)
+ MainThreadScrollCaretIntoView, // scrolling to bring the caret into view
+ // after moving the caret via the keyboard
+
+ // Touch
+ MainThreadTouch, // touch events
+
+ // Scrollbar
+ MainThreadScrollbarDrag, // dragging the scrollbar
+ MainThreadScrollbarButtonClick, // clicking the buttons at the ends of the
+ // scrollback track
+ MainThreadScrollbarTrackClick, // clicking the scrollbar track above or
+ // below the thumb
+
+ // Autoscrolling
+ MainThreadAutoscrolling, // autoscrolling
+
+ // New input methods can be added at the end, up to a maximum of 64.
+ // They should only be added at the end, to preserve the numerical values
+ // of the existing enumerators.
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ScrollInputMethods_h */
diff --git a/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp b/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp
new file mode 100644
index 000000000..758b705a3
--- /dev/null
+++ b/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScrollLinkedEffectDetector.h"
+
+#include "nsIDocument.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+uint32_t ScrollLinkedEffectDetector::sDepth = 0;
+bool ScrollLinkedEffectDetector::sFoundScrollLinkedEffect = false;
+
+/* static */ void
+ScrollLinkedEffectDetector::PositioningPropertyMutated()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sDepth > 0) {
+ // We are inside a scroll event dispatch
+ sFoundScrollLinkedEffect = true;
+ }
+}
+
+ScrollLinkedEffectDetector::ScrollLinkedEffectDetector(nsIDocument* aDoc)
+ : mDocument(aDoc)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ sDepth++;
+}
+
+ScrollLinkedEffectDetector::~ScrollLinkedEffectDetector()
+{
+ sDepth--;
+ if (sDepth == 0) {
+ // We have exited all (possibly-nested) scroll event dispatches,
+ // record whether or not we found an effect, and reset state
+ if (sFoundScrollLinkedEffect) {
+ mDocument->ReportHasScrollLinkedEffect();
+ sFoundScrollLinkedEffect = false;
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/ScrollLinkedEffectDetector.h b/gfx/layers/apz/util/ScrollLinkedEffectDetector.h
new file mode 100644
index 000000000..f792586cf
--- /dev/null
+++ b/gfx/layers/apz/util/ScrollLinkedEffectDetector.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ScrollLinkedEffectDetector_h
+#define mozilla_layers_ScrollLinkedEffectDetector_h
+
+#include "mozilla/RefPtr.h"
+
+class nsIDocument;
+
+namespace mozilla {
+namespace layers {
+
+// ScrollLinkedEffectDetector is used to detect the existence of a scroll-linked
+// effect on a webpage. Generally speaking, a scroll-linked effect is something
+// on the page that animates or changes with respect to the scroll position.
+// Content authors usually rely on running some JS in response to the scroll
+// event in order to implement such effects, and therefore it tends to be laggy
+// or work improperly with APZ enabled. This class helps us detect such an
+// effect so that we can warn the author and/or take other preventative
+// measures.
+class MOZ_STACK_CLASS ScrollLinkedEffectDetector
+{
+private:
+ static uint32_t sDepth;
+ static bool sFoundScrollLinkedEffect;
+
+public:
+ static void PositioningPropertyMutated();
+
+ explicit ScrollLinkedEffectDetector(nsIDocument* aDoc);
+ ~ScrollLinkedEffectDetector();
+
+private:
+ RefPtr<nsIDocument> mDocument;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* mozilla_layers_ScrollLinkedEffectDetector_h */
diff --git a/gfx/layers/apz/util/TouchActionHelper.cpp b/gfx/layers/apz/util/TouchActionHelper.cpp
new file mode 100644
index 000000000..b35fd2ec7
--- /dev/null
+++ b/gfx/layers/apz/util/TouchActionHelper.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TouchActionHelper.h"
+
+#include "mozilla/layers/APZCTreeManager.h"
+#include "nsContainerFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+TouchActionHelper::UpdateAllowedBehavior(uint32_t aTouchActionValue,
+ bool aConsiderPanning,
+ TouchBehaviorFlags& aOutBehavior)
+{
+ if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_AUTO) {
+ // Double-tap-zooming need property value AUTO
+ aOutBehavior &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+ if (aTouchActionValue != NS_STYLE_TOUCH_ACTION_MANIPULATION) {
+ // Pinch-zooming need value AUTO or MANIPULATION
+ aOutBehavior &= ~AllowedTouchBehavior::PINCH_ZOOM;
+ }
+ }
+
+ if (aConsiderPanning) {
+ if (aTouchActionValue == NS_STYLE_TOUCH_ACTION_NONE) {
+ aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN;
+ aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
+ }
+
+ // Values pan-x and pan-y set at the same time to the same element do not affect panning constraints.
+ // Therefore we need to check whether pan-x is set without pan-y and the same for pan-y.
+ if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y)) {
+ aOutBehavior &= ~AllowedTouchBehavior::VERTICAL_PAN;
+ } else if ((aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_Y) && !(aTouchActionValue & NS_STYLE_TOUCH_ACTION_PAN_X)) {
+ aOutBehavior &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
+ }
+ }
+}
+
+TouchBehaviorFlags
+TouchActionHelper::GetAllowedTouchBehavior(nsIWidget* aWidget,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aPoint)
+{
+ TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN |
+ AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+
+ nsPoint relativePoint =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, aRootFrame);
+
+ nsIFrame *target = nsLayoutUtils::GetFrameForPoint(aRootFrame, relativePoint, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+ if (!target) {
+ return behavior;
+ }
+ nsIScrollableFrame *nearestScrollableParent = nsLayoutUtils::GetNearestScrollableFrame(target, 0);
+ nsIFrame* nearestScrollableFrame = do_QueryFrame(nearestScrollableParent);
+
+ // We're walking up the DOM tree until we meet the element with touch behavior and accumulating
+ // touch-action restrictions of all elements in this chain.
+ // The exact quote from the spec, that clarifies more:
+ // To determine the effect of a touch, find the nearest ancestor (starting from the element itself)
+ // that has a default touch behavior. Then examine the touch-action property of each element between
+ // the hit tested element and the element with the default touch behavior (including both the hit
+ // tested element and the element with the default touch behavior). If the touch-action property of
+ // any of those elements disallows the default touch behavior, do nothing. Otherwise allow the element
+ // to start considering the touch for the purposes of executing a default touch behavior.
+
+ // Currently we support only two touch behaviors: panning and zooming.
+ // For panning we walk up until we meet the first scrollable element (the element that supports panning)
+ // or root element.
+ // For zooming we walk up until the root element since Firefox currently supports only zooming of the
+ // root frame but not the subframes.
+
+ bool considerPanning = true;
+
+ for (nsIFrame *frame = target; frame && frame->GetContent() && behavior; frame = frame->GetParent()) {
+ UpdateAllowedBehavior(nsLayoutUtils::GetTouchActionFromFrame(frame), considerPanning, behavior);
+
+ if (frame == nearestScrollableFrame) {
+ // We met the scrollable element, after it we shouldn't consider touch-action
+ // values for the purpose of panning but only for zooming.
+ considerPanning = false;
+ }
+ }
+
+ return behavior;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/apz/util/TouchActionHelper.h b/gfx/layers/apz/util/TouchActionHelper.h
new file mode 100644
index 000000000..1dacfd4c0
--- /dev/null
+++ b/gfx/layers/apz/util/TouchActionHelper.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __mozilla_layers_TouchActionHelper_h__
+#define __mozilla_layers_TouchActionHelper_h__
+
+#include "mozilla/layers/APZUtils.h" // for TouchBehaviorFlags
+
+class nsIFrame;
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+/*
+ * Helper class to figure out the allowed touch behavior for frames, as per
+ * the touch-action spec.
+ */
+class TouchActionHelper
+{
+private:
+ static void UpdateAllowedBehavior(uint32_t aTouchActionValue,
+ bool aConsiderPanning,
+ TouchBehaviorFlags& aOutBehavior);
+
+public:
+ /*
+ * Performs hit testing on content, finds frame that corresponds to the aPoint and retrieves
+ * touch-action css property value from it according the rules specified in the spec:
+ * http://www.w3.org/TR/pointerevents/#the-touch-action-css-property.
+ */
+ static TouchBehaviorFlags GetAllowedTouchBehavior(nsIWidget* aWidget,
+ nsIFrame* aRootFrame,
+ const LayoutDeviceIntPoint& aPoint);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /*__mozilla_layers_TouchActionHelper_h__ */
diff --git a/gfx/layers/basic/AutoMaskData.h b/gfx/layers/basic/AutoMaskData.h
new file mode 100644
index 000000000..7bf7f9b3c
--- /dev/null
+++ b/gfx/layers/basic/AutoMaskData.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_AUTOMASKDATA_H_
+#define GFX_AUTOMASKDATA_H_
+
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Drawing with a mask requires a mask surface and a transform.
+ *
+ * This helper class manages the SourceSurface logic.
+ */
+class MOZ_STACK_CLASS AutoMoz2DMaskData {
+public:
+ AutoMoz2DMaskData() { }
+ ~AutoMoz2DMaskData() { }
+
+ void Construct(const gfx::Matrix& aTransform,
+ gfx::SourceSurface* aSurface)
+ {
+ MOZ_ASSERT(!IsConstructed());
+ mTransform = aTransform;
+ mSurface = aSurface;
+ }
+
+ gfx::SourceSurface* GetSurface()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mSurface.get();
+ }
+
+ const gfx::Matrix& GetTransform()
+ {
+ MOZ_ASSERT(IsConstructed());
+ return mTransform;
+ }
+
+private:
+ bool IsConstructed()
+ {
+ return !!mSurface;
+ }
+
+ gfx::Matrix mTransform;
+ RefPtr<gfx::SourceSurface> mSurface;
+
+ AutoMoz2DMaskData(const AutoMoz2DMaskData&) = delete;
+ AutoMoz2DMaskData& operator=(const AutoMoz2DMaskData&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_AUTOMASKDATA_H_
diff --git a/gfx/layers/basic/BasicCanvasLayer.cpp b/gfx/layers/basic/BasicCanvasLayer.cpp
new file mode 100644
index 000000000..83c5c272e
--- /dev/null
+++ b/gfx/layers/basic/BasicCanvasLayer.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicCanvasLayer.h"
+#include "AsyncCanvasRenderer.h"
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "GLContext.h"
+#include "gfxUtils.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "client/TextureClientSharedSurface.h"
+
+class gfxContext;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<SourceSurface>
+BasicCanvasLayer::UpdateSurface()
+{
+ if (mAsyncRenderer) {
+ MOZ_ASSERT(!mBufferProvider);
+ MOZ_ASSERT(!mGLContext);
+ return mAsyncRenderer->GetSurface();
+ }
+
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return nullptr;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return nullptr;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+ MOZ_ASSERT(resultSurf);
+
+ return resultSurf.forget();
+}
+
+void
+BasicCanvasLayer::Paint(DrawTarget* aDT,
+ const Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden())
+ return;
+
+ RefPtr<SourceSurface> surface;
+ if (IsDirty()) {
+ Painted();
+
+ FirePreTransactionCallback();
+ surface = UpdateSurface();
+ FireDidTransactionCallback();
+ }
+
+ bool bufferPoviderSnapshot = false;
+ if (!surface && mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ bufferPoviderSnapshot = !!surface;
+ }
+
+ if (!surface) {
+ return;
+ }
+
+ const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
+
+ Matrix oldTM;
+ if (needsYFlip) {
+ oldTM = aDT->GetTransform();
+ aDT->SetTransform(Matrix(oldTM).
+ PreTranslate(0.0f, mBounds.height).
+ PreScale(1.0f, -1.0f));
+ }
+
+ FillRectWithMask(aDT, aDeviceOffset,
+ Rect(0, 0, mBounds.width, mBounds.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ if (needsYFlip) {
+ aDT->SetTransform(oldTM);
+ }
+
+ if (bufferPoviderSnapshot) {
+ mBufferProvider->ReturnSnapshot(surface.forget());
+ }
+}
+
+already_AddRefed<CanvasLayer>
+BasicLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCanvasLayer.h b/gfx/layers/basic/BasicCanvasLayer.h
new file mode 100644
index 000000000..a63d2b8c0
--- /dev/null
+++ b/gfx/layers/basic/BasicCanvasLayer.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICCANVASLAYER_H
+#define GFX_BASICCANVASLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class BasicCanvasLayer : public CopyableCanvasLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicCanvasLayer(BasicLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ { }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+protected:
+
+ already_AddRefed<gfx::SourceSurface> UpdateSurface();
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicColorLayer.cpp b/gfx/layers/basic/BasicColorLayer.cpp
new file mode 100644
index 000000000..182bc785b
--- /dev/null
+++ b/gfx/layers/basic/BasicColorLayer.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicColorLayer : public ColorLayer, public BasicImplData {
+public:
+ explicit BasicColorLayer(BasicLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicColorLayer);
+ }
+
+protected:
+ virtual ~BasicColorLayer()
+ {
+ MOZ_COUNT_DTOR(BasicColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override
+ {
+ if (IsHidden()) {
+ return;
+ }
+
+ Rect snapped(mBounds.x, mBounds.y, mBounds.width, mBounds.height);
+ MaybeSnapToDevicePixels(snapped, *aDT, true);
+
+ // Clip drawing in case we're using (unbounded) operator source.
+ aDT->PushClipRect(snapped);
+ FillRectWithMask(aDT, aDeviceOffset, snapped, mColor,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+ aDT->PopClip();
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+BasicLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ColorLayer> layer = new BasicColorLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp
new file mode 100644
index 000000000..1ff27f795
--- /dev/null
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -0,0 +1,996 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicCompositor.h"
+#include "BasicLayersImpl.h" // for FillRectWithMask
+#include "TextureHostBasic.h"
+#include "mozilla/layers/Effects.h"
+#include "nsIWidget.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/ssse3-scaler.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/SSE.h"
+#include "gfxUtils.h"
+#include "YCbCrUtils.h"
+#include <algorithm>
+#include "ImageContainer.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+class DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "DataTextureSourceBasic"; }
+
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface)
+ , mWrappingExistingData(!!aSurface)
+ {}
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
+ mSurface = aSurface;
+ return true;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+public:
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
+};
+
+/**
+ * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer
+ * yuv->rgb conversion. The conversion happens when GetSurface is called.
+ */
+class WrappingTextureSourceYCbCrBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ virtual const char* Name() const override { return "WrappingTextureSourceYCbCrBasic"; }
+
+ explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture)
+ : mTexture(aTexture)
+ , mSize(aTexture->GetSize())
+ , mNeedsUpdate(true)
+ {
+ mFromYCBCR = true;
+ }
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ return this;
+ }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() override { return this; }
+
+ virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override
+ {
+ if (mSurface && !mNeedsUpdate) {
+ return mSurface;
+ }
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ if (!mSurface) {
+ mSurface = Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (!mSurface) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+
+ mSurface =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ mSurface);
+ mNeedsUpdate = false;
+ return mSurface;
+ }
+
+ SurfaceFormat GetFormat() const override
+ {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ virtual IntSize GetSize() const override
+ {
+ return mSize;
+ }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override
+ {
+ return false;
+ }
+
+ virtual void DeallocateDeviceData() override
+ {
+ mTexture = nullptr;
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ virtual void Unbind() override
+ {
+ mNeedsUpdate = true;
+ }
+
+ void SetBufferTextureHost(BufferTextureHost* aTexture) override
+ {
+ mTexture = aTexture;
+ mNeedsUpdate = true;
+ }
+
+ void ConvertAndScale(const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize,
+ unsigned char* aDestBuffer,
+ int32_t aStride)
+ {
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+ ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
+ aDestFormat,
+ aDestSize,
+ aDestBuffer,
+ aStride);
+ }
+public:
+ BufferTextureHost* mTexture;
+ const gfx::IntSize mSize;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mNeedsUpdate;
+};
+
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mDidExternalComposition(false)
+ , mIsPendingEndRemoteDrawing(false)
+{
+ MOZ_COUNT_CTOR(BasicCompositor);
+
+ mMaxTextureSize = Factory::GetMaxSurfaceSize(gfxVars::ContentBackend());
+}
+
+BasicCompositor::~BasicCompositor()
+{
+ MOZ_COUNT_DTOR(BasicCompositor);
+}
+
+bool
+BasicCompositor::Initialize(nsCString* const out_failureReason)
+{
+ return mWidget ? mWidget->InitCompositor(this) : false;
+};
+
+int32_t
+BasicCompositor::GetMaxTextureSize() const
+{
+ return mMaxTextureSize;
+}
+
+void
+BasicCompositingRenderTarget::BindRenderTarget()
+{
+ if (mClearOnBind) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mClearOnBind = false;
+ }
+}
+
+void BasicCompositor::DetachWidget()
+{
+ if (mWidget) {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+ mWidget->CleanupRemoteDrawing();
+ }
+ Compositor::DetachWidget();
+}
+
+TextureFactoryIdentifier
+BasicCompositor::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident(LayersBackend::LAYERS_BASIC,
+ XRE_GetProcessType(),
+ GetMaxTextureSize());
+ return ident;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ if (!target) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
+
+ return rt.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const IntPoint &aSourcePoint)
+{
+ MOZ_CRASH("GFX: Shouldn't be called!");
+ return nullptr;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect, BufferMode aBufferMode)
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt;
+ IntRect rect = aRect.ToUnknownRect();
+
+ if (aBufferMode != BufferMode::BUFFER_NONE) {
+ RefPtr<DrawTarget> target = mWidget->GetBackBufferDrawTarget(mDrawTarget, aRect, aClearRect);
+ if (!target) {
+ return nullptr;
+ }
+ MOZ_ASSERT(target != mDrawTarget);
+ rt = new BasicCompositingRenderTarget(target, rect);
+ } else {
+ IntRect windowRect = rect;
+ // Adjust bounds rect to account for new origin at (0, 0).
+ if (windowRect.Size() != mDrawTarget->GetSize()) {
+ windowRect.ExpandToEnclose(IntPoint(0, 0));
+ }
+ rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect);
+ if (!aClearRect.IsEmpty()) {
+ IntRect clearRect = aRect.ToUnknownRect();
+ mDrawTarget->ClearRect(Rect(clearRect - rt->GetOrigin()));
+ }
+ }
+
+ return rt.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSourceBasic> result = new DataTextureSourceBasic(nullptr);
+ if (aFlags & TextureFlags::RGB_FROM_YCBCR) {
+ result->mFromYCBCR = true;
+ }
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture)
+{
+ BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+ MOZ_ASSERT(bufferTexture);
+
+ if (!bufferTexture) {
+ return nullptr;
+ }
+ RefPtr<DataTextureSource> result = new WrappingTextureSourceYCbCrBasic(bufferTexture);
+ return result.forget();
+}
+
+bool
+BasicCompositor::SupportsEffect(EffectTypes aEffect)
+{
+ return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
+}
+
+static void
+DrawSurfaceWithTextureCoords(DrawTarget *aDest,
+ const gfx::Rect& aDestRect,
+ SourceSurface *aSource,
+ const gfx::Rect& aTextureCoords,
+ gfx::SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ SourceSurface *aMask,
+ const Matrix* aMaskTransform)
+{
+ if (!aSource) {
+ gfxWarning() << "DrawSurfaceWithTextureCoords problem " << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
+ return;
+ }
+
+ // Convert aTextureCoords into aSource's coordinate space
+ gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width,
+ aTextureCoords.y * aSource->GetSize().height,
+ aTextureCoords.width * aSource->GetSize().width,
+ aTextureCoords.height * aSource->GetSize().height);
+
+ // Floating point error can accumulate above and we know our visible region
+ // is integer-aligned, so round it out.
+ sourceRect.Round();
+
+ // Compute a transform that maps sourceRect to aDestRect.
+ Matrix matrix =
+ gfxUtils::TransformRectToRect(sourceRect,
+ gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
+
+ // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
+ gfx::Rect unitRect(0, 0, 1, 1);
+ ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
+
+ FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
+ mode, aMask, aMaskTransform, &matrix);
+}
+
+static void
+SetupMask(const EffectChain& aEffectChain,
+ DrawTarget* aDest,
+ const IntPoint& aOffset,
+ RefPtr<SourceSurface>& aMaskSurface,
+ Matrix& aMaskTransform)
+{
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
+ if (!aMaskSurface) {
+ gfxWarning() << "Invalid sourceMask effect";
+ }
+ MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ aMaskTransform = effectMask->mMaskTransform.As2D();
+ aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
+ }
+}
+
+static bool
+AttemptVideoScale(TextureSourceBasic* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3())
+ return false;
+ if (aNewTransform.IsTranslation()) // unscaled painting should take the regular path
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ // If we're not painting to aBuffer the clip will
+ // be applied later
+ IntRect fillRect = dstRect;
+ if (aDest == aBuffer) {
+ // we need to clip fillRect because LockBits ignores the clip on the aDest
+ fillRect = fillRect.Intersect(clipRect);
+ }
+
+ fillRect = fillRect.Intersect(IntRect(IntPoint(0, 0), aDest->GetSize()));
+ IntPoint offset = fillRect.TopLeft() - dstRect.TopLeft();
+
+ RefPtr<DataSourceSurface> srcSource = aSource->GetSurface(aDest)->GetDataSurface();
+ DataSourceSurface::ScopedMap mapSrc(srcSource, DataSourceSurface::READ);
+
+ ssse3_scale_data((uint32_t*)mapSrc.GetData(), srcSource->GetSize().width, srcSource->GetSize().height,
+ mapSrc.GetStride()/4,
+ ((uint32_t*)dstData) + fillRect.x + (dstStride / 4) * fillRect.y, dstRect.width, dstRect.height,
+ dstStride / 4,
+ offset.x, offset.y,
+ fillRect.width, fillRect.height);
+
+ aDest->ReleaseBits(dstData);
+ return true;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+static bool
+AttemptVideoConvertAndScale(TextureSource* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform, const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect,
+ DrawTarget* aDest, const DrawTarget* aBuffer)
+{
+#if defined(XP_WIN) && defined(_M_X64)
+ // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+ return false;
+#endif
+
+ WrappingTextureSourceYCbCrBasic* wrappingSource = aSource->AsWrappingTextureSourceYCbCrBasic();
+ if (!wrappingSource)
+ return false;
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV conversion.
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f)
+ return false;
+ if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
+ return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect))
+ return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
+ return false;
+
+ if (aDest == aBuffer && !clipRect.Contains(dstRect))
+ return false;
+ if (!IntRect(IntPoint(0, 0), aDest->GetSize()).Contains(dstRect))
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ wrappingSource->ConvertAndScale(dstFormat,
+ dstRect.Size(),
+ dstData + ptrdiff_t(dstRect.x) * BytesPerPixel(dstFormat) + ptrdiff_t(dstRect.y) * dstStride,
+ dstStride);
+ aDest->ReleaseBits(dstData);
+ return true;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+void
+BasicCompositor::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
+
+ // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
+ // |dest| is a temporary surface.
+ RefPtr<DrawTarget> dest = buffer;
+
+ AutoRestoreTransform autoRestoreTransform(dest);
+
+ Matrix newTransform;
+ Rect transformBounds;
+ Matrix4x4 new3DTransform;
+ IntPoint offset = mRenderTarget->GetOrigin();
+
+ if (aTransform.Is2D()) {
+ newTransform = aTransform.As2D();
+ } else {
+ // Create a temporary surface for the transform.
+ dest = Factory::CreateDrawTarget(gfxVars::ContentBackend(), RoundedOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
+ if (!dest) {
+ return;
+ }
+
+ dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));
+
+ // Get the bounds post-transform.
+ transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
+ transformBounds.RoundOut();
+
+ if (transformBounds.IsEmpty()) {
+ return;
+ }
+
+ newTransform = Matrix();
+
+ // When we apply the 3D transformation, we do it against a temporary
+ // surface, so undo the coordinate offset.
+ new3DTransform = aTransform;
+ new3DTransform.PreTranslate(aRect.x, aRect.y, 0);
+ }
+
+ // XXX the transform is probably just an integer offset so this whole
+ // business here is a bit silly.
+ Rect transformedClipRect = buffer->GetTransform().TransformBounds(Rect(aClipRect));
+
+ buffer->PushClipRect(Rect(aClipRect));
+
+ newTransform.PostTranslate(-offset.x, -offset.y);
+ buffer->SetTransform(newTransform);
+
+ RefPtr<SourceSurface> sourceMask;
+ Matrix maskTransform;
+ if (aTransform.Is2D()) {
+ SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
+ }
+
+ CompositionOp blendMode = CompositionOp::OP_OVER;
+ if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
+ blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
+ }
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+
+ bool unboundedOp = !IsOperatorBoundByMask(blendMode);
+ if (unboundedOp) {
+ dest->PushClipRect(aRect);
+ }
+
+ FillRectWithMask(dest, aRect, effectSolidColor->mColor,
+ DrawOptions(aOpacity, blendMode), sourceMask, &maskTransform);
+
+ if (unboundedOp) {
+ dest->PopClip();
+ }
+ break;
+ }
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
+
+ if (source && texturedEffect->mPremultiplied) {
+ // we have a fast path for video here
+ if (source->mFromYCBCR &&
+ AttemptVideoConvertAndScale(texturedEffect->mTexture, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in convert and scaling
+ } else if (source->mFromYCBCR &&
+ !source->GetSurface(dest)) {
+ gfxWarning() << "Failed to get YCbCr to rgb surface.";
+ } else if (source->mFromYCBCR &&
+ AttemptVideoScale(source, sourceMask, aOpacity, blendMode,
+ texturedEffect,
+ newTransform, aRect, transformedClipRect,
+ dest, buffer)) {
+ // we succeeded in scaling
+ } else {
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ source->GetSurface(dest),
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else if (source) {
+ SourceSurface* srcSurf = source->GetSurface(dest);
+ if (srcSurf) {
+ RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
+
+ // Yes, we re-create the premultiplied data every time.
+ // This might be better with a cache, eventually.
+ RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ premultData,
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ }
+ } else {
+ gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
+ }
+
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<BasicCompositingRenderTarget> surface
+ = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());
+ RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();
+
+ DrawSurfaceWithTextureCoords(dest, aRect,
+ sourceSurf,
+ effectRenderTarget->mTextureCoords,
+ effectRenderTarget->mSamplingFilter,
+ DrawOptions(aOpacity, blendMode),
+ sourceMask, &maskTransform);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA: {
+ NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ default: {
+ NS_RUNTIMEABORT("Invalid effect type!");
+ break;
+ }
+ }
+
+ if (!aTransform.Is2D()) {
+ dest->Flush();
+
+ RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
+
+ SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
+
+ if (sourceMask) {
+ RefPtr<DrawTarget> transformDT =
+ dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
+ if (transformDT &&
+ transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
+ RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
+
+ // Transform the source by it's normal transform, and then the inverse
+ // of the mask transform so that it's in the mask's untransformed
+ // coordinate space.
+ Matrix sourceTransform = newTransform;
+ sourceTransform.PostTranslate(transformBounds.TopLeft());
+
+ Matrix inverseMask = maskTransform;
+ inverseMask.Invert();
+
+ sourceTransform *= inverseMask;
+
+ SurfacePattern source(transformSnapshot, ExtendMode::CLAMP, sourceTransform);
+
+ buffer->PushClipRect(transformBounds);
+
+ // Mask in the untransformed coordinate space, and then transform
+ // by the mask transform to put the result back into destination
+ // coords.
+ buffer->SetTransform(maskTransform);
+ buffer->MaskSurface(source, sourceMask, Point(0, 0));
+
+ buffer->PopClip();
+ }
+ } else {
+ buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
+ }
+ }
+
+ buffer->PopClip();
+}
+
+void
+BasicCompositor::ClearRect(const gfx::Rect& aRect)
+{
+ mRenderTarget->mDrawTarget->ClearRect(aRect);
+}
+
+void
+BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut /* = nullptr */,
+ gfx::IntRect *aRenderBoundsOut /* = nullptr */)
+{
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ LayoutDeviceIntRect intRect(LayoutDeviceIntPoint(), mWidget->GetClientSize());
+ IntRect rect = IntRect(0, 0, intRect.width, intRect.height);
+
+ LayoutDeviceIntRegion invalidRegionSafe;
+ if (mDidExternalComposition) {
+ // We do not know rendered region during external composition, just redraw
+ // whole widget.
+ invalidRegionSafe = intRect;
+ mDidExternalComposition = false;
+ } else {
+ // Sometimes the invalid region is larger than we want to draw.
+ invalidRegionSafe.And(
+ LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion), intRect);
+ }
+
+ mInvalidRegion = invalidRegionSafe;
+ mInvalidRect = mInvalidRegion.GetBounds();
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect();
+ }
+
+ BufferMode bufferMode = BufferMode::BUFFERED;
+ if (mTarget) {
+ // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy
+ // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered
+ // draw target that we composite into, then copy the results the destination.
+ mDrawTarget = mTarget;
+ bufferMode = BufferMode::BUFFER_NONE;
+ } else {
+ // StartRemoteDrawingInRegion can mutate mInvalidRegion.
+ mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode);
+ if (!mDrawTarget) {
+ return;
+ }
+ mInvalidRect = mInvalidRegion.GetBounds();
+ if (mInvalidRect.IsEmpty()) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ return;
+ }
+ }
+
+ if (!mDrawTarget || mInvalidRect.IsEmpty()) {
+ return;
+ }
+
+ LayoutDeviceIntRect clearRect;
+ if (!aOpaqueRegion.IsEmpty()) {
+ LayoutDeviceIntRegion clearRegion = mInvalidRegion;
+ clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));
+ clearRect = clearRegion.GetBounds();
+ } else {
+ clearRect = mInvalidRect;
+ }
+
+ // Prevent CreateRenderTargetForWindow from clearing unwanted area.
+ gfxUtils::ClipToRegion(mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ // Setup an intermediate render target to buffer all compositing. We will
+ // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
+ RefPtr<CompositingRenderTarget> target =
+ CreateRenderTargetForWindow(mInvalidRect,
+ clearRect,
+ bufferMode);
+
+ mDrawTarget->PopClip();
+
+ if (!target) {
+ if (!mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+ return;
+ }
+ SetRenderTarget(target);
+
+ // We only allocate a surface sized to the invalidated region, so we need to
+ // translate future coordinates.
+ mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));
+
+ gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
+ mInvalidRegion.ToUnknownRegion());
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = rect;
+ }
+
+ if (aClipRectIn) {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));
+ } else {
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(rect));
+ if (aClipRectOut) {
+ *aClipRectOut = rect;
+ }
+ }
+}
+
+void
+BasicCompositor::EndFrame()
+{
+ Compositor::EndFrame();
+
+ // Pop aClipRectIn/bounds rect
+ mRenderTarget->mDrawTarget->PopClip();
+
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ // We're still clipped to mInvalidRegion, so just fill the bounds.
+ mRenderTarget->mDrawTarget->FillRect(
+ IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),
+ ColorPattern(Color(r, g, b, 0.2f)));
+ }
+
+ // Pop aInvalidregion
+ mRenderTarget->mDrawTarget->PopClip();
+
+ TryToEndRemoteDrawing();
+}
+
+void
+BasicCompositor::TryToEndRemoteDrawing(bool aForceToEnd)
+{
+ if (mIsDestroyed || !mRenderTarget) {
+ return;
+ }
+
+ // It it is not a good timing for EndRemoteDrawing, defter to call it.
+ if (!aForceToEnd && !mTarget && NeedsToDeferEndRemoteDrawing()) {
+ mIsPendingEndRemoteDrawing = true;
+
+ const uint32_t retryMs = 2;
+ RefPtr<BasicCompositor> self = this;
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction([self]() {
+ self->TryToEndRemoteDrawing();
+ });
+ MessageLoop::current()->PostDelayedTask(runnable.forget(), retryMs);
+ return;
+ }
+
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ // Note: Most platforms require us to buffer drawing to the widget surface.
+ // That's why we don't draw to mDrawTarget directly.
+ RefPtr<SourceSurface> source;
+ if (mRenderTarget->mDrawTarget != mDrawTarget) {
+ source = mWidget->EndBackBufferDrawing();
+ } else {
+ source = mRenderTarget->mDrawTarget->Snapshot();
+ }
+ RefPtr<DrawTarget> dest(mTarget ? mTarget : mDrawTarget);
+
+ nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint();
+
+ // The source DrawTarget is clipped to the invalidation region, so we have
+ // to copy the individual rectangles in the region or else we'll draw blank
+ // pixels.
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& r = iter.Get();
+ dest->CopySurface(source,
+ IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(),
+ IntPoint(r.x, r.y) - offset);
+ }
+ }
+
+ if (aForceToEnd || !mTarget) {
+ mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
+ }
+
+ mDrawTarget = nullptr;
+ mRenderTarget = nullptr;
+ mIsPendingEndRemoteDrawing = false;
+}
+
+bool
+BasicCompositor::NeedsToDeferEndRemoteDrawing()
+{
+ MOZ_ASSERT(mDrawTarget);
+ MOZ_ASSERT(mRenderTarget);
+
+ if (mTarget || mRenderTarget->mDrawTarget == mDrawTarget) {
+ return false;
+ }
+
+ return mWidget->NeedsToDeferEndRemoteDrawing();
+}
+
+void
+BasicCompositor::FinishPendingComposite()
+{
+ TryToEndRemoteDrawing(/* aForceToEnd */ true);
+}
+
+void
+BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(!mTarget);
+ MOZ_ASSERT(!mDrawTarget);
+ MOZ_ASSERT(!mRenderTarget);
+
+ mDidExternalComposition = true;
+}
+
+BasicCompositor*
+AssertBasicCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor()
+ : nullptr;
+ MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+ return compositor;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h
new file mode 100644
index 000000000..73f3e82c3
--- /dev/null
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BASICCOMPOSITOR_H
+#define MOZILLA_GFX_BASICCOMPOSITOR_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositingRenderTarget : public CompositingRenderTarget
+{
+public:
+ BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aRect)
+ : CompositingRenderTarget(aRect.TopLeft())
+ , mDrawTarget(aDrawTarget)
+ , mSize(aRect.Size())
+ { }
+
+ virtual const char* Name() const override { return "BasicCompositingRenderTarget"; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ void BindRenderTarget();
+
+ virtual gfx::SurfaceFormat GetFormat() const override
+ {
+ return mDrawTarget ? mDrawTarget->GetFormat()
+ : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN);
+ }
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+};
+
+class BasicCompositor : public Compositor
+{
+public:
+ explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+
+protected:
+ virtual ~BasicCompositor();
+
+public:
+
+ virtual BasicCompositor* AsBasicCompositor() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual void DetachWidget() override;
+
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect,
+ const LayoutDeviceIntRect& aClearRect,
+ BufferMode aBufferMode);
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) override;
+
+ virtual bool SupportsEffect(EffectTypes aEffect) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
+ {
+ mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
+ mRenderTarget->BindRenderTarget();
+ }
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mRenderTarget;
+ }
+
+ virtual void DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+ virtual void EndFrame() override;
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
+
+ virtual bool SupportsPartialTextureUpdate() override { return true; }
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override { return true; }
+ virtual int32_t GetMaxTextureSize() const override;
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
+
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
+ }
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override { }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Basic"; }
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_BASIC;
+ }
+
+ gfx::DrawTarget *GetDrawTarget() { return mDrawTarget; }
+
+ virtual bool IsPendingComposite() override
+ {
+ return mIsPendingEndRemoteDrawing;
+ }
+
+ virtual void FinishPendingComposite() override;
+
+private:
+
+ void TryToEndRemoteDrawing(bool aForceToEnd = false);
+
+ bool NeedsToDeferEndRemoteDrawing();
+
+ // The final destination surface
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ // The current render target for drawing
+ RefPtr<BasicCompositingRenderTarget> mRenderTarget;
+
+ LayoutDeviceIntRect mInvalidRect;
+ LayoutDeviceIntRegion mInvalidRegion;
+ bool mDidExternalComposition;
+
+ uint32_t mMaxTextureSize;
+ bool mIsPendingEndRemoteDrawing;
+};
+
+BasicCompositor* AssertBasicCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
diff --git a/gfx/layers/basic/BasicContainerLayer.cpp b/gfx/layers/basic/BasicContainerLayer.cpp
new file mode 100644
index 000000000..499e202c4
--- /dev/null
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicContainerLayer.h"
+#include <sys/types.h> // for int32_t
+#include "BasicLayersImpl.h" // for ToData
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+#include "ReadbackProcessor.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+BasicContainerLayer::~BasicContainerLayer()
+{
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(BasicContainerLayer);
+}
+
+void
+BasicContainerLayer::ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
+{
+ // We push groups for container layers if we need to, which always
+ // are aligned in device space, so it doesn't really matter how we snap
+ // containers.
+ Matrix residual;
+ Matrix4x4 transformToSurface = aTransformToSurface;
+ bool participate3DCtx = Extend3DContext() || Is3DContextLeaf();
+ if (!participate3DCtx &&
+ GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ // For backface-hidden layers
+ transformToSurface.ProjectTo2D();
+ }
+ Matrix4x4 idealTransform = GetLocalTransform() * transformToSurface;
+ if (!participate3DCtx &&
+ !(GetContentFlags() & CONTENT_BACKFACE_HIDDEN)) {
+ // For non-backface-hidden layers,
+ // 3D components are required to handle CONTENT_BACKFACE_HIDDEN.
+ idealTransform.ProjectTo2D();
+ }
+
+ if (!idealTransform.CanDraw2D()) {
+ if (!Extend3DContext()) {
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(Matrix4x4());
+ ComputeEffectiveTransformForMaskLayers(Matrix4x4());
+ mUseIntermediateSurface = true;
+ return;
+ }
+
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ ComputeEffectiveTransformForMaskLayers(idealTransform);
+ mUseIntermediateSurface = false;
+ return;
+ }
+
+ // With 2D transform or extended 3D context.
+
+ Layer* child = GetFirstChild();
+ bool hasSingleBlendingChild = false;
+ if (!HasMultipleChildren() && child) {
+ hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
+ }
+
+ /* If we have a single childand it is not blending,, it can just inherit our opacity,
+ * otherwise we need a PushGroup and we need to mark ourselves as using
+ * an intermediate surface so our children don't inherit our opacity
+ * via GetEffectiveOpacity.
+ * Having a mask layer always forces our own push group
+ * Having a blend mode also always forces our own push group
+ */
+ mUseIntermediateSurface =
+ GetMaskLayer() ||
+ GetForceIsolatedGroup() ||
+ (GetMixBlendMode() != CompositionOp::OP_OVER && HasMultipleChildren()) ||
+ (GetEffectiveOpacity() != 1.0 && ((HasMultipleChildren() && !Extend3DContext()) || hasSingleBlendingChild));
+
+ mEffectiveTransform =
+ !mUseIntermediateSurface ?
+ idealTransform :
+ (!(GetContentFlags() & CONTENT_BACKFACE_HIDDEN) ?
+ SnapTransformTranslation(idealTransform, &residual) :
+ SnapTransformTranslation3D(idealTransform, &residual));
+ Matrix4x4 childTransformToSurface =
+ (!mUseIntermediateSurface ||
+ (mUseIntermediateSurface && !Extend3DContext() /* 2D */)) ?
+ idealTransform : Matrix4x4::From2D(residual);
+ ComputeEffectiveTransformsForChildren(childTransformToSurface);
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool
+BasicContainerLayer::ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect)
+{
+ Matrix transform;
+ if (!GetEffectiveTransform().CanDraw2D(&transform) ||
+ ThebesMatrix(transform).HasNonIntegerTranslation())
+ return false;
+
+ nsIntPoint offset(int32_t(transform._31), int32_t(transform._32));
+ gfx::IntRect rect = aInRect.Intersect(GetLocalVisibleRegion().ToUnknownRegion().GetBounds() + offset);
+ nsIntRegion covered;
+
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (ToData(l)->IsHidden())
+ continue;
+
+ Matrix childTransform;
+ if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
+ ThebesMatrix(childTransform).HasNonIntegerTranslation() ||
+ l->GetEffectiveOpacity() != 1.0)
+ return false;
+ nsIntRegion childRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ childRegion.MoveBy(int32_t(childTransform._31), int32_t(childTransform._32));
+ childRegion.And(childRegion, rect);
+ if (l->GetClipRect()) {
+ childRegion.And(childRegion, l->GetClipRect()->ToUnknownRect() + offset);
+ }
+ nsIntRegion intersection;
+ intersection.And(covered, childRegion);
+ if (!intersection.IsEmpty())
+ return false;
+ covered.Or(covered, childRegion);
+ }
+
+ return covered.Contains(rect);
+}
+
+void
+BasicContainerLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ ReadbackProcessor readback;
+ if (BasicManager()->IsRetained()) {
+ readback.BuildUpdates(this);
+ }
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ BasicImplData* data = ToData(l);
+ data->Validate(aCallback, aCallbackData, &readback);
+ if (l->GetMaskLayer()) {
+ data = ToData(l->GetMaskLayer());
+ data->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+}
+
+already_AddRefed<ContainerLayer>
+BasicLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ContainerLayer> layer = new BasicContainerLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicContainerLayer.h b/gfx/layers/basic/BasicContainerLayer.h
new file mode 100644
index 000000000..c8227f6e9
--- /dev/null
+++ b/gfx/layers/basic/BasicContainerLayer.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICCONTAINERLAYER_H
+#define GFX_BASICCONTAINERLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicContainerLayer : public ContainerLayer, public BasicImplData {
+public:
+ explicit BasicContainerLayer(BasicLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+protected:
+ virtual ~BasicContainerLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::InsertAfter(aChild, aAfter);
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RemoveChild(aChild);
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RepositionChild(aChild, aAfter);
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override;
+
+ /**
+ * Returns true when:
+ * a) no (non-hidden) childrens' visible areas overlap in
+ * (aInRect intersected with this layer's visible region).
+ * b) the (non-hidden) childrens' visible areas cover
+ * (aInRect intersected with this layer's visible region).
+ * c) this layer and all (non-hidden) children have transforms that are translations
+ * by integers.
+ * aInRect is in the root coordinate system.
+ * Child layers with opacity do not contribute to the covered area in check b).
+ * This method can be conservative; it's OK to return false under any
+ * circumstances.
+ */
+ bool ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect);
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ /**
+ * We don't really have a hard restriction for max layer size, but we pick
+ * 4096 to avoid excessive memory usage.
+ */
+ virtual int32_t GetMaxLayerSize() override { return 4096; }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicImageLayer.cpp b/gfx/layers/basic/BasicImageLayer.cpp
new file mode 100644
index 000000000..0bb6b132d
--- /dev/null
+++ b/gfx/layers/basic/BasicImageLayer.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer (ptr only), etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxPattern::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Point.h" // for IntSize
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicImageLayer : public ImageLayer, public BasicImplData {
+public:
+ explicit BasicImageLayer(BasicLayerManager* aLayerManager) :
+ ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mSize(-1, -1)
+ {
+ MOZ_COUNT_CTOR(BasicImageLayer);
+ }
+protected:
+ virtual ~BasicImageLayer()
+ {
+ MOZ_COUNT_DTOR(BasicImageLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+ virtual already_AddRefed<SourceSurface> GetAsSourceSurface() override;
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ gfx::IntSize mSize;
+};
+
+void
+BasicImageLayer::Paint(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer)
+{
+ if (IsHidden() || !mContainer) {
+ return;
+ }
+
+ RefPtr<ImageFactory> originalIF = mContainer->GetImageFactory();
+ mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nullptr : BasicManager()->GetImageFactory());
+
+ AutoLockImage autoLock(mContainer);
+ Image *image = autoLock.GetImage();
+ if (!image) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+ RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
+ if (!surface || !surface->IsValid()) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+
+ gfx::IntSize size = mSize = surface->GetSize();
+ FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, size.width, size.height),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ mContainer->SetImageFactory(originalIF);
+}
+
+already_AddRefed<SourceSurface>
+BasicImageLayer::GetAsSourceSurface()
+{
+ if (!mContainer) {
+ return nullptr;
+ }
+
+ AutoLockImage lockImage(mContainer);
+ Image* image = lockImage.GetImage();
+ if (!image) {
+ return nullptr;
+ }
+ return image->GetAsSourceSurface();
+}
+
+already_AddRefed<ImageLayer>
+BasicLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ImageLayer> layer = new BasicImageLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp
new file mode 100644
index 000000000..ed9447207
--- /dev/null
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "cairo.h" // for cairo_user_data_key_t
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/mozalloc.h" // for operator delete[], etc
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAutoRef.h" // for nsCountedRef
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "gfx2DGlue.h"
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+
+class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage
+{
+public:
+ BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
+ : RecyclingPlanarYCbCrImage(aRecycleBin)
+ , mScaleHint(aScaleHint)
+ , mStride(0)
+ , mDelayedConversion(false)
+ {
+ SetOffscreenFormat(aOffscreenFormat);
+ }
+
+ ~BasicPlanarYCbCrImage()
+ {
+ if (mDecodedBuffer) {
+ // Right now this only happens if the Image was never drawn, otherwise
+ // this will have been tossed away at surface destruction.
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+ }
+ }
+
+ virtual bool CopyData(const Data& aData) override;
+ virtual void SetDelayedConversion(bool aDelayed) override { mDelayedConversion = aDelayed; }
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ size_t size = RecyclingPlanarYCbCrImage::SizeOfExcludingThis(aMallocSizeOf);
+ size += aMallocSizeOf(mDecodedBuffer.get());
+ return size;
+ }
+
+private:
+ UniquePtr<uint8_t[]> mDecodedBuffer;
+ gfx::IntSize mScaleHint;
+ int mStride;
+ bool mDelayedConversion;
+};
+
+class BasicImageFactory : public ImageFactory
+{
+public:
+ BasicImageFactory() {}
+
+ virtual RefPtr<PlanarYCbCrImage>
+ CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin)
+ {
+ return new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
+ }
+};
+
+bool
+BasicPlanarYCbCrImage::CopyData(const Data& aData)
+{
+ RecyclingPlanarYCbCrImage::CopyData(aData);
+
+ if (mDelayedConversion) {
+ return false;
+ }
+
+ // Do some sanity checks to prevent integer overflow
+ if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image source width or height");
+ return false;
+ }
+
+ gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+
+ gfx::IntSize size(mScaleHint);
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ size.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return false;
+ }
+
+ gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format);
+ mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width);
+ mDecodedBuffer = AllocateBuffer(size.height * mStride);
+ if (!mDecodedBuffer) {
+ // out of memory
+ return false;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride);
+ SetOffscreenFormat(iFormat);
+ mSize = size;
+
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+BasicPlanarYCbCrImage::GetAsSourceSurface()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
+
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ if (!mDecodedBuffer) {
+ return PlanarYCbCrImage::GetAsSourceSurface();
+ }
+
+ gfxImageFormat format = GetOffscreenFormat();
+
+ RefPtr<gfx::SourceSurface> surface;
+ {
+ // Create a DrawTarget so that we can own the data inside mDecodeBuffer.
+ // We create the target out of mDecodedBuffer, and get a snapshot from it.
+ // The draw target is destroyed on scope exit and the surface owns the data.
+ RefPtr<gfx::DrawTarget> drawTarget
+ = gfxPlatform::CreateDrawTargetForData(mDecodedBuffer.get(),
+ mSize,
+ mStride,
+ gfx::ImageFormatToSurfaceFormat(format));
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ surface = drawTarget->Snapshot();
+ }
+
+ mRecycleBin->RecycleBuffer(Move(mDecodedBuffer), mSize.height * mStride);
+
+ mSourceSurface = surface;
+ return surface.forget();
+}
+
+
+ImageFactory*
+BasicLayerManager::GetImageFactory()
+{
+ if (!mFactory) {
+ mFactory = new BasicImageFactory();
+ }
+
+ return mFactory.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicImplData.h b/gfx/layers/basic/BasicImplData.h
new file mode 100644
index 000000000..2ffa310a9
--- /dev/null
+++ b/gfx/layers/basic/BasicImplData.h
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICIMPLDATA_H
+#define GFX_BASICIMPLDATA_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+/**
+ * This is the ImplData for all Basic layers. It also exposes methods
+ * private to the Basic implementation that are common to all Basic layer types.
+ * In particular, there is an internal Paint() method that we can use
+ * to paint the contents of non-PaintedLayers.
+ *
+ * The class hierarchy for Basic layers is like this:
+ * BasicImplData
+ * Layer | | |
+ * | | | |
+ * +-> ContainerLayer | | |
+ * | | | | |
+ * | +-> BasicContainerLayer <--+ | |
+ * | | |
+ * +-> PaintedLayer | |
+ * | | | |
+ * | +-> BasicPaintedLayer <---------+ |
+ * | |
+ * +-> ImageLayer |
+ * | |
+ * +-> BasicImageLayer <--------------+
+ */
+class BasicImplData {
+public:
+ BasicImplData() : mHidden(false),
+ mClipToVisibleRegion(false),
+ mDrawAtomically(false),
+ mOperator(gfx::CompositionOp::OP_OVER)
+ {
+ MOZ_COUNT_CTOR(BasicImplData);
+ }
+ virtual ~BasicImplData()
+ {
+ MOZ_COUNT_DTOR(BasicImplData);
+ }
+
+ /**
+ * Layers that paint themselves, such as ImageLayers, should paint
+ * in response to this method call. aContext will already have been
+ * set up to account for all the properties of the layer (transform,
+ * opacity, etc).
+ */
+ virtual void Paint(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) {}
+
+ /**
+ * Like Paint() but called for PaintedLayers with the additional parameters
+ * they need.
+ * If mClipToVisibleRegion is set, then the layer must clip to its
+ * effective visible region (snapped or unsnapped, it doesn't matter).
+ */
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMasklayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {}
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) {}
+
+ /**
+ * Layers will get this call when their layer manager is destroyed, this
+ * indicates they should clear resources they don't really need after their
+ * LayerManager ceases to exist.
+ */
+ virtual void ClearCachedResources() {}
+
+ /**
+ * This variable is set by MarkLayersHidden() before painting. It indicates
+ * that the layer should not be composited during this transaction.
+ */
+ void SetHidden(bool aCovered) { mHidden = aCovered; }
+ bool IsHidden() const { return false; }
+ /**
+ * This variable is set by MarkLayersHidden() before painting. This is
+ * the operator to be used when compositing the layer in this transaction. It must
+ * be OVER or SOURCE.
+ */
+ void SetOperator(gfx::CompositionOp aOperator)
+ {
+ NS_ASSERTION(aOperator == gfx::CompositionOp::OP_OVER ||
+ aOperator == gfx::CompositionOp::OP_SOURCE,
+ "Bad composition operator");
+ mOperator = aOperator;
+ }
+
+ gfx::CompositionOp GetOperator() const { return mOperator; }
+
+ /**
+ * Return a surface for this layer. Will use an existing surface, if
+ * possible, or may create a temporary surface. Implement this
+ * method for any layers that might be used as a mask. Should only
+ * return false if a surface cannot be created. If true is
+ * returned, only one of |aSurface| or |aDescriptor| is valid.
+ */
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
+
+ bool GetClipToVisibleRegion() { return mClipToVisibleRegion; }
+ void SetClipToVisibleRegion(bool aClip) { mClipToVisibleRegion = aClip; }
+
+ void SetDrawAtomically(bool aDrawAtomically) { mDrawAtomically = aDrawAtomically; }
+
+protected:
+ bool mHidden;
+ bool mClipToVisibleRegion;
+ bool mDrawAtomically;
+ gfx::CompositionOp mOperator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp
new file mode 100644
index 000000000..41c37dc8e
--- /dev/null
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -0,0 +1,991 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint32_t
+#include <stdlib.h> // for rand, RAND_MAX
+#include <sys/types.h> // for int32_t
+#include <stack> // for stack
+#include "BasicContainerLayer.h" // for BasicContainerLayer
+#include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ImageContainer.h" // for ImageFactory
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "ReadbackProcessor.h" // for ReadbackProcessor
+#include "RenderTrace.h" // for RenderTraceLayers, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager, etc
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxMatrix.h" // for gfxMatrix
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/LayersTypes.h" // for BufferMode::BUFFER_NONE, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#include "nsTArray.h" // for AutoTArray
+#include "TreeTraversal.h" // for ForEachNode
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/**
+ * Clips to the smallest device-pixel-aligned rectangle containing aRect
+ * in user space.
+ * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
+ * aRect.
+ */
+static bool
+ClipToContain(gfxContext* aContext, const IntRect& aRect)
+{
+ gfxRect userRect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gfxRect deviceRect = aContext->UserToDevice(userRect);
+ deviceRect.RoundOut();
+
+ gfxMatrix currentMatrix = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ aContext->NewPath();
+ aContext->Rectangle(deviceRect);
+ aContext->Clip();
+ aContext->SetMatrix(currentMatrix);
+
+ return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
+}
+
+bool
+BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion, PushedGroup& aGroupResult)
+{
+ aGroupResult.mVisibleRegion = aRegion;
+ aGroupResult.mFinalTarget = aContext;
+ aGroupResult.mOperator = GetEffectiveOperator(aLayer);
+ aGroupResult.mOpacity = aLayer->GetEffectiveOpacity();
+
+ // If we need to call PushGroup, we should clip to the smallest possible
+ // area first to minimize the size of the temporary surface.
+ bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
+
+ bool canPushGroup = aGroupResult.mOperator == CompositionOp::OP_OVER ||
+ (aGroupResult.mOperator == CompositionOp::OP_SOURCE && (aLayer->CanUseOpaqueSurface() || aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+
+ if (!canPushGroup) {
+ aContext->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+
+ // PushGroup/PopGroup do not support non operator over.
+ gfxMatrix oldMat = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ gfxRect rect = aContext->GetClipExtents();
+ aContext->SetMatrix(oldMat);
+ rect.RoundOut();
+ IntRect surfRect;
+ ToRect(rect).ToIntRect(&surfRect);
+
+ if (!surfRect.IsEmpty()) {
+ RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ RefPtr<gfxContext> ctx =
+ gfxContext::CreateOrNull(dt, ToRect(rect).TopLeft());
+ if (!ctx) {
+ gfxCriticalNote << "BasicLayerManager context problem in PushGroupForLayer " << gfx::hexa(dt);
+ return false;
+ }
+ ctx->SetMatrix(oldMat);
+
+ aGroupResult.mGroupOffset = surfRect.TopLeft();
+ aGroupResult.mGroupTarget = ctx;
+
+ aGroupResult.mMaskSurface = GetMaskForLayer(aLayer, &aGroupResult.mMaskTransform);
+ return true;
+ }
+ aContext->Restore();
+ }
+
+ Matrix maskTransform;
+ RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
+
+ if (maskSurf) {
+ // The returned transform will transform the mask to device space on the
+ // destination. Since the User->Device space transform will be applied
+ // to the mask by PopGroupAndBlend we need to adjust the transform to
+ // transform the mask to user space.
+ Matrix currentTransform = ToMatrix(aGroupResult.mFinalTarget->CurrentMatrix());
+ currentTransform.Invert();
+ maskTransform = maskTransform * currentTransform;
+ }
+
+ if (aLayer->CanUseOpaqueSurface() &&
+ ((didCompleteClip && aRegion.GetNumRects() == 1) ||
+ !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
+ // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
+ // group. We need to make sure that only pixels inside the layer's visible
+ // region are copied back to the destination. Remember if we've already
+ // clipped precisely to the visible region.
+ aGroupResult.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
+ if (aGroupResult.mNeedsClipToVisibleRegion) {
+ aGroupResult.mFinalTarget->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget, aGroupResult.mVisibleRegion);
+ }
+
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
+ aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGroupResult.mOpacity, maskSurf, maskTransform);
+ }
+ }
+
+ aGroupResult.mGroupTarget = aGroupResult.mFinalTarget;
+
+ return true;
+}
+
+void
+BasicLayerManager::PopGroupForLayer(PushedGroup &group)
+{
+ if (group.mFinalTarget == group.mGroupTarget) {
+ group.mFinalTarget->PopGroupAndBlend();
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Restore();
+ }
+ return;
+ }
+
+ DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
+ RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
+ group.mGroupTarget = nullptr;
+
+ RefPtr<SourceSurface> src = sourceDT->Snapshot();
+
+ if (group.mMaskSurface) {
+ Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+ dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+ Matrix surfTransform = group.mMaskTransform;
+ surfTransform.Invert();
+ dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
+ Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+ group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
+ } else {
+ // For now this is required since our group offset is in device space of the final target,
+ // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
+ // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
+ // always become null.
+ dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
+ Rect(0, 0, src->GetSize().width, src->GetSize().height), DrawSurfaceOptions(SamplingFilter::POINT), DrawOptions(group.mOpacity, group.mOperator));
+ }
+
+ if (group.mNeedsClipToVisibleRegion) {
+ dt->PopClip();
+ }
+
+ group.mFinalTarget->Restore();
+}
+
+static IntRect
+ToInsideIntRect(const gfxRect& aRect)
+{
+ return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+}
+
+// A context helper for BasicLayerManager::PaintLayer() that holds all the
+// painting context together in a data structure so it can be easily passed
+// around. It also uses ensures that the Transform and Opaque rect are restored
+// to their former state on destruction.
+
+class PaintLayerContext {
+public:
+ PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ : mTarget(aTarget)
+ , mTargetMatrixSR(aTarget)
+ , mLayer(aLayer)
+ , mCallback(aCallback)
+ , mCallbackData(aCallbackData)
+ , mPushedOpaqueRect(false)
+ {}
+
+ ~PaintLayerContext()
+ {
+ // Matrix is restored by mTargetMatrixSR
+ if (mPushedOpaqueRect)
+ {
+ ClearOpaqueRect();
+ }
+ }
+
+ // Gets the effective transform and returns true if it is a 2D
+ // transform.
+ bool Setup2DTransform()
+ {
+ // Will return an identity matrix for 3d transforms.
+ return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
+ }
+
+ // Applies the effective transform if it's 2D. If it's a 3D transform then
+ // it applies an identity.
+ void Apply2DTransform()
+ {
+ mTarget->SetMatrix(ThebesMatrix(mTransform));
+ }
+
+ // Set the opaque rect to match the bounds of the visible region.
+ void AnnotateOpaqueRect()
+ {
+ const nsIntRegion visibleRegion = mLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ const IntRect& bounds = visibleRegion.GetBounds();
+
+ DrawTarget *dt = mTarget->GetDrawTarget();
+ const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
+
+ // Try to annotate currentSurface with a region of pixels that have been
+ // (or will be) painted opaque, if no such region is currently set.
+ if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+ (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ !mTransform.HasNonAxisAlignedTransform()) {
+
+ gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
+ gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
+ opaqueRect.RoundIn();
+ IntRect intOpaqueRect;
+ if (opaqueRect.ToIntRect(&intOpaqueRect)) {
+ mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
+ mPushedOpaqueRect = true;
+ }
+ }
+ }
+
+ // Clear the Opaque rect. Although this doesn't really restore it to it's
+ // previous state it will happen on the exit path of the PaintLayer() so when
+ // painting is complete the opaque rect qill be clear.
+ void ClearOpaqueRect() {
+ mTarget->GetDrawTarget()->SetOpaqueRect(IntRect());
+ }
+
+ gfxContext* mTarget;
+ gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
+ Layer* mLayer;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+ Matrix mTransform;
+ bool mPushedOpaqueRect;
+};
+
+BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(BLM_WIDGET)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ NS_ASSERTION(aWidget, "Must provide a widget");
+}
+
+BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType)
+ : mPhase(PHASE_NONE)
+ , mWidget(nullptr)
+ , mDoubleBuffering(BufferMode::BUFFER_NONE)
+ , mType(aType)
+ , mUsingDefaultTarget(false)
+ , mTransactionIncomplete(false)
+{
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ MOZ_ASSERT(mType != BLM_WIDGET);
+}
+
+BasicLayerManager::~BasicLayerManager()
+{
+ NS_ASSERTION(!InTransaction(), "Died during transaction?");
+
+ ClearCachedResources();
+
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(BasicLayerManager);
+}
+
+void
+BasicLayerManager::SetDefaultTarget(gfxContext* aContext)
+{
+ NS_ASSERTION(!InTransaction(),
+ "Must set default target outside transaction");
+ mDefaultTarget = aContext;
+}
+
+void
+BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
+{
+ mDoubleBuffering = aDoubleBuffering;
+}
+
+bool
+BasicLayerManager::BeginTransaction()
+{
+ mInTransaction = true;
+ mUsingDefaultTarget = true;
+ return BeginTransactionWithTarget(mDefaultTarget);
+}
+
+bool
+BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ mInTransaction = true;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+ mTarget = aTarget;
+ return true;
+}
+
+static void
+TransformIntRect(IntRect& aRect, const Matrix& aMatrix,
+ IntRect (*aRoundMethod)(const gfxRect&))
+{
+ Rect gr = Rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ gr = aMatrix.TransformBounds(gr);
+ aRect = (*aRoundMethod)(ThebesRect(gr));
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * It can't be used as is by accelerated layers because of intermediate surfaces.
+ * This must set the hidden flag to true or false on *all* layers in the subtree.
+ * It also sets the operator for all layers to "OVER", and call
+ * SetDrawAtomically(false).
+ * It clears mClipToVisibleRegion on all layers.
+ * @param aClipRect the cliprect, in the root coordinate system. We assume
+ * that any layer drawing is clipped to this rect. It is therefore not
+ * allowed to add to the opaque region outside that rect.
+ * @param aDirtyRect the dirty rect that will be painted, in the root
+ * coordinate system. Layers outside this rect should be hidden.
+ * @param aOpaqueRegion the opaque region covering aLayer, in the
+ * root coordinate system.
+ */
+enum {
+ ALLOW_OPAQUE = 0x01,
+};
+static void
+MarkLayersHidden(Layer* aLayer, const IntRect& aClipRect,
+ const IntRect& aDirtyRect,
+ nsIntRegion& aOpaqueRegion,
+ uint32_t aFlags)
+{
+ IntRect newClipRect(aClipRect);
+ uint32_t newFlags = aFlags;
+
+ // Allow aLayer or aLayer's descendants to cover underlying layers
+ // only if it's opaque.
+ if (aLayer->GetOpacity() != 1.0f) {
+ newFlags &= ~ALLOW_OPAQUE;
+ }
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ // Clip rect is applied after aLayer's transform, i.e., in the coordinate
+ // system of aLayer's parent.
+ TransformIntRect(cr, tr, ToInsideIntRect);
+ } else {
+ cr.SetRect(0, 0, 0, 0);
+ }
+ }
+ newClipRect.IntersectRect(newClipRect, cr);
+ }
+ }
+
+ BasicImplData* data = ToData(aLayer);
+ data->SetOperator(CompositionOp::OP_OVER);
+ data->SetClipToVisibleRegion(false);
+ data->SetDrawAtomically(false);
+
+ if (!aLayer->AsContainerLayer()) {
+ Matrix transform;
+ if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
+ data->SetHidden(false);
+ return;
+ }
+
+ nsIntRegion region = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ IntRect r = region.GetBounds();
+ TransformIntRect(r, transform, ToOutsideIntRect);
+ r.IntersectRect(r, aDirtyRect);
+ data->SetHidden(aOpaqueRegion.Contains(r));
+
+ // Allow aLayer to cover underlying layers only if aLayer's
+ // content is opaque
+ if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ (newFlags & ALLOW_OPAQUE)) {
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ r = iter.Get();
+ TransformIntRect(r, transform, ToInsideIntRect);
+
+ r.IntersectRect(r, newClipRect);
+ aOpaqueRegion.Or(aOpaqueRegion, r);
+ }
+ }
+ } else {
+ Layer* child = aLayer->GetLastChild();
+ bool allHidden = true;
+ for (; child; child = child->GetPrevSibling()) {
+ MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
+ if (!ToData(child)->IsHidden()) {
+ allHidden = false;
+ }
+ }
+ data->SetHidden(allHidden);
+ }
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * MarkLayersHidden must be called before calling this.
+ * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
+ * clipped and in the dirty rect), in the root coordinate system.
+ */
+static void
+ApplyDoubleBuffering(Layer* aLayer, const IntRect& aVisibleRect)
+{
+ BasicImplData* data = ToData(aLayer);
+ if (data->IsHidden())
+ return;
+
+ IntRect newVisibleRect(aVisibleRect);
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ NS_ASSERTION(!ThebesMatrix(tr).HasNonIntegerTranslation(),
+ "Parent can only have an integer translation");
+ cr += nsIntPoint(int32_t(tr._31), int32_t(tr._32));
+ } else {
+ NS_ERROR("Parent can only have an integer translation");
+ }
+ }
+ newVisibleRect.IntersectRect(newVisibleRect, cr);
+ }
+ }
+
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ // Layers that act as their own backbuffers should be drawn to the destination
+ // using OP_SOURCE to ensure that alpha values in a transparent window are
+ // cleared. This can also be faster than OP_OVER.
+ if (!container) {
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ data->SetDrawAtomically(true);
+ } else {
+ if (container->UseIntermediateSurface() ||
+ !container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
+ // We need to double-buffer this container.
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ container->ForceIntermediateSurface();
+ } else {
+ // Tell the children to clip to their visible regions so our assumption
+ // that they don't paint outside their visible regions is valid!
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ToData(child)->SetClipToVisibleRegion(true);
+ ApplyDoubleBuffering(child, newVisibleRect);
+ }
+ }
+ }
+}
+
+void
+BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+}
+
+void
+BasicLayerManager::AbortTransaction()
+{
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_NONE;
+ mUsingDefaultTarget = false;
+ mInTransaction = false;
+}
+
+bool
+BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ PROFILER_LABEL("BasicLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ RenderTraceLayers(mRoot, "FF00");
+
+ mTransactionIncomplete = false;
+
+ if (mRoot) {
+ if (aFlags & END_NO_COMPOSITE) {
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ mRoot->ApplyPendingUpdatesToSubtree();
+ }
+
+ // Need to do this before we call ApplyDoubleBuffering,
+ // which depends on correct effective transforms
+ if (mTarget) {
+ mSnapEffectiveTransforms =
+ !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
+ } else {
+ mSnapEffectiveTransforms = true;
+ }
+ mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(ToMatrix(mTarget->CurrentMatrix())) : Matrix4x4());
+
+ ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
+ if (mRoot->GetMaskLayer()) {
+ ToData(mRoot->GetMaskLayer())->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+
+ if (mTarget && mRoot &&
+ !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
+ !(aFlags & END_NO_COMPOSITE)) {
+ IntRect clipRect;
+
+ {
+ gfxContextMatrixAutoSaveRestore save(mTarget);
+ mTarget->SetMatrix(gfxMatrix());
+ clipRect = ToOutsideIntRect(mTarget->GetClipExtents());
+ }
+
+ if (IsRetained()) {
+ nsIntRegion region;
+ MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
+ if (mUsingDefaultTarget && mDoubleBuffering != BufferMode::BUFFER_NONE) {
+ ApplyDoubleBuffering(mRoot, clipRect);
+ }
+ }
+
+ PaintLayer(mTarget, mRoot, aCallback, aCallbackData);
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mTarget->GetDrawTarget()->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+ if (mWidget) {
+ FlashWidgetUpdateArea(mTarget);
+ }
+ RecordFrame();
+ PostPresent();
+
+ if (!mTransactionIncomplete) {
+ // Clear out target if we have a complete transaction.
+ mTarget = nullptr;
+ }
+ }
+
+ if (mRoot) {
+ mAnimationReadyTime = TimeStamp::Now();
+ mRoot->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ if (!mTransactionIncomplete) {
+ // This is still valid if the transaction was incomplete.
+ mUsingDefaultTarget = false;
+ }
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ // XXX - We should probably assert here that for an incomplete transaction
+ // out target is the default target.
+
+ return !mTransactionIncomplete;
+}
+
+void
+BasicLayerManager::FlashWidgetUpdateArea(gfxContext *aContext)
+{
+ if (gfxPrefs::WidgetUpdateFlashing()) {
+ float r = float(rand()) / RAND_MAX;
+ float g = float(rand()) / RAND_MAX;
+ float b = float(rand()) / RAND_MAX;
+ aContext->SetColor(Color(r, g, b, 0.2f));
+ aContext->Paint();
+ }
+}
+
+bool
+BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot) {
+ return false;
+ }
+
+ return EndTransactionInternal(nullptr, nullptr, aFlags);
+}
+
+void
+BasicLayerManager::SetRoot(Layer* aLayer)
+{
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+}
+
+void
+BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
+ gfxContext* aGroupTarget)
+{
+ MOZ_ASSERT(aGroupTarget);
+ BasicImplData* data = ToData(aPaintContext.mLayer);
+
+ /* Only paint ourself, or our children - This optimization relies on this! */
+ Layer* child = aPaintContext.mLayer->GetFirstChild();
+ if (!child) {
+ if (aPaintContext.mLayer->AsPaintedLayer()) {
+ data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
+ aPaintContext.mCallback, aPaintContext.mCallbackData);
+ } else {
+ data->Paint(aGroupTarget->GetDrawTarget(),
+ aGroupTarget->GetDeviceOffset(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+ } else {
+ ContainerLayer* container =
+ static_cast<ContainerLayer*>(aPaintContext.mLayer);
+ AutoTArray<Layer*, 12> children;
+ container->SortChildrenBy3DZOrder(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* layer = children.ElementAt(i);
+ if (layer->IsBackfaceHidden()) {
+ continue;
+ }
+ if (!layer->AsContainerLayer() && !layer->IsVisible()) {
+ continue;
+ }
+
+ PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
+ aPaintContext.mCallbackData);
+ if (mTransactionIncomplete)
+ break;
+ }
+ }
+}
+
+void
+BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion)
+{
+ // If we're doing our own double-buffering, we need to avoid drawing
+ // the results of an incomplete transaction to the destination surface ---
+ // that could cause flicker. Double-buffering is implemented using a
+ // temporary surface for one or more container layers, so we need to stop
+ // those temporary surfaces from being composited to aTarget.
+ // ApplyDoubleBuffering guarantees that this container layer can't
+ // intersect any other leaf layers, so if the transaction is not yet marked
+ // incomplete, the contents of this container layer are the final contents
+ // for the window.
+ if (!mTransactionIncomplete) {
+ if (aNeedsClipToVisibleRegion) {
+ gfxUtils::ClipToRegion(aPaintContext.mTarget,
+ aPaintContext.mLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+
+ CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
+ AutoSetOperator setOperator(aPaintContext.mTarget, op);
+
+ PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+}
+
+/**
+ * Install the clip applied to the layer on the given gfxContext. The
+ * given gfxContext is the buffer that the layer will be painted to.
+ */
+static void
+InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer)
+{
+ const Maybe<ParentLayerIntRect> &clipRect = aLayer->GetLocalClipRect();
+
+ if (!clipRect) {
+ return;
+ }
+ MOZ_ASSERT(!aLayer->Extend3DContext() ||
+ !aLayer->Combines3DTransformWithAncestors(),
+ "Layers in a preserve 3D context have no clip"
+ " except leaves and the estabisher!");
+
+ Layer* parent = aLayer->GetParent();
+ Matrix4x4 transform3d =
+ parent && parent->Extend3DContext() ?
+ parent->GetEffectiveTransform() :
+ Matrix4x4();
+ Matrix transform;
+ if (!transform3d.CanDraw2D(&transform)) {
+ gfxDevCrash(LogReason::CannotDraw3D) << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
+ }
+ gfxMatrix oldTransform = aTarget->CurrentMatrix();
+ transform *= ToMatrix(oldTransform);
+ aTarget->SetMatrix(ThebesMatrix(transform));
+
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y,
+ clipRect->width, clipRect->height));
+ aTarget->Clip();
+
+ aTarget->SetMatrix(oldTransform);
+}
+
+void
+BasicLayerManager::PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ MOZ_ASSERT(aTarget);
+
+ PROFILER_LABEL("BasicLayerManager", "PaintLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback, aCallbackData);
+
+ // Don't attempt to paint layers with a singular transform, cairo will
+ // just throw an error.
+ if (aLayer->GetEffectiveTransform().IsSingular()) {
+ return;
+ }
+
+ RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
+
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ bool needsGroup = container && container->UseIntermediateSurface();
+ BasicImplData* data = ToData(aLayer);
+ bool needsClipToVisibleRegion =
+ data->GetClipToVisibleRegion() && !aLayer->AsPaintedLayer();
+ NS_ASSERTION(needsGroup || !container ||
+ container->GetOperator() == CompositionOp::OP_OVER,
+ "non-OVER operator should have forced UseIntermediateSurface");
+ NS_ASSERTION(!container || !aLayer->GetMaskLayer() ||
+ container->UseIntermediateSurface(),
+ "ContainerLayer with mask layer should force UseIntermediateSurface");
+
+ gfxContextAutoSaveRestore contextSR;
+ gfxMatrix transform;
+ // Will return an identity matrix for 3d transforms, and is handled separately below.
+ bool is2D = paintLayerContext.Setup2DTransform();
+ MOZ_ASSERT(is2D || needsGroup || !container ||
+ container->Extend3DContext() ||
+ container->Is3DContextLeaf(),
+ "Must PushGroup for 3d transforms!");
+
+ Layer* parent = aLayer->GetParent();
+ bool inPreserves3DChain = parent && parent->Extend3DContext();
+ bool needsSaveRestore =
+ needsGroup || clipRect || needsClipToVisibleRegion || !is2D ||
+ inPreserves3DChain;
+ if (needsSaveRestore) {
+ contextSR.SetContext(aTarget);
+
+ // The clips on ancestors on the preserved3d chain should be
+ // installed on the aTarget before painting the layer.
+ InstallLayerClipPreserves3D(aTarget, aLayer);
+ for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
+ InstallLayerClipPreserves3D(aTarget, l);
+ }
+ }
+
+ paintLayerContext.Apply2DTransform();
+
+ nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ // If needsGroup is true, we'll clip to the visible region after we've popped the group
+ if (needsClipToVisibleRegion && !needsGroup) {
+ gfxUtils::ClipToRegion(aTarget, visibleRegion);
+ // Don't need to clip to visible region again
+ needsClipToVisibleRegion = false;
+ }
+
+ if (is2D) {
+ paintLayerContext.AnnotateOpaqueRect();
+ }
+
+ bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
+ if (clipIsEmpty) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ if (is2D) {
+ if (needsGroup) {
+ PushedGroup pushedGroup;
+ if (PushGroupForLayer(aTarget, aLayer, aLayer->GetLocalVisibleRegion().ToUnknownRegion(), pushedGroup)) {
+ PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
+ PopGroupForLayer(pushedGroup);
+ }
+ } else {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ }
+ } else {
+ if (!needsGroup && container) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ IntRect bounds = visibleRegion.GetBounds();
+ // DrawTarget without the 3D transform applied:
+ RefPtr<DrawTarget> untransformedDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
+ SurfaceFormat::B8G8R8A8);
+ if (!untransformedDT || !untransformedDT->IsValid()) {
+ return;
+ }
+ untransformedDT->SetTransform(Matrix::Translation(-Point(bounds.x, bounds.y)));
+
+ RefPtr<gfxContext> groupTarget =
+ gfxContext::CreatePreservingTransformOrNull(untransformedDT);
+ MOZ_ASSERT(groupTarget); // already checked the target above
+
+ PaintSelfOrChildren(paintLayerContext, groupTarget);
+
+ // Temporary fast fix for bug 725886
+ // Revert these changes when 725886 is ready
+#ifdef DEBUG
+ if (aLayer->GetDebugColorIndex() != 0) {
+ Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
+ untransformedDT->FillRect(Rect(bounds), ColorPattern(color));
+ }
+#endif
+ Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
+ Rect xformBounds =
+ effectiveTransform.TransformAndClipBounds(Rect(bounds),
+ ToRect(aTarget->GetClipExtents()));
+ xformBounds.RoundOut();
+ effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+ effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
+
+ RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
+ RefPtr<DrawTarget> xformDT =
+ untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<SourceSurface> xformSurf;
+ if(xformDT && untransformedSurf &&
+ xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
+ xformSurf = xformDT->Snapshot();
+ }
+
+ if (xformSurf) {
+ aTarget->SetPattern(
+ new gfxPattern(xformSurf,
+ Matrix::Translation(xformBounds.TopLeft())));
+
+ // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
+ // of the source surface out to the current clip region, clip to
+ // the rectangle of the result surface now.
+ aTarget->NewPath();
+ aTarget->SnappedRectangle(ThebesRect(xformBounds));
+ aTarget->Clip();
+ FlushGroup(paintLayerContext, needsClipToVisibleRegion);
+ }
+ }
+}
+
+void
+BasicLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+void
+BasicLayerManager::ClearLayer(Layer* aLayer)
+{
+ ToData(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+already_AddRefed<ReadbackLayer>
+BasicLayerManager::CreateReadbackLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h
new file mode 100644
index 000000000..beb4357bb
--- /dev/null
+++ b/gfx/layers/basic/BasicLayers.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICLAYERS_H
+#define GFX_BASICLAYERS_H
+
+#include <stdint.h> // for INT32_MAX, int32_t
+#include "Layers.h" // for Layer (ptr only), etc
+#include "gfxTypes.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ImageFactory;
+class ImageLayer;
+class PaintLayerContext;
+class ReadbackLayer;
+
+/**
+ * This is a cairo/Thebes-only, main-thread-only implementation of layers.
+ *
+ * In each transaction, the client sets up the layer tree and then during
+ * the drawing phase, each PaintedLayer is painted directly into the target
+ * context (with appropriate clipping and Push/PopGroups performed
+ * between layers).
+ */
+class BasicLayerManager final :
+ public LayerManager
+{
+public:
+ enum BasicLayerManagerType {
+ BLM_WIDGET,
+ BLM_OFFSCREEN,
+ BLM_INACTIVE
+ };
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will not
+ * be retained.
+ */
+ explicit BasicLayerManager(BasicLayerManagerType aType);
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will be
+ * retained; that is, we will try to retain the visible contents of
+ * PaintedLayers as cairo surfaces. We create PaintedLayer buffers by
+ * creating similar surfaces to the default target context, or to
+ * aWidget's GetThebesSurface if there is no default target context, or
+ * to the passed-in context if there is no widget and no default
+ * target context.
+ *
+ * This does not keep a strong reference to the widget, so the caller
+ * must ensure that the widget outlives the layer manager or call
+ * ClearWidget before the widget dies.
+ */
+ explicit BasicLayerManager(nsIWidget* aWidget);
+
+protected:
+ virtual ~BasicLayerManager();
+
+public:
+ BasicLayerManager* AsBasicLayerManager() override { return this; }
+
+ /**
+ * Set the default target context that will be used when BeginTransaction
+ * is called. This can only be called outside a transaction.
+ *
+ * aDoubleBuffering can request double-buffering for drawing to the
+ * default target. When BUFFERED, the layer manager avoids blitting
+ * temporary results to aContext and then overpainting them with final
+ * results, by using a temporary buffer when necessary. In BUFFERED
+ * mode we always completely overwrite the contents of aContext's
+ * destination surface (within the clip region) using OP_SOURCE.
+ */
+ void SetDefaultTarget(gfxContext* aContext);
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ gfxContext* GetDefaultTarget() { return mDefaultTarget; }
+
+ nsIWidget* GetRetainerWidget() { return mWidget; }
+ void ClearRetainerWidget() { mWidget = nullptr; }
+
+ virtual bool IsWidgetLayerManager() override { return mWidget != nullptr; }
+ virtual bool IsInactiveLayerManager() override { return mType == BLM_INACTIVE; }
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return IsWidgetLayerManager(); }
+
+ void AbortTransaction();
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual ImageFactory *GetImageFactory();
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_BASIC; }
+ virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("Basic"); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ gfxContext* GetTarget() { return mTarget; }
+ void SetTarget(gfxContext* aTarget) { mUsingDefaultTarget = false; mTarget = aTarget; }
+ bool IsRetained() { return mWidget != nullptr; }
+
+ virtual const char* Name() const override { return "Basic"; }
+
+ // Clear the cached contents of this layer tree.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+ bool IsTransactionIncomplete() { return mTransactionIncomplete; }
+
+ struct PushedGroup
+ {
+ PushedGroup() : mFinalTarget(nullptr), mNeedsClipToVisibleRegion(false), mOperator(gfx::CompositionOp::OP_COUNT), mOpacity(0.0f){}
+ gfxContext* mFinalTarget;
+ RefPtr<gfxContext> mGroupTarget;
+ nsIntRegion mVisibleRegion;
+ bool mNeedsClipToVisibleRegion;
+ gfx::IntPoint mGroupOffset;
+ gfx::CompositionOp mOperator;
+ gfx::Float mOpacity;
+ RefPtr<gfx::SourceSurface> mMaskSurface;
+ gfx::Matrix mMaskTransform;
+ };
+
+ // Construct a PushedGroup for a specific layer.
+ // Return false if it has some errors in PushGroupForLayer(). Then, the
+ // "aGroupResult" is unavailable for future using.
+ bool PushGroupForLayer(gfxContext* aContext, Layer* aLayerContext, const nsIntRegion& aRegion, PushedGroup& aGroupResult);
+
+ void PopGroupForLayer(PushedGroup& aGroup);
+
+ virtual bool IsCompositingCheap() override { return false; }
+ virtual int32_t GetMaxTextureSize() const override { return INT32_MAX; }
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+ // This is the main body of the PaintLayer routine which will if it has
+ // children, recurse into PaintLayer() otherwise it will paint using the
+ // underlying Paint() method of the Layer. It will not do both.
+ void PaintSelfOrChildren(PaintLayerContext& aPaintContext, gfxContext* aGroupTarget);
+
+ // Paint the group onto the underlying target. This is used by PaintLayer to
+ // flush the group to the underlying target.
+ void FlushGroup(PaintLayerContext& aPaintContext, bool aNeedsClipToVisibleRegion);
+
+ // Paints aLayer to mTarget.
+ void PaintLayer(gfxContext* aTarget,
+ Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ // Clear the contents of a layer
+ void ClearLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ void FlashWidgetUpdateArea(gfxContext* aContext);
+
+ // Widget whose surface should be used as the basis for PaintedLayer
+ // buffers.
+ nsIWidget* mWidget;
+ // The default context for BeginTransaction.
+ RefPtr<gfxContext> mDefaultTarget;
+ // The context to draw into.
+ RefPtr<gfxContext> mTarget;
+ // Image factory we use.
+ RefPtr<ImageFactory> mFactory;
+
+ BufferMode mDoubleBuffering;
+ BasicLayerManagerType mType;
+ bool mUsingDefaultTarget;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_BASICLAYERS_H */
diff --git a/gfx/layers/basic/BasicLayersImpl.cpp b/gfx/layers/basic/BasicLayersImpl.cpp
new file mode 100644
index 000000000..c2262c512
--- /dev/null
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicLayersImpl.h"
+#include <new> // for operator new
+#include "Layers.h" // for Layer, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "AutoMaskData.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+bool
+GetMaskData(Layer* aMaskLayer,
+ const Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData)
+{
+ if (aMaskLayer) {
+ RefPtr<SourceSurface> surface =
+ static_cast<BasicImplData*>(aMaskLayer->ImplData())->GetAsSourceSurface();
+ if (surface) {
+ Matrix transform;
+ Matrix4x4 effectiveTransform = aMaskLayer->GetEffectiveTransform();
+ DebugOnly<bool> maskIs2D = effectiveTransform.CanDraw2D(&transform);
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+ transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
+ aMaskData->Construct(transform, surface);
+ return true;
+ }
+ }
+ return false;
+}
+
+already_AddRefed<SourceSurface>
+GetMaskForLayer(Layer* aLayer, Matrix* aMaskTransform)
+{
+ if (!aLayer->GetMaskLayer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aMaskTransform);
+
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aLayer->GetMaskLayer(), Point(), &mask)) {
+ *aMaskTransform = mask.GetTransform();
+ RefPtr<SourceSurface> surf = mask.GetSurface();
+ return surf.forget();
+ }
+
+ return nullptr;
+}
+
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, Point(), &mask)) {
+ aContext->SetMatrix(ThebesMatrix(mask.GetTransform()));
+ aContext->Mask(mask.GetSurface(), aOpacity);
+ return;
+ }
+
+ // if there is no mask, just paint normally
+ aContext->Paint(aOpacity);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(ColorPattern(aColor), aMaskSource, Point(), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect, ColorPattern(aColor), aOptions);
+}
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ const Color& aColor,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aColor, aOptions, mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aColor, aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ ExtendMode aExtendMode,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform,
+ const Matrix* aSurfaceTransform)
+{
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ Matrix transform = oldTransform * inverseMask;
+ if (aSurfaceTransform) {
+ transform = (*aSurfaceTransform) * transform;
+ }
+
+ SurfacePattern source(aSurface, aExtendMode, transform, aSamplingFilter);
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect,
+ SurfacePattern(aSurface, aExtendMode,
+ aSurfaceTransform ? (*aSurfaceTransform) : Matrix(),
+ aSamplingFilter), aOptions);
+}
+
+void
+FillRectWithMask(DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const Rect& aRect,
+ SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions,
+ Layer* aMaskLayer)
+{
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP,
+ mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP);
+}
+
+BasicImplData*
+ToData(Layer* aLayer)
+{
+ return static_cast<BasicImplData*>(aLayer->ImplData());
+}
+
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer)
+{
+ CompositionOp op = aLayer->GetEffectiveMixBlendMode();
+
+ if (op != CompositionOp::OP_OVER) {
+ return op;
+ }
+
+ return ToData(aLayer)->GetOperator();
+}
+
+ShadowableLayer*
+ToShadowable(Layer* aLayer)
+{
+ return aLayer->AsShadowableLayer();
+}
+
+bool
+ShouldShadow(Layer* aLayer)
+{
+ if (!ToShadowable(aLayer)) {
+ MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_READBACK,
+ "Only expect not to shadow ReadbackLayers");
+ return false;
+ }
+ return true;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicLayersImpl.h b/gfx/layers/basic/BasicLayersImpl.h
new file mode 100644
index 000000000..5626fe329
--- /dev/null
+++ b/gfx/layers/basic/BasicLayersImpl.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICLAYERSIMPL_H
+#define GFX_BASICLAYERSIMPL_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "gfxContext.h" // for gfxContext, etc
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/Maybe.h" // for Maybe
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class AutoMoz2DMaskData;
+class Layer;
+
+class AutoSetOperator {
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+public:
+ AutoSetOperator(gfxContext* aContext, CompositionOp aOperator) {
+ if (aOperator != CompositionOp::OP_OVER) {
+ aContext->SetOp(aOperator);
+ mContext = aContext;
+ }
+ }
+ ~AutoSetOperator() {
+ if (mContext) {
+ mContext->SetOp(CompositionOp::OP_OVER);
+ }
+ }
+private:
+ RefPtr<gfxContext> mContext;
+};
+
+class BasicReadbackLayer : public ReadbackLayer,
+ public BasicImplData
+{
+public:
+ explicit BasicReadbackLayer(BasicLayerManager* aLayerManager) :
+ ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
+ {
+ MOZ_COUNT_CTOR(BasicReadbackLayer);
+ }
+
+protected:
+ virtual ~BasicReadbackLayer()
+ {
+ MOZ_COUNT_DTOR(BasicReadbackLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ReadbackLayer::SetVisibleRegion(aRegion);
+ }
+
+protected:
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+/*
+ * Extract a mask surface for a mask layer
+ * Returns true and through outparams a surface for the mask layer if
+ * a mask layer is present and has a valid surface and transform;
+ * false otherwise.
+ * The transform for the layer will be put in aMaskData
+ */
+bool
+GetMaskData(Layer* aMaskLayer,
+ const gfx::Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData);
+
+already_AddRefed<gfx::SourceSurface> GetMaskForLayer(Layer* aLayer, gfx::Matrix* aMaskTransform);
+
+// Paint the current source to a context using a mask, if present
+void
+PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
+
+// Fill the rect with the source, using a mask and opacity, if present
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ gfx::ExtendMode aExtendMode,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr,
+ const gfx::Matrix* aSurfaceTransform = nullptr);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+void
+FillRectWithMask(gfx::DrawTarget* aDT,
+ const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect,
+ const gfx::Color& aColor,
+ const gfx::DrawOptions& aOptions,
+ Layer* aMaskLayer);
+
+BasicImplData*
+ToData(Layer* aLayer);
+
+/**
+ * Returns the operator to be used when blending and compositing this layer.
+ * Currently there is no way to specify both a blending and a compositing
+ * operator other than normal and source over respectively.
+ *
+ * If the layer has
+ * an effective blend mode operator other than normal, as returned by
+ * GetEffectiveMixBlendMode, this operator is used for blending, and source
+ * over is used for compositing.
+ * If the blend mode for this layer is normal, the compositing operator
+ * returned by GetOperator is used.
+ */
+gfx::CompositionOp
+GetEffectiveOperator(Layer* aLayer);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicPaintedLayer.cpp b/gfx/layers/basic/BasicPaintedLayer.cpp
new file mode 100644
index 000000000..7ce0c24af
--- /dev/null
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BasicPaintedLayer.h"
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "ReadbackProcessor.h" // for ReadbackProcessor::Update, etc
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "BasicLayersImpl.h" // for AutoMaskData, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "AutoMaskData.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static nsIntRegion
+IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
+{
+ gfxRect clip = aContext->GetClipExtents();
+ nsIntRegion result;
+ result.And(aRegion, IntRect::RoundOut(clip.X(), clip.Y(),
+ clip.Width(), clip.Height()));
+ return result;
+}
+
+void
+BasicPaintedLayer::PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ PROFILER_LABEL("BasicPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(BasicManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ float opacity = GetEffectiveOpacity();
+ CompositionOp effectiveOperator = GetEffectiveOperator(this);
+
+ if (!BasicManager()->IsRetained()) {
+ mValidRegion.SetEmpty();
+ mContentClient->Clear();
+
+ nsIntRegion toDraw = IntersectWithClip(GetLocalVisibleRegion().ToUnknownRegion(), aContext);
+
+ RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
+
+ if (!toDraw.IsEmpty() && !IsHidden()) {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ aContext->Save();
+
+ bool needsGroup = opacity != 1.0 ||
+ effectiveOperator != CompositionOp::OP_OVER ||
+ aMaskLayer;
+ RefPtr<gfxContext> context = nullptr;
+ BasicLayerManager::PushedGroup group;
+ bool availableGroup = false;
+
+ if (needsGroup) {
+ availableGroup =
+ BasicManager()->PushGroupForLayer(aContext, this, toDraw, group);
+ if (availableGroup) {
+ context = group.mGroupTarget;
+ }
+ } else {
+ context = aContext;
+ }
+ if (context) {
+ SetAntialiasingFlags(this, context->GetDrawTarget());
+ aCallback(this, context, toDraw, toDraw, DrawRegionClip::NONE,
+ nsIntRegion(), aCallbackData);
+ }
+ if (needsGroup && availableGroup) {
+ BasicManager()->PopGroupForLayer(group);
+ }
+
+ aContext->Restore();
+ }
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ return;
+ }
+
+ if (BasicManager()->IsTransactionIncomplete())
+ return;
+
+ gfxRect clipExtents;
+ clipExtents = aContext->GetClipExtents();
+
+ // Pull out the mask surface and transform here, because the mask
+ // is internal to basic layers
+ AutoMoz2DMaskData mask;
+ SourceSurface* maskSurface = nullptr;
+ Matrix maskTransform;
+ if (GetMaskData(aMaskLayer, aContext->GetDeviceOffset(), &mask)) {
+ maskSurface = mask.GetSurface();
+ maskTransform = mask.GetTransform();
+ }
+
+ if (!IsHidden() && !clipExtents.IsEmpty()) {
+ mContentClient->DrawTo(this, aContext->GetDrawTarget(), opacity,
+ effectiveOperator,
+ maskSurface, &maskTransform);
+ }
+}
+
+void
+BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback)
+{
+ if (!mContentClient) {
+ // This client will have a null Forwarder, which means it will not have
+ // a ContentHost on the other side.
+ mContentClient = new ContentClientBasic(mBackend);
+ }
+
+ if (!BasicManager()->IsRetained()) {
+ return;
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ uint32_t flags = 0;
+#ifndef MOZ_WIDGET_ANDROID
+ if (BasicManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+#endif
+ if (mDrawAtomically) {
+ flags |= RotatedContentBuffer::PAINT_NO_ROTATION;
+ }
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
+ if (target && target->IsValid()) {
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+ SetAntialiasingFlags(this, target);
+
+ RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ PaintBuffer(ctx,
+ state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
+ state.mDidSelfCopy,
+ state.mClip,
+ aCallback, aCallbackData);
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this));
+ Mutated();
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ } else {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ target = nullptr;
+ }
+
+ // It's possible that state.mRegionToInvalidate is nonempty here,
+ // if we are shrinking the valid region to nothing. So use mRegionToDraw
+ // instead.
+ NS_WARNING_ASSERTION(
+ state.mRegionToDraw.IsEmpty(),
+ "No context when we have something to draw, resource exhaustion?");
+ }
+
+ for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = readbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+ RefPtr<DrawTarget> dt =
+ update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
+ update.mSequenceCounter);
+ if (dt) {
+ NS_ASSERTION(GetEffectiveOpacity() == 1.0, "Should only read back opaque layers");
+ NS_ASSERTION(!GetMaskLayer(), "Should only read back layers without masks");
+ dt->SetTransform(dt->GetTransform().PreTranslate(offset.x, offset.y));
+ mContentClient->DrawTo(this, dt, 1.0, CompositionOp::OP_OVER,
+ nullptr, nullptr);
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+}
+
+already_AddRefed<PaintedLayer>
+BasicLayerManager::CreatePaintedLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+
+ BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
+
+ if (mDefaultTarget) {
+ backend = mDefaultTarget->GetDrawTarget()->GetBackendType();
+ } else if (mType == BLM_WIDGET) {
+ backend = gfxPlatform::GetPlatform()->GetContentBackendFor(LayersBackend::LAYERS_BASIC);
+ }
+
+ RefPtr<PaintedLayer> layer = new BasicPaintedLayer(this, backend);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicPaintedLayer.h b/gfx/layers/basic/BasicPaintedLayer.h
new file mode 100644
index 000000000..e616948e1
--- /dev/null
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_BASICPAINTEDLAYER_H
+#define GFX_BASICPAINTEDLAYER_H
+
+#include "Layers.h" // for PaintedLayer, LayerManager, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/layers/ContentClient.h" // for ContentClientBasic
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+class BasicPaintedLayer : public PaintedLayer, public BasicImplData {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) :
+ PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mContentClient(nullptr)
+ , mBackend(aBackend)
+ {
+ MOZ_COUNT_CTOR(BasicPaintedLayer);
+ }
+
+protected:
+ virtual ~BasicPaintedLayer()
+ {
+ MOZ_COUNT_DTOR(BasicPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void PaintThebes(gfxContext* aContext,
+ Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ ReadbackProcessor* aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ if (!BasicManager()->IsRetained()) {
+ // Don't do any snapping of our transform, since we're just going to
+ // draw straight through without intermediate buffers.
+ mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
+ if (gfxPoint(0,0) != mResidualTranslation) {
+ mResidualTranslation = gfxPoint(0,0);
+ mValidRegion.SetEmpty();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ return;
+ }
+ PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ BasicLayerManager* BasicManager()
+ {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+protected:
+ virtual void
+ PaintBuffer(gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aExtendedRegionToDraw,
+ const nsIntRegion& aRegionToInvalidate,
+ bool aDidSelfCopy,
+ DrawRegionClip aClip,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+ aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
+ aClip, aRegionToInvalidate, aCallbackData);
+ // Everything that's visible has been validated. Do this instead of just
+ // OR-ing with aRegionToDraw, since that can lead to a very complex region
+ // here (OR doesn't automatically simplify to the simplest possible
+ // representation of a region.)
+ nsIntRegion tmp;
+ tmp.Or(mVisibleRegion.ToUnknownRegion(), aExtendedRegionToDraw);
+ mValidRegion.Or(mValidRegion, tmp);
+ }
+
+ RefPtr<ContentClientBasic> mContentClient;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
new file mode 100644
index 000000000..8834773e4
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurfaceTextureHostBasic.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureSourceBasic::MacIOSurfaceTextureSourceBasic(
+ BasicCompositor* aCompositor,
+ MacIOSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+{
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+MacIOSurfaceTextureSourceBasic::~MacIOSurfaceTextureSourceBasic()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+gfx::IntSize
+MacIOSurfaceTextureSourceBasic::GetSize() const
+{
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureSourceBasic::GetFormat() const
+{
+ // Set the format the same way as CreateSourceSurfaceFromMacIOSurface.
+ return mSurface->GetFormat() == gfx::SurfaceFormat::NV12
+ ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8;
+}
+
+MacIOSurfaceTextureHostBasic::MacIOSurfaceTextureHostBasic(
+ TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor
+)
+ : TextureHost(aFlags)
+{
+ mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(),
+ aDescriptor.scaleFactor(),
+ !aDescriptor.isOpaque());
+}
+
+gfx::SourceSurface*
+MacIOSurfaceTextureSourceBasic::GetSurface(gfx::DrawTarget* aTarget)
+{
+ if (!mSourceSurface) {
+ mSourceSurface = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ }
+ return mSourceSurface;
+}
+
+void
+MacIOSurfaceTextureSourceBasic::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertBasicCompositor(aCompositor);
+}
+
+bool
+MacIOSurfaceTextureHostBasic::Lock()
+{
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = new MacIOSurfaceTextureSourceBasic(mCompositor, mSurface);
+ }
+ return true;
+}
+
+void
+MacIOSurfaceTextureHostBasic::SetCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = AssertBasicCompositor(aCompositor);
+ if (!compositor) {
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = compositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(compositor);
+ }
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostBasic::GetFormat() const {
+ return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8X8;
+}
+
+gfx::IntSize
+MacIOSurfaceTextureHostBasic::GetSize() const {
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
new file mode 100644
index 000000000..7949aecfc
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+/**
+ * A texture source meant for use with BasicCompositor.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ */
+class MacIOSurfaceTextureSourceBasic
+ : public TextureSourceBasic,
+ public TextureSource
+{
+public:
+ MacIOSurfaceTextureSourceBasic(BasicCompositor* aCompositor,
+ MacIOSurface* aSurface);
+ virtual ~MacIOSurfaceTextureSourceBasic();
+
+ virtual const char* Name() const override { return "MacIOSurfaceTextureSourceBasic"; }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+ virtual gfx::SurfaceFormat GetFormat() const override;
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override { }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceBasic.
+ */
+class MacIOSurfaceTextureHostBasic : public TextureHost
+{
+public:
+ MacIOSurfaceTextureHostBasic(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "MacIOSurfaceTextureHostBasic"; }
+#endif
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<MacIOSurfaceTextureSourceBasic> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_BASIC_H
diff --git a/gfx/layers/basic/TextureClientX11.cpp b/gfx/layers/basic/TextureClientX11.cpp
new file mode 100644
index 000000000..c79ab1a53
--- /dev/null
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "mozilla/layers/TextureClientX11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+#include "mozilla/X11Util.h"
+#include <X11/Xlib.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+X11TextureData::X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface)
+: mSize(aSize)
+, mFormat(aFormat)
+, mSurface(aSurface)
+, mClientDeallocation(aClientDeallocation)
+, mIsCrossProcess(aIsCrossProcess)
+{
+ MOZ_ASSERT(mSurface);
+}
+
+bool
+X11TextureData::Lock(OpenMode aMode)
+{
+ return true;
+}
+
+void
+X11TextureData::Unlock()
+{
+ if (mSurface && mIsCrossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+}
+
+void
+X11TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+X11TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return false;
+ }
+
+ if (!mClientDeallocation) {
+ // Pass to the host the responsibility of freeing the pixmap. ReleasePixmap means
+ // the underlying pixmap will not be deallocated in mSurface's destructor.
+ // ToSurfaceDescriptor is at most called once per TextureClient.
+ mSurface->ReleasePixmap();
+ }
+
+ aOutDescriptor = SurfaceDescriptorX11(mSurface);
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+X11TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return nullptr;
+ }
+
+ IntSize size = mSurface->GetSize();
+ RefPtr<gfx::DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+
+ return dt.forget();
+}
+
+bool
+X11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+
+ return true;
+}
+
+void
+X11TextureData::Deallocate(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+TextureData*
+X11TextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return X11TextureData::Create(mSize, mFormat, aFlags, aAllocator);
+}
+
+X11TextureData*
+X11TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
+ if (aSize.width <= 0 || aSize.height <= 0 ||
+ aSize.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ aSize.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ gfxDebug() << "Asking for X11 surface of invalid size " << aSize.width << "x" << aSize.height;
+ return nullptr;
+ }
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aFormat);
+ RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, imageFormat);
+ if (!surface || surface->GetType() != gfxSurfaceType::Xlib) {
+ NS_ERROR("creating Xlib surface failed!");
+ return nullptr;
+ }
+
+ gfxXlibSurface* xlibSurface = static_cast<gfxXlibSurface*>(surface.get());
+
+ bool crossProcess = !aAllocator->IsSameProcess();
+ X11TextureData* texture = new X11TextureData(aSize, aFormat,
+ !!(aFlags & TextureFlags::DEALLOCATE_CLIENT),
+ crossProcess,
+ xlibSurface);
+ if (crossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+
+ return texture;
+}
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/basic/TextureClientX11.h b/gfx/layers/basic/TextureClientX11.h
new file mode 100644
index 000000000..084538ea1
--- /dev/null
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_X11_H
+#define MOZILLA_GFX_TEXTURECLIENT_X11_H
+
+#include "mozilla/layers/TextureClient.h"
+#include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureData : public TextureData
+{
+public:
+ static X11TextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+protected:
+ X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface);
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<gfxXlibSurface> mSurface;
+ bool mClientDeallocation;
+ bool mIsCrossProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/TextureHostBasic.cpp b/gfx/layers/basic/TextureHostBasic.cpp
new file mode 100644
index 000000000..8058e43ef
--- /dev/null
+++ b/gfx/layers/basic/TextureHostBasic.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHostBasic.h"
+#ifdef XP_MACOSX
+#include "MacIOSurfaceTextureHostBasic.h"
+#endif
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<TextureHost>
+CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+#ifdef XP_MACOSX
+ if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorMacIOSurface) {
+ const SurfaceDescriptorMacIOSurface& desc =
+ aDesc.get_SurfaceDescriptorMacIOSurface();
+ return MakeAndAddRef<MacIOSurfaceTextureHostBasic>(aFlags, desc);
+ }
+#endif
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/TextureHostBasic.h b/gfx/layers/basic/TextureHostBasic.h
new file mode 100644
index 000000000..e08624f5a
--- /dev/null
+++ b/gfx/layers/basic/TextureHostBasic.h
@@ -0,0 +1,34 @@
+
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+#define MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+
+#include "CompositableHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A texture source interface that can be used by the software Compositor.
+ */
+class TextureSourceBasic
+{
+public:
+ TextureSourceBasic() : mFromYCBCR(false) {}
+ virtual ~TextureSourceBasic() {}
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) = 0;
+ virtual void SetBufferTextureHost(BufferTextureHost* aTexture) {}
+ bool mFromYCBCR; // we to track sources from YCBCR so we can use a less accurate fast path for video
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTUREHOSTBASIC_H_
diff --git a/gfx/layers/basic/X11BasicCompositor.cpp b/gfx/layers/basic/X11BasicCompositor.cpp
new file mode 100644
index 000000000..6f21d15d2
--- /dev/null
+++ b/gfx/layers/basic/X11BasicCompositor.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11BasicCompositor.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+bool
+X11DataTextureSourceBasic::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ // Reallocate our internal X11 surface if we don't have a DrawTarget yet,
+ // or if we changed surface size or format since last update.
+ if (!mBufferDrawTarget ||
+ (aSurface->GetSize() != mBufferDrawTarget->GetSize()) ||
+ (aSurface->GetFormat() != mBufferDrawTarget->GetFormat())) {
+
+ RefPtr<gfxASurface> surf;
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aSurface->GetFormat());
+ Display *display = DefaultXDisplay();
+ Screen *screen = DefaultScreenOfDisplay(display);
+ XRenderPictFormat *xrenderFormat =
+ gfxXlibSurface::FindRenderFormat(display, imageFormat);
+
+ if (xrenderFormat) {
+ surf = gfxXlibSurface::Create(screen, xrenderFormat,
+ aSurface->GetSize());
+ }
+
+ if (!surf) {
+ NS_WARNING("Couldn't create native surface, fallback to image surface");
+ surf = new gfxImageSurface(aSurface->GetSize(), imageFormat);
+ }
+
+ mBufferDrawTarget = gfxPlatform::GetPlatform()->
+ CreateDrawTargetForSurface(surf, aSurface->GetSize());
+ }
+
+ // Image contents have changed, upload to our DrawTarget
+ // If aDestRegion is null, means we're updating the whole surface
+ // Note : Incremental update with a source offset is only used on Mac.
+ NS_ASSERTION(!aSrcOffset, "SrcOffset should not be used with linux OMTC basic");
+
+ if (aDestRegion) {
+ for (auto iter = aDestRegion->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ IntRect srcRect(rect.x, rect.y, rect.width, rect.height);
+ IntPoint dstPoint(rect.x, rect.y);
+
+ // We're uploading regions to our buffer, so let's just copy contents over
+ mBufferDrawTarget->CopySurface(aSurface, srcRect, dstPoint);
+ }
+ } else {
+ // We're uploading the whole buffer, so let's just copy the full surface
+ IntSize size = aSurface->GetSize();
+ mBufferDrawTarget->CopySurface(aSurface, IntRect(0, 0, size.width, size.height),
+ IntPoint(0, 0));
+ }
+
+ return true;
+}
+
+TextureSourceBasic*
+X11DataTextureSourceBasic::AsSourceBasic()
+{
+ return this;
+}
+
+IntSize
+X11DataTextureSourceBasic::GetSize() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the size of an uninitialized TextureSource");
+ return IntSize(0, 0);
+ } else {
+ return mBufferDrawTarget->GetSize();
+ }
+}
+
+gfx::SurfaceFormat
+X11DataTextureSourceBasic::GetFormat() const
+{
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the format of an uninitialized TextureSource");
+ return gfx::SurfaceFormat::UNKNOWN;
+ } else {
+ return mBufferDrawTarget->GetFormat();
+ }
+}
+
+SourceSurface*
+X11DataTextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ RefPtr<gfx::SourceSurface> surface;
+ if (mBufferDrawTarget) {
+ surface = mBufferDrawTarget->Snapshot();
+ return surface.get();
+ } else {
+ return nullptr;
+ }
+}
+
+void
+X11DataTextureSourceBasic::DeallocateDeviceData()
+{
+ mBufferDrawTarget = nullptr;
+}
+
+already_AddRefed<DataTextureSource>
+X11BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSource> result =
+ new X11DataTextureSourceBasic();
+ return result.forget();
+}
+
+void
+X11BasicCompositor::EndFrame()
+{
+ BasicCompositor::EndFrame();
+ XFlush(DefaultXDisplay());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/X11BasicCompositor.h b/gfx/layers/basic/X11BasicCompositor.h
new file mode 100644
index 000000000..2504e3f67
--- /dev/null
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11BASICCOMPOSITOR_H
+#define MOZILLA_GFX_X11BASICCOMPOSITOR_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "gfxXlibSurface.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Image-backed surfaces.
+class X11DataTextureSourceBasic : public DataTextureSource
+ , public TextureSourceBasic
+{
+public:
+ X11DataTextureSourceBasic() {};
+
+ virtual const char* Name() const override { return "X11DataTextureSourceBasic"; }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ virtual TextureSourceBasic* AsSourceBasic() override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+private:
+ // We are going to buffer layer content on this xlib draw target
+ RefPtr<mozilla::gfx::DrawTarget> mBufferDrawTarget;
+};
+
+class X11BasicCompositor : public BasicCompositor
+{
+public:
+ explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : BasicCompositor(aParent, aWidget)
+ {}
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
+ virtual void EndFrame() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
diff --git a/gfx/layers/basic/X11TextureSourceBasic.cpp b/gfx/layers/basic/X11TextureSourceBasic.cpp
new file mode 100644
index 000000000..24f21e5fc
--- /dev/null
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11TextureSourceBasic.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceBasic::X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface)
+ : mCompositor(aCompositor),
+ mSurface(aSurface)
+{
+}
+
+IntSize
+X11TextureSourceBasic::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+SurfaceFormat
+X11TextureSourceBasic::GetFormat() const
+{
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+SourceSurface*
+X11TextureSourceBasic::GetSurface(DrawTarget* aTarget)
+{
+ if (!mSourceSurface) {
+ mSourceSurface =
+ Factory::CreateSourceSurfaceForCairoSurface(mSurface->CairoSurface(),
+ GetSize(), GetFormat());
+ }
+ return mSourceSurface;
+}
+
+void
+X11TextureSourceBasic::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertBasicCompositor(aCompositor);
+}
+
+SurfaceFormat
+X11TextureSourceBasic::ContentTypeToSurfaceFormat(gfxContentType aType)
+{
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::B8G8R8X8;
+ case gfxContentType::ALPHA:
+ return SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::B8G8R8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+}
+}
diff --git a/gfx/layers/basic/X11TextureSourceBasic.h b/gfx/layers/basic/X11TextureSourceBasic.h
new file mode 100644
index 000000000..f813560e0
--- /dev/null
+++ b/gfx/layers/basic/X11TextureSourceBasic.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+#define MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceBasic
+ : public TextureSourceBasic
+ , public X11TextureSource
+{
+public:
+ X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface);
+
+ virtual const char* Name() const override { return "X11TextureSourceBasic"; }
+
+ virtual X11TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override { }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void Updated() override { }
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp
new file mode 100644
index 000000000..40513984e
--- /dev/null
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasClient.h"
+
+#include "ClientCanvasLayer.h" // for ClientCanvasLayer
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "ScopedGLHelpers.h"
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "nsDebug.h" // for printf_stderr, NS_ASSERTION
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TextureClientSharedSurface.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/* static */ already_AddRefed<CanvasClient>
+CanvasClient::CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ switch (aType) {
+ case CanvasClientTypeShSurf:
+ return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
+ case CanvasClientAsync:
+ return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
+ default:
+ return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
+ break;
+ }
+}
+
+void
+CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ if (!GetForwarder() || !mLayer || !aRenderer ||
+ !aRenderer->GetCanvasClient()) {
+ return;
+ }
+
+ uint64_t asyncID = aRenderer->GetCanvasClientAsyncID();
+ if (asyncID == 0 || mAsyncID == asyncID) {
+ return;
+ }
+
+ static_cast<ShadowLayerForwarder*>(GetForwarder())
+ ->AttachAsyncCompositable(asyncID, mLayer);
+ mAsyncID = asyncID;
+}
+
+void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+
+ if (!aTexture->IsSharedWithCompositor()) {
+ if (!AddTextureClient(aTexture)) {
+ return;
+ }
+ }
+
+ mBackBuffer = nullptr;
+ mFrontBuffer = nullptr;
+ mBufferProviderTexture = aTexture;
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = aTexture;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+ t->mFrameID = mFrameID;
+
+ GetForwarder()->UseTextures(this, textures);
+ aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
+CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ mBufferProviderTexture = nullptr;
+
+ AutoRemoveTexture autoRemove(this);
+ if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
+ autoRemove.mTexture = mBackBuffer;
+ mBackBuffer = nullptr;
+ }
+
+ bool bufferCreated = false;
+ if (!mBackBuffer) {
+ bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
+ gfxContentType contentType = isOpaque
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+ gfx::SurfaceFormat surfaceFormat
+ = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
+ if (!mBackBuffer) {
+ NS_WARNING("Failed to allocate the TextureClient");
+ return;
+ }
+ mBackBuffer->EnableReadLock();
+ MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
+
+ bufferCreated = true;
+ }
+
+ bool updated = false;
+ {
+ TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
+ if (target) {
+ if (!aLayer->UpdateTarget(target)) {
+ NS_WARNING("Failed to copy the canvas into a TextureClient.");
+ return;
+ }
+ updated = true;
+ }
+ }
+
+ if (bufferCreated && !AddTextureClient(mBackBuffer)) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ if (updated) {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mBackBuffer;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
+ t->mFrameID = mFrameID;
+ GetForwarder()->UseTextures(this, textures);
+ mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ mBackBuffer.swap(mFrontBuffer);
+}
+
+already_AddRefed<TextureClient>
+CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer)
+{
+ if (aLayer->IsGLLayer()) {
+ // We want a cairo backend here as we don't want to be copying into
+ // an accelerated backend and we like LockBits to work. This is currently
+ // the most effective way to make this work.
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, BackendType::CAIRO,
+ mTextureFlags | aFlags);
+ }
+
+#ifdef XP_WIN
+ return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags);
+#else
+ // XXX - We should use CreateTextureClientForDrawing, but we first need
+ // to use double buffering.
+ gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, backend,
+ mTextureFlags | aFlags);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+
+CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+{ }
+
+CanvasClientSharedSurface::~CanvasClientSharedSurface()
+{
+ ClearSurfaces();
+}
+
+////////////////////////////////////////
+// Readback
+
+// For formats compatible with R8G8B8A8.
+static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
+ // [RR, GG, BB, AA]
+ Swap(pixel[0], pixel[2]);
+}
+
+class TexClientFactory
+{
+ CompositableForwarder* const mAllocator;
+ const bool mHasAlpha;
+ const gfx::IntSize mSize;
+ const gfx::BackendType mBackendType;
+ const TextureFlags mBaseTexFlags;
+ const LayersBackend mLayersBackend;
+
+public:
+ TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
+ const gfx::IntSize& size, gfx::BackendType backendType,
+ TextureFlags baseTexFlags, LayersBackend layersBackend)
+ : mAllocator(allocator)
+ , mHasAlpha(hasAlpha)
+ , mSize(size)
+ , mBackendType(backendType)
+ , mBaseTexFlags(baseTexFlags)
+ , mLayersBackend(layersBackend)
+ {
+ }
+
+protected:
+ already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
+ return TextureClient::CreateForRawBufferAccess(mAllocator, format,
+ mSize, mBackendType,
+ mBaseTexFlags);
+ }
+
+public:
+ already_AddRefed<TextureClient> CreateB8G8R8AX8() {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ return Create(format);
+ }
+
+ already_AddRefed<TextureClient> CreateR8G8B8AX8() {
+ RefPtr<TextureClient> ret;
+
+ bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
+ if (!areRGBAFormatsBroken) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ ret = Create(format);
+ }
+
+ if (!ret) {
+ ret = CreateB8G8R8AX8();
+ if (ret) {
+ ret->AddFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return ret.forget();
+ }
+};
+
+static already_AddRefed<TextureClient>
+TexClientFromReadback(SharedSurface* src, CompositableForwarder* allocator,
+ TextureFlags baseFlags, LayersBackend layersBackend)
+{
+ auto backendType = gfx::BackendType::CAIRO;
+ TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
+ baseFlags, layersBackend);
+
+ RefPtr<TextureClient> texClient;
+
+ {
+ gl::ScopedReadbackFB autoReadback(src);
+
+ // We have a source FB, now we need a format.
+ GLenum destFormat = LOCAL_GL_BGRA;
+ GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
+ GLenum readFormat;
+ GLenum readType;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ auto gl = src->mGL;
+ GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
+
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA ||
+ readFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // With a format and type, we can create texClient.
+ if (readFormat == LOCAL_GL_BGRA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // 0xAARRGGBB
+ // In Lendian: [BB, GG, RR, AA]
+ texClient = factory.CreateB8G8R8AX8();
+
+ } else if (readFormat == LOCAL_GL_RGBA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // [RR, GG, BB, AA]
+ texClient = factory.CreateR8G8B8AX8();
+ } else {
+ MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
+ }
+
+ MOZ_ASSERT(texClient);
+ if (!texClient)
+ return nullptr;
+
+ // With a texClient, we can lock for writing.
+ TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
+ DebugOnly<bool> succeeded = autoLock.Succeeded();
+ MOZ_ASSERT(succeeded, "texture should have locked");
+
+ MappedTextureData mapped;
+ texClient->BorrowMappedData(mapped);
+
+ // ReadPixels from the current FB into mapped.data.
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+
+ {
+ ScopedPackState scopedPackState(gl);
+
+ MOZ_ASSERT(mapped.stride/4 == mapped.size.width);
+ gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data);
+ }
+
+ // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
+ // RB_SWAPPED doesn't work with Basic. (bug ???????)
+ // RB_SWAPPED doesn't work with D3D9. (bug ???????)
+ bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
+ layersBackend == LayersBackend::LAYERS_D3D9 ||
+ layersBackend == LayersBackend::LAYERS_D3D11;
+ if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
+ layersNeedsManualSwap)
+ {
+ size_t pixels = width * height;
+ uint8_t* itr = mapped.data;
+ for (size_t i = 0; i < pixels; i++) {
+ SwapRB_R8G8B8A8(itr);
+ itr += 4;
+ }
+
+ texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return texClient.forget();
+}
+
+////////////////////////////////////////
+
+static already_AddRefed<SharedSurfaceTextureClient>
+CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
+{
+ RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
+ if (!dest) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* destSurf = dest->Surf();
+
+ destSurf->ProducerAcquire();
+ SharedSurface::ProdCopy(src, dest->Surf(), factory);
+ destSurf->ProducerRelease();
+
+ return dest.forget();
+}
+
+void
+CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ Renderer renderer;
+ renderer.construct<ClientCanvasLayer*>(aLayer);
+ UpdateRenderer(aSize, renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ Renderer renderer;
+ renderer.construct<AsyncCanvasRenderer*>(aRenderer);
+ UpdateRenderer(aRenderer->GetSize(), renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer)
+{
+ GLContext* gl = nullptr;
+ ClientCanvasLayer* layer = nullptr;
+ AsyncCanvasRenderer* asyncRenderer = nullptr;
+ if (aRenderer.constructed<ClientCanvasLayer*>()) {
+ layer = aRenderer.ref<ClientCanvasLayer*>();
+ gl = layer->mGLContext;
+ } else {
+ asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
+ gl = asyncRenderer->mGLContext;
+ }
+ gl->MakeCurrent();
+
+ RefPtr<TextureClient> newFront;
+
+ if (layer && layer->mGLFrontbuffer) {
+ mShSurfClient = CloneSurface(layer->mGLFrontbuffer.get(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ gfxCriticalError() << "Invalid canvas front buffer";
+ return;
+ }
+ } else if (layer && layer->mIsMirror) {
+ mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ return;
+ }
+ } else {
+ mShSurfClient = gl->Screen()->Front();
+ if (mShSurfClient && mShSurfClient->GetAllocator() &&
+ mShSurfClient->GetAllocator() != GetForwarder()->GetTextureForwarder()) {
+ mShSurfClient = CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory());
+ }
+ if (!mShSurfClient) {
+ return;
+ }
+ }
+ MOZ_ASSERT(mShSurfClient);
+
+ newFront = mShSurfClient;
+
+ SharedSurface* surf = mShSurfClient->Surf();
+
+ // Readback if needed.
+ mReadbackClient = nullptr;
+
+ auto forwarder = GetForwarder();
+
+ bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
+ if (needsReadback) {
+ TextureFlags flags = TextureFlags::IMMUTABLE;
+
+ CompositableForwarder* shadowForwarder = nullptr;
+ if (layer) {
+ flags |= layer->Flags();
+ shadowForwarder = layer->ClientManager()->AsShadowForwarder();
+ } else {
+ MOZ_ASSERT(asyncRenderer);
+ flags |= mTextureFlags;
+ shadowForwarder = GetForwarder();
+ }
+
+ auto layersBackend = shadowForwarder->GetCompositorBackendType();
+ mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend);
+
+ newFront = mReadbackClient;
+ } else {
+ mReadbackClient = nullptr;
+ }
+
+ if (asyncRenderer) {
+ // If surface type is Basic, above codes will readback
+ // the GLContext to mReadbackClient in order to send frame to
+ // compositor. We copy from this TextureClient directly by
+ // calling CopyFromTextureClient().
+ // Therefore, if main-thread want the content of GLContext,
+ // it doesn't have to readback from GLContext again.
+ //
+ // Otherwise, if surface type isn't Basic, we will read from
+ // SharedSurface directly from main-thread. We still pass
+ // mReadbackClient which is nullptr here to tell
+ // AsyncCanvasRenderer reset some properties.
+ asyncRenderer->CopyFromTextureClient(mReadbackClient);
+ }
+
+ MOZ_ASSERT(newFront);
+ if (!newFront) {
+ // May happen in a release build in case of memory pressure.
+ gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
+ return;
+ }
+
+ mNewFront = newFront;
+}
+
+void
+CanvasClientSharedSurface::Updated()
+{
+ if (!mNewFront) {
+ return;
+ }
+
+ auto forwarder = GetForwarder();
+
+ mFront = mNewFront;
+ mNewFront = nullptr;
+
+ // Add the new TexClient.
+ MOZ_ALWAYS_TRUE( AddTextureClient(mFront) );
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mFront;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
+ t->mFrameID = mFrameID;
+ forwarder->UseTextures(this, textures);
+}
+
+void
+CanvasClientSharedSurface::OnDetach() {
+ ClearSurfaces();
+}
+
+void
+CanvasClientSharedSurface::ClearSurfaces()
+{
+ if (mFront) {
+ mFront->CancelWaitForRecycle();
+ }
+ mFront = nullptr;
+ mNewFront = nullptr;
+ mShSurfClient = nullptr;
+ mReadbackClient = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CanvasClient.h b/gfx/layers/client/CanvasClient.h
new file mode 100644
index 000000000..cd88d02ab
--- /dev/null
+++ b/gfx/layers/client/CanvasClient.h
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_CANVASCLIENT_H
+#define MOZILLA_GFX_CANVASCLIENT_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/PersistentBufferProvider.h"
+
+// Fix X11 header brain damage that conflicts with MaybeOneOf::None
+#undef None
+#include "mozilla/MaybeOneOf.h"
+
+#include "mozilla/mozalloc.h" // for operator delete
+
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+
+namespace mozilla {
+namespace layers {
+
+class AsyncCanvasRenderer;
+class ClientCanvasLayer;
+class CompositableForwarder;
+class ShadowableLayer;
+class SharedSurfaceTextureClient;
+
+/**
+ * Compositable client for 2d and webgl canvas.
+ */
+class CanvasClient : public CompositableClient
+{
+public:
+ typedef MaybeOneOf<ClientCanvasLayer*, AsyncCanvasRenderer*> Renderer;
+
+ /**
+ * Creates, configures, and returns a new canvas client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ enum CanvasClientType {
+ CanvasClientSurface,
+ CanvasClientGLContext,
+ CanvasClientTypeShSurf,
+ CanvasClientAsync, // webgl on workers
+ };
+ static already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ CanvasClient(CompositableForwarder* aFwd, TextureFlags aFlags)
+ : CompositableClient(aFwd, aFlags)
+ , mFrameID(0)
+ {
+ mTextureFlags = aFlags;
+ }
+
+ virtual ~CanvasClient() {}
+
+ virtual void Clear() {};
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) = 0;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ ++mFrameID;
+ return CompositableClient::AddTextureClient(aTexture);
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
+
+ virtual void UpdateFromTexture(TextureClient* aTexture) {}
+
+ virtual void Updated() { }
+
+protected:
+ int32_t mFrameID;
+};
+
+// Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
+class CanvasClient2D : public CanvasClient
+{
+public:
+ CanvasClient2D(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE, mTextureFlags);
+ }
+
+ virtual void Clear() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
+
+ virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ return CanvasClient::AddTextureClient(aTexture);
+ }
+
+ virtual void OnDetach() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+private:
+ already_AddRefed<TextureClient>
+ CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer);
+
+ RefPtr<TextureClient> mBackBuffer;
+ RefPtr<TextureClient> mFrontBuffer;
+ // We store this texture separately to make sure it is not written into
+ // in Update() if for some silly reason we end up alternating between
+ // UpdateFromTexture and Update.
+ // This code is begging for a cleanup. The situation described above should
+ // not be made possible.
+ RefPtr<TextureClient> mBufferProviderTexture;
+};
+
+// Used for GL canvases where we don't need to do any readback, i.e., with a
+// GL backend.
+class CanvasClientSharedSurface : public CanvasClient
+{
+private:
+ RefPtr<SharedSurfaceTextureClient> mShSurfClient;
+ RefPtr<TextureClient> mReadbackClient;
+ RefPtr<TextureClient> mFront;
+ RefPtr<TextureClient> mNewFront;
+
+ void ClearSurfaces();
+
+public:
+ CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags);
+
+ ~CanvasClientSharedSurface();
+
+ virtual TextureInfo GetTextureInfo() const override {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Clear() override {
+ ClearSurfaces();
+ }
+
+ virtual void Update(gfx::IntSize aSize,
+ ClientCanvasLayer* aLayer) override;
+ void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ virtual void Updated() override;
+
+ virtual void OnDetach() override;
+};
+
+/**
+ * Used for OMT<canvas> uploads using the image bridge protocol.
+ * Actual CanvasClient is on the ImageBridgeChild thread, so we
+ * only forward its AsyncID here
+ */
+class CanvasClientBridge final : public CanvasClient
+{
+public:
+ CanvasClientBridge(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ , mAsyncID(0)
+ , mLayer(nullptr)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override
+ {
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ void SetLayer(ShadowableLayer* aLayer)
+ {
+ mLayer = aLayer;
+ }
+
+protected:
+ uint64_t mAsyncID;
+ ShadowableLayer* mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp
new file mode 100644
index 000000000..32acf1eb2
--- /dev/null
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientCanvasLayer.h"
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "SharedSurfaceEGL.h" // for SurfaceFactory_EGLImage
+#include "SharedSurfaceGL.h" // for SurfaceFactory_GLTexture, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "gfxPrefs.h" // for WebGLForceLayersReadback
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+ClientCanvasLayer::~ClientCanvasLayer()
+{
+ MOZ_COUNT_DTOR(ClientCanvasLayer);
+ if (mCanvasClient) {
+ mCanvasClient->OnDetach();
+ mCanvasClient = nullptr;
+ }
+}
+
+void
+ClientCanvasLayer::Initialize(const Data& aData)
+{
+ CopyableCanvasLayer::Initialize(aData);
+
+ mCanvasClient = nullptr;
+
+ if (!mGLContext)
+ return;
+
+ GLScreenBuffer* screen = mGLContext->Screen();
+
+ SurfaceCaps caps;
+ if (mGLFrontbuffer) {
+ // The screen caps are irrelevant if we're using a separate frontbuffer.
+ caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA()
+ : SurfaceCaps::ForRGB();
+ } else {
+ MOZ_ASSERT(screen);
+ caps = screen->mCaps;
+ }
+ MOZ_ASSERT(caps.alpha == aData.mHasAlpha);
+
+ auto forwarder = ClientManager()->AsShadowForwarder();
+
+ mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+ if (!aData.mIsGLAlphaPremult) {
+ mFlags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags);
+
+ if (mGLFrontbuffer || aData.mIsMirror) {
+ // We're using a source other than the one in the default screen.
+ // (SkiaGL)
+ mFactory = Move(factory);
+ if (!mFactory) {
+ // Absolutely must have a factory here, so create a basic one
+ mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags);
+ }
+ } else {
+ if (factory)
+ screen->Morph(Move(factory));
+ }
+}
+
+void
+ClientCanvasLayer::RenderLayer()
+{
+ PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ RenderMaskLayers(this);
+
+ if (!mCanvasClient) {
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mOriginPos == gl::OriginPos::BottomLeft) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ if (!mIsAlphaPremultiplied) {
+ flags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mCanvasClient) {
+ return;
+ }
+ if (HasShadow()) {
+ if (mAsyncRenderer) {
+ static_cast<CanvasClientBridge*>(mCanvasClient.get())->SetLayer(this);
+ } else {
+ mCanvasClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this);
+ }
+ }
+ }
+
+ if (mCanvasClient && mAsyncRenderer) {
+ mCanvasClient->UpdateAsync(mAsyncRenderer);
+ }
+
+ if (!IsDirty()) {
+ return;
+ }
+ Painted();
+
+ FirePreTransactionCallback();
+ if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+ if (!mBufferProvider->SetForwarder(ClientManager()->AsShadowForwarder())) {
+ gfxCriticalNote << "BufferProvider::SetForwarder failed";
+ return;
+ }
+ mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+ } else {
+ mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+ }
+
+ FireDidTransactionCallback();
+
+ ClientManager()->Hold(this);
+ mCanvasClient->Updated();
+}
+
+bool
+ClientCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
+{
+ MOZ_ASSERT(aDestTarget);
+ if (!aDestTarget) {
+ return false;
+ }
+
+ RefPtr<SourceSurface> surface;
+
+ if (!mGLContext) {
+ AutoReturnSnapshot autoReturn;
+
+ if (mAsyncRenderer) {
+ surface = mAsyncRenderer->GetSurface();
+ } else if (mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ autoReturn.mSnapshot = &surface;
+ autoReturn.mBufferProvider = mBufferProvider;
+ }
+
+ MOZ_ASSERT(surface);
+ if (!surface) {
+ return false;
+ }
+
+ aDestTarget->CopySurface(surface,
+ IntRect(0, 0, mBounds.width, mBounds.height),
+ IntPoint(0, 0));
+ return true;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return false;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ // Try to read back directly into aDestTarget's output buffer
+ uint8_t* destData;
+ IntSize destSize;
+ int32_t destStride;
+ SurfaceFormat destFormat;
+ if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
+ if (destSize == readSize && destFormat == format) {
+ RefPtr<DataSourceSurface> data =
+ Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
+ mGLContext->Readback(frontbuffer, data);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(data, data);
+ }
+ aDestTarget->ReleaseBits(destData);
+ return true;
+ }
+ aDestTarget->ReleaseBits(destData);
+ }
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return false;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+
+ aDestTarget->CopySurface(resultSurf,
+ IntRect(0, 0, readSize.width, readSize.height),
+ IntPoint(0, 0));
+
+ return true;
+}
+
+CanvasClient::CanvasClientType
+ClientCanvasLayer::GetCanvasClientType()
+{
+ if (mAsyncRenderer) {
+ return CanvasClient::CanvasClientAsync;
+ }
+
+ if (mGLContext) {
+ return CanvasClient::CanvasClientTypeShSurf;
+ }
+ return CanvasClient::CanvasClientSurface;
+}
+
+already_AddRefed<CanvasLayer>
+ClientLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientCanvasLayer> layer =
+ new ClientCanvasLayer(this);
+ CREATE_SHADOW(Canvas);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientCanvasLayer.h b/gfx/layers/client/ClientCanvasLayer.h
new file mode 100644
index 000000000..9a655727c
--- /dev/null
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTCANVASLAYER_H
+#define GFX_CLIENTCANVASLAYER_H
+
+#include "CanvasClient.h" // for CanvasClient, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayersMessages.h" // for CanvasLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gl {
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+
+class ClientCanvasLayer : public CopyableCanvasLayer,
+ public ClientLayer
+{
+ typedef CanvasClient::CanvasClientType CanvasClientType;
+public:
+ explicit ClientCanvasLayer(ClientLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientCanvasLayer);
+ }
+
+protected:
+ virtual ~ClientCanvasLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Initialize(const Data& aData) override;
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->Clear();
+ }
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = CanvasLayerAttributes(mSamplingFilter, mBounds);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ mCanvasClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mCanvasClient;
+ }
+
+ const TextureFlags& Flags() const { return mFlags; }
+
+protected:
+
+ bool UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CanvasClientType GetCanvasClientType();
+
+ RefPtr<CanvasClient> mCanvasClient;
+
+ UniquePtr<gl::SurfaceFactory> mFactory;
+
+ TextureFlags mFlags;
+
+ friend class CanvasClient2D;
+ friend class CanvasClientSharedSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientColorLayer.cpp b/gfx/layers/client/ClientColorLayer.cpp
new file mode 100644
index 000000000..aa086f35a
--- /dev/null
+++ b/gfx/layers/client/ClientColorLayer.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/layers/LayersMessages.h" // for ColorLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientColorLayer : public ColorLayer,
+ public ClientLayer {
+public:
+ explicit ClientColorLayer(ClientLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientColorLayer);
+ }
+
+protected:
+ virtual ~ClientColorLayer()
+ {
+ MOZ_COUNT_DTOR(ClientColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer()
+ {
+ RenderMaskLayers(this);
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+ {
+ aAttrs = ColorLayerAttributes(GetColor(), GetBounds());
+ }
+
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+ClientLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientColorLayer> layer =
+ new ClientColorLayer(this);
+ CREATE_SHADOW(Color);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientContainerLayer.cpp b/gfx/layers/client/ClientContainerLayer.cpp
new file mode 100644
index 000000000..f72854af9
--- /dev/null
+++ b/gfx/layers/client/ClientContainerLayer.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientContainerLayer.h"
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<ContainerLayer>
+ClientLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientContainerLayer> layer =
+ new ClientContainerLayer(this);
+ CREATE_SHADOW(Container);
+ return layer.forget();
+}
+
+already_AddRefed<RefLayer>
+ClientLayerManager::CreateRefLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientRefLayer> layer =
+ new ClientRefLayer(this);
+ CREATE_SHADOW(Ref);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientContainerLayer.h b/gfx/layers/client/ClientContainerLayer.h
new file mode 100644
index 000000000..de10a55b4
--- /dev/null
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTCONTAINERLAYER_H
+#define GFX_CLIENTCONTAINERLAYER_H
+
+#include <stdint.h> // for uint32_t
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include "ReadbackProcessor.h"
+#include "ClientPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+
+class ClientContainerLayer : public ContainerLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientContainerLayer(ClientLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+
+protected:
+ virtual ~ClientContainerLayer()
+ {
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(ClientContainerLayer);
+ }
+
+public:
+ virtual void RenderLayer() override
+ {
+ RenderMaskLayers(this);
+
+ DefaultComputeSupportsComponentAlphaChildren();
+
+ AutoTArray<Layer*, 12> children;
+ SortChildrenBy3DZOrder(children);
+
+ ReadbackProcessor readback;
+ readback.BuildUpdates(this);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* child = children.ElementAt(i);
+
+ ToClientLayer(child)->RenderLayerWithReadback(&readback);
+
+ if (!ClientManager()->GetRepeatTransaction() &&
+ !child->GetInvalidRegion().IsEmpty()) {
+ child->Mutated();
+ }
+ }
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if(!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+
+ if (!ContainerLayer::InsertAfter(aChild, aAfter)) {
+ return false;
+ }
+
+ ClientManager()->AsShadowForwarder()->InsertAfter(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ // hold on to aChild before we remove it!
+ ShadowableLayer *heldChild = ClientManager()->Hold(aChild);
+ if (!ContainerLayer::RemoveChild(aChild)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this), heldChild);
+ return true;
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ if (!ContainerLayer::RepositionChild(aChild, aAfter)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RepositionChild(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+class ClientRefLayer : public RefLayer,
+ public ClientLayer {
+public:
+ explicit ClientRefLayer(ClientLayerManager* aManager) :
+ RefLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientRefLayer);
+ }
+
+protected:
+ virtual ~ClientRefLayer()
+ {
+ MOZ_COUNT_DTOR(ClientRefLayer);
+ }
+
+public:
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() { }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientImageLayer.cpp b/gfx/layers/client/ClientImageLayer.cpp
new file mode 100644
index 000000000..8703f77a5
--- /dev/null
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageClient.h" // for ImageClient, etc
+#include "mozilla/layers/LayersMessages.h" // for ImageLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientImageLayer : public ImageLayer,
+ public ClientLayer {
+public:
+ explicit ClientImageLayer(ClientLayerManager* aLayerManager)
+ : ImageLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ , mImageClientTypeContainer(CompositableType::UNKNOWN)
+ {
+ MOZ_COUNT_CTOR(ClientImageLayer);
+ }
+
+protected:
+ virtual ~ClientImageLayer()
+ {
+ DestroyBackBuffer();
+ MOZ_COUNT_DTOR(ClientImageLayer);
+ }
+
+ virtual void SetContainer(ImageContainer* aContainer) override
+ {
+ ImageLayer::SetContainer(aContainer);
+ mImageClientTypeContainer = CompositableType::UNKNOWN;
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mImageClient) {
+ mImageClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = ImageLayerAttributes(mSamplingFilter, mScaleToSize, mScaleMode);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ DestroyBackBuffer();
+ ClientLayer::Disconnect();
+ }
+
+ void DestroyBackBuffer()
+ {
+ if (mImageClient) {
+ mImageClient->SetLayer(nullptr);
+ mImageClient->OnDetach();
+ mImageClient = nullptr;
+ }
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mImageClient;
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CompositableType GetImageClientType()
+ {
+ if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
+ return mImageClientTypeContainer;
+ }
+
+ if (mContainer->IsAsync()) {
+ mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
+ return mImageClientTypeContainer;
+ }
+
+ AutoLockImage autoLock(mContainer);
+
+ mImageClientTypeContainer = autoLock.HasImage()
+ ? CompositableType::IMAGE : CompositableType::UNKNOWN;
+ return mImageClientTypeContainer;
+ }
+
+ RefPtr<ImageClient> mImageClient;
+ CompositableType mImageClientTypeContainer;
+};
+
+void
+ClientImageLayer::RenderLayer()
+{
+ RenderMaskLayers(this);
+
+ if (!mContainer) {
+ return;
+ }
+
+ if (!mImageClient ||
+ !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ CompositableType type = GetImageClientType();
+ if (type == CompositableType::UNKNOWN) {
+ return;
+ }
+ TextureFlags flags = TextureFlags::DEFAULT;
+ mImageClient = ImageClient::CreateImageClient(type,
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mImageClient) {
+ return;
+ }
+ mImageClient->SetLayer(this);
+ if (HasShadow() && !mContainer->IsAsync()) {
+ mImageClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
+ }
+ if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ return;
+ }
+ }
+ ClientManager()->Hold(this);
+}
+
+already_AddRefed<ImageLayer>
+ClientLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientImageLayer> layer =
+ new ClientImageLayer(this);
+ CREATE_SHADOW(Image);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp
new file mode 100644
index 000000000..074807e8c
--- /dev/null
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -0,0 +1,907 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h"
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "gfxPrefs.h" // for gfxPrefs::LayersTile...
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Hal.h"
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/dom/TabChild.h" // for TabChild
+#include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/ContentClient.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayerChild.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "ClientReadbackLayer.h" // for ClientReadbackLayer
+#include "nsAString.h"
+#include "nsDisplayList.h"
+#include "nsIWidgetListener.h"
+#include "nsTArray.h" // for AutoTArray
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TiledLayerBuffer.h"
+#include "mozilla/dom/WindowBinding.h" // for Overfill Callback
+#include "FrameLayerBuilder.h" // for FrameLayerbuilder
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "LayerMetricsWrapper.h"
+#endif
+#ifdef XP_WIN
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientLayerManager::MemoryPressureObserver::Destroy()
+{
+ UnregisterMemoryPressureEvent();
+ mClientLayerManager = nullptr;
+}
+
+NS_IMETHODIMP
+ClientLayerManager::MemoryPressureObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aSomeData)
+{
+ if (!mClientLayerManager || strcmp(aTopic, "memory-pressure")) {
+ return NS_OK;
+ }
+
+ mClientLayerManager->HandleMemoryPressure();
+ return NS_OK;
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::RegisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ MOZ_ASSERT(observerService);
+
+ if (observerService) {
+ observerService->AddObserver(this, "memory-pressure", false);
+ }
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::UnregisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ observerService->RemoveObserver(this, "memory-pressure");
+ }
+}
+
+NS_IMPL_ISUPPORTS(ClientLayerManager::MemoryPressureObserver, nsIObserver)
+
+ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mLatestTransactionId(0)
+ , mLastPaintTime(TimeDuration::Forever())
+ , mTargetRotation(ROTATION_0)
+ , mRepeatTransaction(false)
+ , mIsRepeatTransaction(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+ , mNeedsComposite(false)
+ , mPaintSequenceNumber(0)
+ , mForwarder(new ShadowLayerForwarder(this))
+ , mDeviceCounter(gfxPlatform::GetPlatform()->GetDeviceCounter())
+{
+ MOZ_COUNT_CTOR(ClientLayerManager);
+ mMemoryPressureObserver = new MemoryPressureObserver(this);
+}
+
+
+ClientLayerManager::~ClientLayerManager()
+{
+ mMemoryPressureObserver->Destroy();
+ ClearCachedResources();
+ // Stop receiveing AsyncParentMessage at Forwarder.
+ // After the call, the message is directly handled by LayerTransactionChild.
+ // Basically this function should be called in ShadowLayerForwarder's
+ // destructor. But when the destructor is triggered by
+ // CompositorBridgeChild::Destroy(), the destructor can not handle it correctly.
+ // See Bug 1000525.
+ mForwarder->StopReceiveAsyncParentMessge();
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(ClientLayerManager);
+}
+
+void
+ClientLayerManager::Destroy()
+{
+ // It's important to call ClearCachedResource before Destroy because the
+ // former will early-return if the later has already run.
+ ClearCachedResources();
+ LayerManager::Destroy();
+
+ if (mTransactionIdAllocator) {
+ // Make sure to notify the refresh driver just in case it's waiting on a
+ // pending transaction. Do this at the top of the event loop so we don't
+ // cause a paint to occur during compositor shutdown.
+ RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
+ uint64_t id = mLatestTransactionId;
+
+ RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
+ allocator->NotifyTransactionCompleted(id);
+ });
+ NS_DispatchToMainThread(task.forget());
+ }
+
+ // Forget the widget pointer in case we outlive our owning widget.
+ mWidget = nullptr;
+}
+
+int32_t
+ClientLayerManager::GetMaxTextureSize() const
+{
+ return mForwarder->GetMaxTextureSize();
+}
+
+void
+ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
+ ScreenRotation aRotation)
+{
+ mTargetRotation = aRotation;
+ }
+
+void
+ClientLayerManager::SetRoot(Layer* aLayer)
+{
+ if (mRoot != aLayer) {
+ // Have to hold the old root and its children in order to
+ // maintain the same view of the layer tree in this process as
+ // the parent sees. Otherwise layers can be destroyed
+ // mid-transaction and bad things can happen (v. bug 612573)
+ if (mRoot) {
+ Hold(mRoot);
+ }
+ mForwarder->SetRoot(Hold(aLayer));
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+ }
+}
+
+void
+ClientLayerManager::Mutated(Layer* aLayer)
+{
+ LayerManager::Mutated(aLayer);
+
+ NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
+ mForwarder->Mutated(Hold(aLayer));
+}
+
+already_AddRefed<ReadbackLayer>
+ClientLayerManager::CreateReadbackLayer()
+{
+ RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
+ return layer.forget();
+}
+
+bool
+ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
+ if (!mForwarder->IPCOpen()) {
+ gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
+ return false;
+ }
+
+ mInTransaction = true;
+ mTransactionStart = TimeStamp::Now();
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+
+ if (DependsOnStaleDevice()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ mDeviceCounter = gfxPlatform::GetPlatform()->GetDeviceCounter();
+ }
+
+ MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
+
+ // If the last transaction was incomplete (a failed DoEmptyTransaction),
+ // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
+ // to the previous transaction.
+ dom::ScreenOrientationInternal orientation;
+ if (dom::TabChild* window = mWidget->GetOwningTabChild()) {
+ orientation = window->GetOrientation();
+ } else {
+ hal::ScreenConfiguration currentConfig;
+ hal::GetCurrentScreenConfiguration(&currentConfig);
+ orientation = currentConfig.orientation();
+ }
+ LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds();
+ targetBounds.x = targetBounds.y = 0;
+ mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation,
+ orientation);
+
+ // If we're drawing on behalf of a context with async pan/zoom
+ // enabled, then the entire buffer of painted layers might be
+ // composited (including resampling) asynchronously before we get
+ // a chance to repaint, so we have to ensure that it's all valid
+ // and not rotated.
+ //
+ // Desktop does not support async zoom yet, so we ignore this for those
+ // platforms.
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
+ if (mWidget && mWidget->GetOwningTabChild()) {
+ mCompositorMightResample = AsyncPanZoomEnabled();
+ }
+#endif
+
+ // If we have a non-default target, we need to let our shadow manager draw
+ // to it. This will happen at the end of the transaction.
+ if (aTarget && XRE_IsParentProcess()) {
+ mShadowTarget = aTarget;
+ } else {
+ NS_ASSERTION(!aTarget,
+ "Content-process ClientLayerManager::BeginTransactionWithTarget not supported");
+ }
+
+ // If this is a new paint, increment the paint sequence number.
+ if (!mIsRepeatTransaction) {
+ // Increment the paint sequence number even if test logging isn't
+ // enabled in this process; it may be enabled in the parent process,
+ // and the parent process expects unique sequence numbers.
+ ++mPaintSequenceNumber;
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewPaint(mPaintSequenceNumber);
+ }
+ }
+ return true;
+}
+
+bool
+ClientLayerManager::BeginTransaction()
+{
+ return BeginTransactionWithTarget(nullptr);
+}
+
+bool
+ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags)
+{
+ PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
+
+ PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+ profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
+
+ mTransactionIncomplete = false;
+
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ GetRoot()->ApplyPendingUpdatesToSubtree();
+
+ mPaintedLayerCallback = aCallback;
+ mPaintedLayerCallbackData = aCallbackData;
+
+ GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
+
+ // Skip the painting if the device is in device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ TimeStamp start = TimeStamp::Now();
+ root->RenderLayer();
+ mLastPaintTime = TimeStamp::Now() - start;
+ } else {
+ root->RenderLayer();
+ }
+ } else {
+ gfxCriticalNote << "LayerManager::EndTransaction skip RenderLayer().";
+ }
+
+ if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
+ GetRoot()->Mutated();
+ }
+
+ if (!mIsRepeatTransaction) {
+ mAnimationReadyTime = TimeStamp::Now();
+ GetRoot()->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+ mPaintedLayerCallback = nullptr;
+ mPaintedLayerCallbackData = nullptr;
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ }
+
+ return !mTransactionIncomplete;
+}
+
+void
+ClientLayerManager::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations)
+{
+ if (mForwarder) {
+ mForwarder->StorePluginWidgetConfigurations(aConfigurations);
+ }
+}
+
+void
+ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ if (!mForwarder->IPCOpen()) {
+ mInTransaction = false;
+ return;
+ }
+
+ if (mWidget) {
+ mWidget->PrepareWindowEffects();
+ }
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+ ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
+
+ if (mRepeatTransaction) {
+ mRepeatTransaction = false;
+ mIsRepeatTransaction = true;
+ if (BeginTransaction()) {
+ ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
+ }
+ mIsRepeatTransaction = false;
+ } else {
+ MakeSnapshotIfRequired();
+ }
+
+ mInTransaction = false;
+ mTransactionStart = TimeStamp();
+}
+
+bool
+ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot || !mForwarder->IPCOpen()) {
+ return false;
+ }
+
+ if (!EndTransactionInternal(nullptr, nullptr, aFlags)) {
+ // Return without calling ForwardTransaction. This leaves the
+ // ShadowLayerForwarder transaction open; the following
+ // EndTransaction will complete it.
+ return false;
+ }
+ if (mWidget) {
+ mWidget->PrepareWindowEffects();
+ }
+ ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
+ MakeSnapshotIfRequired();
+ return true;
+}
+
+CompositorBridgeChild *
+ClientLayerManager::GetRemoteRenderer()
+{
+ if (!mWidget) {
+ return nullptr;
+ }
+
+ return mWidget->GetRemoteRenderer();
+}
+
+CompositorBridgeChild*
+ClientLayerManager::GetCompositorBridgeChild()
+{
+ if (!XRE_IsParentProcess()) {
+ return CompositorBridgeChild::Get();
+ }
+ return GetRemoteRenderer();
+}
+
+void
+ClientLayerManager::Composite()
+{
+ mForwarder->Composite();
+}
+
+void
+ClientLayerManager::DidComposite(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ MOZ_ASSERT(mWidget);
+
+ // |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
+ // layers transaction.
+ if (aTransactionId) {
+ nsIWidgetListener *listener = mWidget->GetWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ listener = mWidget->GetAttachedWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
+ }
+
+ // These observers fire whether or not we were in a transaction.
+ for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
+ mDidCompositeObservers[i]->DidComposite();
+ }
+}
+
+void
+ClientLayerManager::GetCompositorSideAPZTestData(APZTestData* aData) const
+{
+ if (mForwarder->HasShadowManager()) {
+ if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ }
+}
+
+float
+ClientLayerManager::RequestProperty(const nsAString& aProperty)
+{
+ if (mForwarder->HasShadowManager()) {
+ float value;
+ if (!mForwarder->GetShadowManager()->SendRequestProperty(nsString(aProperty), &value)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ return value;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber)
+{
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewRepaintRequest(aSequenceNumber);
+ }
+}
+
+void
+ClientLayerManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(), "Frame Uniformity only supported in parent process");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ child->SendGetFrameUniformity(aOutData);
+ return;
+ }
+
+ return LayerManager::GetFrameUniformity(aOutData);
+}
+
+bool
+ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback)
+{
+ MOZ_ASSERT(aCallback != nullptr);
+ MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ NS_ASSERTION(child, "Could not get CompositorBridgeChild");
+
+ child->AddOverfillObserver(this);
+ child->SendRequestOverfill();
+ mOverfillCallbacks.AppendElement(aCallback);
+ }
+
+ return true;
+}
+
+void
+ClientLayerManager::RunOverfillCallback(const uint32_t aOverfill)
+{
+ for (size_t i = 0; i < mOverfillCallbacks.Length(); i++) {
+ ErrorResult error;
+ mOverfillCallbacks[i]->Call(aOverfill, error);
+ }
+
+ mOverfillCallbacks.Clear();
+}
+
+void
+ClientLayerManager::MakeSnapshotIfRequired()
+{
+ if (!mShadowTarget) {
+ return;
+ }
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
+ // The compositor doesn't draw to a different sized surface
+ // when there's a rotation. Instead we rotate the result
+ // when drawing into dt
+ LayoutDeviceIntRect outerBounds = mWidget->GetBounds();
+
+ IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents());
+ if (mTargetRotation) {
+ bounds =
+ RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation);
+ }
+
+ SurfaceDescriptor inSnapshot;
+ if (!bounds.IsEmpty() &&
+ mForwarder->AllocSurfaceDescriptor(bounds.Size(),
+ gfxContentType::COLOR_ALPHA,
+ &inSnapshot)) {
+
+ // Make a copy of |inSnapshot| because the call to send it over IPC
+ // will call forget() on the Shmem inside, and zero it out.
+ SurfaceDescriptor outSnapshot = inSnapshot;
+
+ if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
+ RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot);
+ DrawTarget* dt = mShadowTarget->GetDrawTarget();
+
+ Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ Rect srcRect(0, 0, bounds.width, bounds.height);
+
+ gfx::Matrix rotate =
+ ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
+ mTargetRotation);
+
+ gfx::Matrix oldMatrix = dt->GetTransform();
+ dt->SetTransform(rotate * oldMatrix);
+ dt->DrawSurface(surf, dstRect, srcRect,
+ DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_OVER));
+ dt->SetTransform(oldMatrix);
+ }
+ mForwarder->DestroySurfaceDescriptor(&outSnapshot);
+ }
+ }
+ }
+ mShadowTarget = nullptr;
+}
+
+void
+ClientLayerManager::FlushRendering()
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendFlushRendering();
+ }
+ }
+}
+
+void
+ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
+{
+ mForwarder->IdentifyTextureHost(aNewIdentifier);
+}
+
+void
+ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendNotifyRegionInvalidated(aRegion);
+ }
+ }
+}
+
+uint32_t
+ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ uint32_t startIndex;
+ renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
+ return startIndex;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
+ }
+}
+
+void
+ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
+{
+ TimeStamp start = TimeStamp::Now();
+
+ // Skip the synchronization for buffer since we also skip the painting during
+ // device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (mForwarder->GetSyncObject() &&
+ mForwarder->GetSyncObject()->IsSyncObjectValid()) {
+ mForwarder->GetSyncObject()->FinalizeFrame();
+ }
+ }
+
+ mPhase = PHASE_FORWARD;
+
+ mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
+ TimeStamp transactionStart;
+ if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
+ transactionStart = mTransactionIdAllocator->GetTransactionStart();
+ } else {
+ transactionStart = mTransactionStart;
+ }
+
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
+ }
+
+ // forward this transaction's changeset to our LayerManagerComposite
+ bool sent;
+ AutoTArray<EditReply, 10> replies;
+ if (mForwarder->EndTransaction(&replies, mRegionToClear,
+ mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
+ mIsRepeatTransaction, transactionStart, &sent)) {
+ for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
+ const EditReply& reply = replies[i];
+
+ switch (reply.type()) {
+ case EditReply::TOpContentBufferSwap: {
+ MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
+
+ const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
+
+ RefPtr<CompositableClient> compositable =
+ CompositableClient::FromIPDLActor(obs.compositableChild());
+ ContentClientRemote* contentClient =
+ static_cast<ContentClientRemote*>(compositable.get());
+ MOZ_ASSERT(contentClient);
+
+ contentClient->SwapBuffers(obs.frontUpdatedRegion());
+
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+ }
+
+ if (sent) {
+ mNeedsComposite = false;
+ }
+ } else if (HasShadowManager()) {
+ NS_WARNING("failed to forward Layers transaction");
+ }
+
+ if (!sent) {
+ // Clear the transaction id so that it doesn't get returned
+ // unless we forwarded to somewhere that doesn't actually
+ // have a compositor.
+ mTransactionIdAllocator->RevokeTransactionId(mLatestTransactionId);
+ }
+
+ mPhase = PHASE_NONE;
+
+ // this may result in Layers being deleted, which results in
+ // PLayer::Send__delete__() and DeallocShmem()
+ mKeepAlive.Clear();
+
+ TabChild* window = mWidget ? mWidget->GetOwningTabChild() : nullptr;
+ if (window) {
+ TimeStamp end = TimeStamp::Now();
+ window->DidRequestComposite(start, end);
+ }
+}
+
+ShadowableLayer*
+ClientLayerManager::Hold(Layer* aLayer)
+{
+ MOZ_ASSERT(HasShadowManager(),
+ "top-level tree, no shadow tree to remote to");
+
+ ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer);
+ MOZ_ASSERT(shadowable, "trying to remote an unshadowable layer");
+
+ mKeepAlive.AppendElement(aLayer);
+ return shadowable;
+}
+
+bool
+ClientLayerManager::IsCompositingCheap()
+{
+ // Whether compositing is cheap depends on the parent backend.
+ return mForwarder->mShadowManager &&
+ LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
+}
+
+bool
+ClientLayerManager::AreComponentAlphaLayersEnabled()
+{
+ return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
+ AsShadowForwarder()->SupportsComponentAlpha() &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+void
+ClientLayerManager::SetIsFirstPaint()
+{
+ mForwarder->SetIsFirstPaint();
+}
+
+void
+ClientLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ if (mDestroyed) {
+ // ClearCachedResource was already called by ClientLayerManager::Destroy
+ return;
+ }
+ MOZ_ASSERT(!HasShadowManager() || !aSubtree);
+ mForwarder->ClearCachedResources();
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressure()
+{
+ if (mRoot) {
+ HandleMemoryPressureLayer(mRoot);
+ }
+
+ if (GetCompositorBridgeChild()) {
+ GetCompositorBridgeChild()->HandleMemoryPressure();
+ }
+}
+
+void
+ClientLayerManager::ClearLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ HandleMemoryPressureLayer(child);
+ }
+}
+
+void
+ClientLayerManager::GetBackendName(nsAString& aName)
+{
+ switch (mForwarder->GetCompositorBackendType()) {
+ case LayersBackend::LAYERS_NONE: aName.AssignLiteral("None"); return;
+ case LayersBackend::LAYERS_BASIC: aName.AssignLiteral("Basic"); return;
+ case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
+ case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
+ case LayersBackend::LAYERS_D3D11: {
+#ifdef XP_WIN
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ aName.AssignLiteral("Direct3D 11 WARP");
+ } else {
+ aName.AssignLiteral("Direct3D 11");
+ }
+#endif
+ return;
+ }
+ default: NS_RUNTIMEABORT("Invalid backend");
+ }
+}
+
+bool
+ClientLayerManager::AsyncPanZoomEnabled() const
+{
+ return mWidget && mWidget->AsyncPanZoomEnabled();
+}
+
+void
+ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
+{
+ mForwarder->SetPaintSyncId(aSyncId);
+}
+
+void
+ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+ mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+void
+ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ if (!mDidCompositeObservers.Contains(aObserver)) {
+ mDidCompositeObservers.AppendElement(aObserver);
+ }
+}
+
+void
+ClientLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ mDidCompositeObservers.RemoveElement(aObserver);
+}
+
+bool
+ClientLayerManager::DependsOnStaleDevice() const
+{
+ return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
+}
+
+
+already_AddRefed<PersistentBufferProvider>
+ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat)
+{
+ // Don't use a shared buffer provider if compositing is considered "not cheap"
+ // because the canvas will most likely be flattened into a thebes layer instead
+ // of being sent to the compositor, in which case rendering into shared memory
+ // is wasteful.
+ if (IsCompositingCheap() &&
+ gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+ RefPtr<PersistentBufferProvider> provider
+ = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+ if (provider) {
+ return provider.forget();
+ }
+ }
+
+ return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
+}
+
+
+ClientLayer::~ClientLayer()
+{
+ if (HasShadow()) {
+ PLayerChild::Send__delete__(GetShadow());
+ }
+ MOZ_COUNT_DTOR(ClientLayer);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h
new file mode 100644
index 000000000..5bcd5e412
--- /dev/null
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -0,0 +1,422 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTLAYERMANAGER_H
+#define GFX_CLIENTLAYERMANAGER_H
+
+#include <stdint.h> // for int32_t
+#include "Layers.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/LinkedList.h" // For LinkedList
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/layers/APZTestData.h" // for APZTestData
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsIObserver.h" // for nsIObserver
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsAString
+#include "mozilla/layers/TransactionIdAllocator.h"
+#include "nsIWidget.h" // For plugin window configuration information structs
+
+namespace mozilla {
+namespace layers {
+
+class ClientPaintedLayer;
+class CompositorBridgeChild;
+class ImageLayer;
+class PLayerChild;
+class FrameUniformityData;
+
+class ClientLayerManager final : public LayerManager
+{
+ typedef nsTArray<RefPtr<Layer> > LayerRefArray;
+
+public:
+ explicit ClientLayerManager(nsIWidget* aWidget);
+
+ virtual void Destroy() override;
+
+protected:
+ virtual ~ClientLayerManager();
+
+public:
+ virtual ShadowLayerForwarder* AsShadowForwarder() override
+ {
+ return mForwarder;
+ }
+
+ virtual ClientLayerManager* AsClientLayerManager() override
+ {
+ return this;
+ }
+
+ virtual int32_t GetMaxTextureSize() const override;
+
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool BeginTransaction() override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_CLIENT; }
+ virtual LayersBackend GetCompositorBackendType() override
+ {
+ return AsShadowForwarder()->GetCompositorBackendType();
+ }
+ virtual void GetBackendName(nsAString& name) override;
+ virtual const char* Name() const override { return "Client"; }
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual void Mutated(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<RefLayer> CreateRefLayer() override;
+
+ void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier);
+ TextureFactoryIdentifier GetTextureFactoryIdentifier()
+ {
+ return AsShadowForwarder()->GetTextureFactoryIdentifier();
+ }
+
+ virtual void FlushRendering() override;
+ void SendInvalidRegion(const nsIntRegion& aRegion);
+
+ virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
+
+ virtual void StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals) override;
+
+ virtual bool NeedsWidgetInvalidation() override { return false; }
+
+ ShadowableLayer* Hold(Layer* aLayer);
+
+ bool HasShadowManager() const { return mForwarder->HasShadowManager(); }
+
+ virtual bool IsCompositingCheap() override;
+ virtual bool HasShadowManagerInternal() const override { return HasShadowManager(); }
+
+ virtual void SetIsFirstPaint() override;
+
+ /**
+ * Pass through call to the forwarder for nsPresContext's
+ * CollectPluginGeometryUpdates. Passes widget configuration information
+ * to the compositor for transmission to the chrome process. This
+ * configuration gets set when the window paints.
+ */
+ void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations) override;
+
+ // Drop cached resources and ask our shadow manager to do the same,
+ // if we have one.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void HandleMemoryPressure();
+
+ void SetRepeatTransaction() { mRepeatTransaction = true; }
+ bool GetRepeatTransaction() { return mRepeatTransaction; }
+
+ bool IsRepeatTransaction() { return mIsRepeatTransaction; }
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+
+ bool HasShadowTarget() { return !!mShadowTarget; }
+
+ void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
+
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+ DrawPaintedLayerCallback GetPaintedLayerCallback() const
+ { return mPaintedLayerCallback; }
+
+ void* GetPaintedLayerCallbackData() const
+ { return mPaintedLayerCallbackData; }
+
+ CompositorBridgeChild* GetRemoteRenderer();
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ // Disable component alpha layers with the software compositor.
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ void SetNeedsComposite(bool aNeedsComposite)
+ {
+ mNeedsComposite = aNeedsComposite;
+ }
+ bool NeedsComposite() const { return mNeedsComposite; }
+
+ virtual void Composite() override;
+ virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
+ virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
+ virtual void RunOverfillCallback(const uint32_t aOverfill) override;
+
+ void DidComposite(uint64_t aTransactionId,
+ const mozilla::TimeStamp& aCompositeStart,
+ const mozilla::TimeStamp& aCompositeEnd);
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ // Log APZ test data for the current paint. We supply the paint sequence
+ // number ourselves, and take care of calling APZTestData::StartNewPaint()
+ // when a new paint is started.
+ void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Log APZ test data for a repaint request. The sequence number must be
+ // passed in from outside, and APZTestData::StartNewRepaintRequest() needs
+ // to be called from the outside as well when a new repaint request is started.
+ void StartNewRepaintRequest(SequenceNumber aSequenceNumber);
+
+ // TODO(botond): When we start using this and write a wrapper similar to
+ // nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks
+ // gfxPrefs::APZTestLoggingEnabled().
+ void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
+ FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForRepaintRequest(aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Get the content-side APZ test data for reading. For writing, use the
+ // LogTestData...() functions.
+ const APZTestData& GetAPZTestData() const {
+ return mApzTestData;
+ }
+
+ // Get a copy of the compositor-side APZ test data for our layers ID.
+ void GetCompositorSideAPZTestData(APZTestData* aData) const;
+
+ void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
+
+ float RequestProperty(const nsAString& aProperty) override;
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void SetNextPaintSyncId(int32_t aSyncId);
+
+ void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
+ class DidCompositeObserver {
+ public:
+ virtual void DidComposite() = 0;
+ };
+
+ void AddDidCompositeObserver(DidCompositeObserver* aObserver);
+ void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
+
+ virtual already_AddRefed<PersistentBufferProvider>
+ CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+private:
+ // Listen memory-pressure event for ClientLayerManager
+ class MemoryPressureObserver final : public nsIObserver
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit MemoryPressureObserver(ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager)
+ {
+ RegisterMemoryPressureEvent();
+ }
+
+ void Destroy();
+
+ private:
+ virtual ~MemoryPressureObserver() {}
+ void RegisterMemoryPressureEvent();
+ void UnregisterMemoryPressureEvent();
+
+ ClientLayerManager* mClientLayerManager;
+ };
+
+ /**
+ * Forward transaction results to the parent context.
+ */
+ void ForwardTransaction(bool aScheduleComposite);
+
+ /**
+ * Take a snapshot of the parent context, and copy
+ * it into mShadowTarget.
+ */
+ void MakeSnapshotIfRequired();
+
+ void ClearLayer(Layer* aLayer);
+
+ void HandleMemoryPressureLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags);
+
+ bool DependsOnStaleDevice() const;
+
+ LayerRefArray mKeepAlive;
+
+ nsIWidget* mWidget;
+
+ /* PaintedLayer callbacks; valid at the end of a transaciton,
+ * while rendering */
+ DrawPaintedLayerCallback mPaintedLayerCallback;
+ void *mPaintedLayerCallbackData;
+
+ // When we're doing a transaction in order to draw to a non-default
+ // target, the layers transaction is only performed in order to send
+ // a PLayers:Update. We save the original non-default target to
+ // mShadowTarget, and then perform the transaction using
+ // mDummyTarget as the render target. After the transaction ends,
+ // we send a message to our remote side to capture the actual pixels
+ // being drawn to the default target, and then copy those pixels
+ // back to mShadowTarget.
+ RefPtr<gfxContext> mShadowTarget;
+
+ RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
+ uint64_t mLatestTransactionId;
+ TimeDuration mLastPaintTime;
+
+ // Sometimes we draw to targets that don't natively support
+ // landscape/portrait orientation. When we need to implement that
+ // ourselves, |mTargetRotation| describes the induced transform we
+ // need to apply when compositing content to our target.
+ ScreenRotation mTargetRotation;
+
+ // Used to repeat the transaction right away (to avoid rebuilding
+ // a display list) to support progressive drawing.
+ bool mRepeatTransaction;
+ bool mIsRepeatTransaction;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+ bool mNeedsComposite;
+
+ // An incrementing sequence number for paints.
+ // Incremented in BeginTransaction(), but not for repeat transactions.
+ uint32_t mPaintSequenceNumber;
+
+ APZTestData mApzTestData;
+
+ RefPtr<ShadowLayerForwarder> mForwarder;
+ AutoTArray<dom::OverfillCallback*,0> mOverfillCallbacks;
+ mozilla::TimeStamp mTransactionStart;
+
+ nsTArray<DidCompositeObserver*> mDidCompositeObservers;
+
+ RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
+ uint64_t mDeviceCounter;
+};
+
+class ClientLayer : public ShadowableLayer
+{
+public:
+ ClientLayer()
+ {
+ MOZ_COUNT_CTOR(ClientLayer);
+ }
+
+ ~ClientLayer();
+
+ void SetShadow(PLayerChild* aShadow)
+ {
+ MOZ_ASSERT(!mShadow, "can't have two shadows (yet)");
+ mShadow = aShadow;
+ }
+
+ virtual void Disconnect()
+ {
+ // This is an "emergency Disconnect()", called when the compositing
+ // process has died. |mShadow| and our Shmem buffers are
+ // automatically managed by IPDL, so we don't need to explicitly
+ // free them here (it's hard to get that right on emergency
+ // shutdown anyway).
+ mShadow = nullptr;
+ }
+
+ virtual void ClearCachedResources() { }
+
+ // Shrink memory usage.
+ // Called when "memory-pressure" is observed.
+ virtual void HandleMemoryPressure() { }
+
+ virtual void RenderLayer() = 0;
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
+
+ virtual ClientPaintedLayer* AsThebes() { return nullptr; }
+
+ static inline ClientLayer *
+ ToClientLayer(Layer* aLayer)
+ {
+ return static_cast<ClientLayer*>(aLayer->ImplData());
+ }
+
+ template <typename LayerType>
+ static inline void RenderMaskLayers(LayerType* aLayer) {
+ if (aLayer->GetMaskLayer()) {
+ ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
+ }
+ for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+ ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
+ }
+ }
+};
+
+// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
+// our layer tree to a parent process. Record the new layer creation
+// in the current open transaction as a side effect.
+template<typename CreatedMethod> void
+CreateShadowFor(ClientLayer* aLayer,
+ ClientLayerManager* aMgr,
+ CreatedMethod aMethod)
+{
+ PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
+ if (!shadow) {
+ return;
+ }
+
+ aLayer->SetShadow(shadow);
+ (aMgr->AsShadowForwarder()->*aMethod)(aLayer);
+ aMgr->Hold(aLayer->AsLayer());
+}
+
+#define CREATE_SHADOW(_type) \
+ CreateShadowFor(layer, this, \
+ &ShadowLayerForwarder::Created ## _type ## Layer)
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTLAYERMANAGER_H */
diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp
new file mode 100644
index 000000000..871f10559
--- /dev/null
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientPaintedLayer.h"
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxContext.h" // for gfxContext
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/Preferences.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "gfx2DGlue.h"
+#include "ReadbackProcessor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientPaintedLayer::PaintThebes()
+{
+ PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(ClientManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (ClientManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+#endif
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+
+ bool didUpdate = false;
+ RotatedContentBuffer::DrawIterator iter;
+ while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
+ if (!target || !target->IsValid()) {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ }
+ continue;
+ }
+
+ SetAntialiasingFlags(this, target);
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ ClientManager()->GetPaintedLayerCallback()(this,
+ ctx,
+ iter.mDrawRegion,
+ iter.mDrawRegion,
+ state.mClip,
+ state.mRegionToInvalidate,
+ ClientManager()->GetPaintedLayerCallbackData());
+
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ didUpdate = true;
+ }
+
+ if (didUpdate) {
+ Mutated();
+
+ mValidRegion.Or(mValidRegion, state.mRegionToDraw);
+
+ ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
+ MOZ_ASSERT(contentClientRemote->GetIPDLActor());
+
+ // Hold(this) ensures this layer is kept alive through the current transaction
+ // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
+ // so deleting this Hold for whatever reason will break things.
+ ClientManager()->Hold(this);
+ contentClientRemote->Updated(state.mRegionToDraw,
+ mVisibleRegion.ToUnknownRegion(),
+ state.mDidSelfCopy);
+ }
+}
+
+void
+ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
+{
+ RenderMaskLayers(this);
+
+ if (!mContentClient) {
+ mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
+ if (!mContentClient) {
+ return;
+ }
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ nsIntRegion readbackRegion;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ IntPoint origin(mVisibleRegion.GetBounds().x, mVisibleRegion.GetBounds().y);
+ mContentClient->BeginPaint();
+ PaintThebes();
+ mContentClient->EndPaint(&readbackUpdates);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayer()
+{
+ return CreatePaintedLayerWithHint(NONE);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ if (gfxPrefs::LayersTilesEnabled()) {
+ RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ } else {
+ RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ }
+}
+
+void
+ClientPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h
new file mode 100644
index 000000000..949f746ae
--- /dev/null
+++ b/gfx/layers/client/ClientPaintedLayer.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTPAINTEDLAYER_H
+#define GFX_CLIENTPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/ContentClient.h" // for ContentClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+class ClientPaintedLayer : public PaintedLayer,
+ public ClientLayer {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit ClientPaintedLayer(ClientLayerManager* aLayerManager,
+ LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) :
+ PaintedLayer(aLayerManager, static_cast<ClientLayer*>(this), aCreationHint),
+ mContentClient(nullptr)
+ {
+ MOZ_COUNT_CTOR(ClientPaintedLayer);
+ }
+
+protected:
+ virtual ~ClientPaintedLayer()
+ {
+ if (mContentClient) {
+ mContentClient->OnDetach();
+ mContentClient = nullptr;
+ }
+ MOZ_COUNT_DTOR(ClientPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void RenderLayer() override { RenderLayerWithReadback(nullptr); }
+
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+ }
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mContentClient;
+ }
+
+ virtual void Disconnect() override
+ {
+ mContentClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ void PaintThebes();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void DestroyBackBuffer()
+ {
+ mContentClient = nullptr;
+ }
+
+ RefPtr<ContentClient> mContentClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientReadbackLayer.h b/gfx/layers/client/ClientReadbackLayer.h
new file mode 100644
index 000000000..5f58486ca
--- /dev/null
+++ b/gfx/layers/client/ClientReadbackLayer.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTREADBACKLAYER_H
+#define GFX_CLIENTREADBACKLAYER_H
+
+#include "ClientLayerManager.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientReadbackLayer :
+ public ReadbackLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientReadbackLayer(ClientLayerManager *aManager)
+ : ReadbackLayer(aManager, nullptr)
+ {
+ mImplData = static_cast<ClientLayer*>(this);
+ }
+
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+ virtual Layer* AsLayer() override { return this; }
+ virtual void RenderLayer() override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTREADBACKLAYER_H */
diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp
new file mode 100644
index 000000000..5a07681f6
--- /dev/null
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -0,0 +1,615 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientTiledPaintedLayer.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for ScreenIntRect, CSSPoint, etc
+#include "UnitTransforms.h" // for TransformTo
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Rect.h" // for Rect, RectTyped
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "LayersLogging.h"
+#include "mozilla/layers/SingleTiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::Rect;
+using gfx::IntRect;
+using gfx::IntSize;
+
+ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint)
+ : PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint)
+ , mContentClient()
+ , mHaveSingleTiledContentClient(false)
+{
+ MOZ_COUNT_CTOR(ClientTiledPaintedLayer);
+ mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
+ mPaintData.mFirstPaint = true;
+}
+
+ClientTiledPaintedLayer::~ClientTiledPaintedLayer()
+{
+ MOZ_COUNT_DTOR(ClientTiledPaintedLayer);
+}
+
+void
+ClientTiledPaintedLayer::ClearCachedResources()
+{
+ if (mContentClient) {
+ mContentClient->ClearCachedResources();
+ }
+ mValidRegion.SetEmpty();
+ mContentClient = nullptr;
+}
+
+void
+ClientTiledPaintedLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+}
+
+static Maybe<LayerRect>
+ApplyParentLayerToLayerTransform(const ParentLayerToLayerMatrix4x4& aTransform,
+ const ParentLayerRect& aParentLayerRect,
+ const LayerRect& aClip)
+{
+ return UntransformBy(aTransform, aParentLayerRect, aClip);
+}
+
+static LayerToParentLayerMatrix4x4
+GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor)
+{
+ gfx::Matrix4x4 transform;
+ const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent();
+ for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM);
+ ancestorParent ? iter != ancestorParent : iter.IsValid();
+ iter = iter.GetParent()) {
+ transform = transform * iter.GetTransform();
+
+ if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+ // When scrolling containers, layout adds a post-scale into the transform
+ // of the displayport-ancestor (which we pick up in GetTransform() above)
+ // to cancel out the pres shell resolution (for historical reasons). The
+ // compositor in turn cancels out this post-scale (i.e., scales by the
+ // pres shell resolution), and to get correct calculations, we need to do
+ // so here, too.
+ //
+ // With containerless scrolling, the offending post-scale is on the
+ // parent layer of the displayport-ancestor, which we don't reach in this
+ // loop, so we don't need to worry about it.
+ float presShellResolution = iter.GetPresShellResolution();
+ transform.PostScale(presShellResolution, presShellResolution, 1.0f);
+ }
+ }
+ return ViewAs<LayerToParentLayerMatrix4x4>(transform);
+}
+
+void
+ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation)
+{
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation = false;
+ for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
+ hasTransformAnimation |= ancestor.HasTransformAnimation();
+ const FrameMetrics& metrics = ancestor.Metrics();
+ if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
+ scrollAncestor = ancestor;
+ }
+ if (!metrics.GetDisplayPort().IsEmpty()) {
+ displayPortAncestor = ancestor;
+ // Any layer that has a displayport must be scrollable, so we can break
+ // here.
+ break;
+ }
+ }
+ if (aOutScrollAncestor) {
+ *aOutScrollAncestor = scrollAncestor;
+ }
+ if (aOutDisplayPortAncestor) {
+ *aOutDisplayPortAncestor = displayPortAncestor;
+ }
+ if (aOutHasTransformAnimation) {
+ *aOutHasTransformAnimation = hasTransformAnimation;
+ }
+}
+
+void
+ClientTiledPaintedLayer::BeginPaint()
+{
+ mPaintData.ResetPaintData();
+
+ if (!GetBaseTransform().Is2D()) {
+ // Give up if there is a complex CSS transform on the layer. We might
+ // eventually support these but for now it's too complicated to handle
+ // given that it's a pretty rare scenario.
+ return;
+ }
+
+ // Get the metrics of the nearest scrollable layer and the nearest layer
+ // with a displayport.
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation;
+ GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation);
+
+ if (!displayPortAncestor || !scrollAncestor) {
+ // No displayport or scroll ancestor, so we can't do progressive rendering.
+#if defined(MOZ_WIDGET_ANDROID)
+ // Both Android and b2g on phones are guaranteed to have a displayport set, so this
+ // should never happen.
+ NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor");
+#endif
+ return;
+ }
+
+ TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this,
+ scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation);
+
+ const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
+ const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();
+
+ // Calculate the transform required to convert ParentLayer space of our
+ // display port ancestor to the Layer space of this layer.
+ ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer =
+ GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse();
+
+ LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));
+
+ // Compute the critical display port that applies to this layer in the
+ // LayoutDevice space of this layer, but only if there is no OMT animation
+ // on this layer. If there is an OMT animation then we need to draw the whole
+ // visible region of this layer as determined by layout, because we don't know
+ // what parts of it might move into view in the compositor.
+ mPaintData.mHasTransformAnimation = hasTransformAnimation;
+ if (!mPaintData.mHasTransformAnimation &&
+ mContentClient->GetLowPrecisionTiledBuffer()) {
+ ParentLayerRect criticalDisplayPort =
+ (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
+ + displayportMetrics.GetCompositionBounds().TopLeft();
+ Maybe<LayerRect> criticalDisplayPortTransformed =
+ ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds);
+ if (criticalDisplayPortTransformed) {
+ mPaintData.mCriticalDisplayPort = Some(RoundedToInt(*criticalDisplayPortTransformed));
+ } else {
+ mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0));
+ }
+ }
+ TILING_LOG("TILING %p: Critical displayport %s\n", this,
+ mPaintData.mCriticalDisplayPort ?
+ Stringify(*mPaintData.mCriticalDisplayPort).c_str() : "not set");
+
+ // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
+ // before any async transforms have occurred, we can use the zoom for this.
+ mPaintData.mResolution = displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str());
+
+ // Store the applicable composition bounds in this layer's Layer units.
+ mPaintData.mTransformToCompBounds =
+ GetTransformToAncestorsParentLayer(this, scrollAncestor);
+ ParentLayerToLayerMatrix4x4 transformToBounds = mPaintData.mTransformToCompBounds.Inverse();
+ Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform(
+ transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds);
+ if (compositionBoundsTransformed) {
+ mPaintData.mCompositionBounds = *compositionBoundsTransformed;
+ } else {
+ mPaintData.mCompositionBounds.SetEmpty();
+ }
+ TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());
+
+ // Calculate the scroll offset since the last transaction
+ mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
+}
+
+bool
+ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics)
+{
+ CompositorBridgeChild* compositor = nullptr;
+ if (Manager() && Manager()->AsClientLayerManager()) {
+ compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ FrameMetrics compositorMetrics;
+ if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta
+ // is so small then we have nothing to gain from using paint heuristics.
+ float COORDINATE_EPSILON = 1.f;
+
+ return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x,
+ aParentMetrics.GetScrollOffset().x,
+ COORDINATE_EPSILON) ||
+ !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y,
+ aParentMetrics.GetScrollOffset().y,
+ COORDINATE_EPSILON);
+}
+
+bool
+ClientTiledPaintedLayer::UseProgressiveDraw() {
+ if (!gfxPrefs::ProgressivePaint()) {
+ // pref is disabled, so never do progressive
+ return false;
+ }
+
+ if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) {
+ return false;
+ }
+
+ if (ClientManager()->HasShadowTarget()) {
+ // This condition is true when we are in a reftest scenario. We don't want
+ // to draw progressively here because it can cause intermittent reftest
+ // failures because the harness won't wait for all the tiles to be drawn.
+ return false;
+ }
+
+ if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
+ // This layer is fixed-position and so even if it does have a scrolling
+ // ancestor it will likely be entirely on-screen all the time, so we
+ // should draw it all at once
+ return false;
+ }
+
+ if (mPaintData.mHasTransformAnimation) {
+ // The compositor is going to animate this somehow, so we want it all
+ // on the screen at once.
+ return false;
+ }
+
+ if (ClientManager()->AsyncPanZoomEnabled()) {
+ LayerMetricsWrapper scrollAncestor;
+ GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+ MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set
+ if (!scrollAncestor) {
+ return false;
+ }
+ const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
+ if (!IsScrollingOnCompositor(parentMetrics)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // If we have started drawing low-precision already, then we
+ // shouldn't do anything there.
+ if (mPaintData.mLowPrecisionPaintCount != 0) {
+ return false;
+ }
+
+ // Only draw progressively when there is something to paint and the
+ // resolution is unchanged
+ if (!aInvalidRegion.IsEmpty() &&
+ UseProgressiveDraw() &&
+ mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) {
+ // Store the old valid region, then clear it before painting.
+ // We clip the old valid region to the visible region, as it only gets
+ // used to decide stale content (currently valid and previously visible)
+ nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());
+
+ return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion,
+ oldValidRegion, &mPaintData, aCallback, aCallbackData);
+ }
+
+ // Otherwise do a non-progressive paint. We must do this even when
+ // the region to paint is empty as the valid region may have shrunk.
+
+ mValidRegion = aVisibleRegion;
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());
+
+ mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion,
+ aCallback, aCallbackData);
+ mPaintData.mPaintFinished = true;
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // Render the low precision buffer, if the visible region is larger than the
+ // critical display port.
+ if (!mPaintData.mCriticalDisplayPort ||
+ !nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()).Contains(aVisibleRegion)) {
+ nsIntRegion oldValidRegion = mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+
+ bool updatedBuffer = false;
+
+ // If the frame resolution or format have changed, invalidate the buffer
+ if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != mPaintData.mResolution ||
+ mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) {
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ updatedBuffer = true;
+ }
+ oldValidRegion.SetEmpty();
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ aInvalidRegion = aVisibleRegion;
+ }
+
+ // Invalidate previously valid content that is no longer visible
+ if (mPaintData.mLowPrecisionPaintCount == 1) {
+ mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion);
+ }
+ mPaintData.mLowPrecisionPaintCount++;
+
+ // Remove the valid high-precision region from the invalid low-precision
+ // region. We don't want to spend time drawing things twice.
+ aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str());
+
+ if (!aInvalidRegion.IsEmpty()) {
+ updatedBuffer = mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate(
+ mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
+ &mPaintData, aCallback, aCallbackData);
+ }
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+ return updatedBuffer;
+ }
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ TILING_LOG("TILING %p: Clearing low-precision buffer\n", this);
+ // Clear the low precision tiled buffer.
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ // Return true here so we send a Painted callback after clearing the valid
+ // region of the low precision buffer. This allows the shadow buffer's valid
+ // region to be updated and the associated resources to be freed.
+ return true;
+ }
+ return false;
+}
+
+void
+ClientTiledPaintedLayer::EndPaint()
+{
+ mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
+ mPaintData.mPaintFinished = true;
+ mPaintData.mFirstPaint = false;
+ TILING_LOG("TILING %p: Paint finished\n", this);
+}
+
+void
+ClientTiledPaintedLayer::RenderLayer()
+{
+ LayerManager::DrawPaintedLayerCallback callback =
+ ClientManager()->GetPaintedLayerCallback();
+ void *data = ClientManager()->GetPaintedLayerCallbackData();
+
+ IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
+ IntSize tileSize = gfx::gfxVars::TileSize();
+ bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 ||
+ layerSize.height <= tileSize.height / 2;
+
+ // Use single tile when layer is not scrollable, is smaller than one
+ // tile, or when more than half of the tiles' pixels in either
+ // dimension would be wasted.
+ bool wantSingleTiledContentClient =
+ (mCreationHint == LayerManager::NONE ||
+ layerSize <= tileSize ||
+ isHalfTileWidthOrHeight) &&
+ SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
+ gfxPrefs::LayersSingleTileEnabled();
+
+ if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) {
+ mContentClient = nullptr;
+ mValidRegion.SetEmpty();
+ }
+
+ if (!mContentClient) {
+ if (wantSingleTiledContentClient) {
+ mContentClient = new SingleTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = true;
+ } else {
+ mContentClient = new MultiTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = false;
+ }
+
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ if (mContentClient->GetTiledBuffer()->HasFormatChanged()) {
+ mValidRegion = nsIntRegion();
+ mContentClient->GetTiledBuffer()->ResetPaintedAndValidState();
+ }
+
+ TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
+ TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+
+ nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion();
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
+ if (MayResample()) {
+ // If we're resampling then bilinear filtering can read up to 1 pixel
+ // outside of our texture coords. Make the visible region a single rect,
+ // and pad it out by 1 pixel (restricted to tile boundaries) so that
+ // we always have valid content or transparent pixels to sample from.
+ IntRect bounds = neededRegion.GetBounds();
+ IntRect wholeTiles = bounds;
+ wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize());
+ IntRect padded = bounds;
+ padded.Inflate(1);
+ padded.IntersectRect(padded, wholeTiles);
+ neededRegion = padded;
+ }
+#endif
+
+ nsIntRegion invalidRegion;
+ invalidRegion.Sub(neededRegion, mValidRegion);
+ if (invalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (!callback) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ if (!ClientManager()->IsRepeatTransaction()) {
+ // Only paint the mask layers on the first transaction.
+ RenderMaskLayers(this);
+
+ // For more complex cases we need to calculate a bunch of metrics before we
+ // can do the paint.
+ BeginPaint();
+ if (mPaintData.mPaintFinished) {
+ return;
+ }
+
+ // Make sure that tiles that fall outside of the visible region or outside of the
+ // critical displayport are discarded on the first update. Also make sure that we
+ // only draw stuff inside the critical displayport on the first update.
+ mValidRegion.And(mValidRegion, neededRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ } else {
+ if (mPaintData.mCriticalDisplayPort) {
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+ TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ }
+
+ nsIntRegion lowPrecisionInvalidRegion;
+ if (mContentClient->GetLowPrecisionTiledBuffer()) {
+ // Calculate the invalid region for the low precision buffer. Make sure
+ // to remove the valid high-precision area so we don't double-paint it.
+ lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion);
+ lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
+ }
+ TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str());
+
+ bool updatedHighPrecision = RenderHighPrecision(invalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedHighPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more high-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If there is nothing to draw in low-precision, then we're done.
+ if (lowPrecisionInvalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (updatedHighPrecision) {
+ // If there are low precision updates, but we just did some high-precision
+ // updates, then mark the paint as unfinished and request a repeat transaction.
+ // This is so that we don't perform low-precision updates in the same transaction
+ // as high-precision updates.
+ TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this);
+ ClientManager()->SetRepeatTransaction();
+ mPaintData.mLowPrecisionPaintCount = 1;
+ mPaintData.mPaintFinished = false;
+ return;
+ }
+
+ bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedLowPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more low-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If we get here, we've done all the high- and low-precision
+ // paints we wanted to do, so we can finish the paint and chill.
+ EndPaint();
+}
+
+bool
+ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint)
+{
+ // The only creation hint is whether the layer is scrollable or not, and this
+ // is only respected on B2G and OSX, where it's used to determine whether to
+ // use a tiled content client or not.
+ // There are pretty nasty performance consequences for not using tiles on
+ // large, scrollable layers, so we want the layer to be recreated in this
+ // situation.
+ return aHint == GetCreationHint();
+}
+
+void
+ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientTiledPaintedLayer.h b/gfx/layers/client/ClientTiledPaintedLayer.h
new file mode 100644
index 000000000..9e1e003ee
--- /dev/null
+++ b/gfx/layers/client/ClientTiledPaintedLayer.h
@@ -0,0 +1,153 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIENTTILEDPAINTEDLAYER_H
+#define GFX_CLIENTTILEDPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayer, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/TiledContentClient.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+/**
+ * An implementation of PaintedLayer that ONLY supports remote
+ * composition that is backed by tiles. This painted layer implementation
+ * is better suited to mobile hardware to work around slow implementation
+ * of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
+ *
+ * Tiled PaintedLayers use a different protocol compared with other
+ * layers. A copy of the tiled buffer is made and sent to the compositing
+ * thread via the layers protocol. Tiles are uploaded by the buffers
+ * asynchonously without using IPC, that means they are not safe for cross-
+ * process use (bug 747811). Each tile has a TextureHost/Client pair but
+ * they communicate directly rather than using the Texture protocol.
+ *
+ * There is no ContentClient for tiled layers. There is a ContentHost, however.
+ */
+class ClientTiledPaintedLayer : public PaintedLayer,
+ public ClientLayer
+{
+ typedef PaintedLayer Base;
+
+public:
+ explicit ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE);
+
+protected:
+ ~ClientTiledPaintedLayer();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+public:
+ // Override name to distinguish it from ClientPaintedLayer in layer dumps
+ virtual const char* Name() const override { return "TiledPaintedLayer"; }
+
+ // PaintedLayer
+ virtual Layer* AsLayer() override { return this; }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override {
+ mInvalidRegion.Add(aRegion);
+ nsIntRegion invalidRegion = mInvalidRegion.GetRegion();
+ mValidRegion.Sub(mValidRegion, invalidRegion);
+ mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, invalidRegion);
+ }
+
+ // Shadow methods
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override;
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ /**
+ * Helper method to find the nearest ancestor layers which
+ * scroll and have a displayport. The parameters are out-params
+ * which hold the return values; the values passed in may be null.
+ */
+ void GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation);
+
+ virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) override;
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ /**
+ * For the initial PaintThebes of a transaction, calculates all the data
+ * needed for that paint and any repeated transactions.
+ */
+ void BeginPaint();
+
+ /**
+ * Check if the layer is being scrolled by APZ on the compositor.
+ */
+ bool IsScrollingOnCompositor(const FrameMetrics& aParentMetrics);
+
+ /**
+ * Check if we should use progressive draw on this layer. We will
+ * disable progressive draw based on a preference or if the layer
+ * is not being scrolled.
+ */
+ bool UseProgressiveDraw();
+
+ /**
+ * Helper function to do the high-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * Helper function to do the low-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * This causes the paint to be marked as finished, and updates any data
+ * necessary to persist until the next paint.
+ */
+ void EndPaint();
+
+ RefPtr<TiledContentClient> mContentClient;
+ // Flag to indicate if mContentClient is a SingleTiledContentClient. This is
+ // only valid when mContentClient is non-null.
+ bool mHaveSingleTiledContentClient;
+ nsIntRegion mLowPrecisionValidRegion;
+ BasicTiledLayerPaintData mPaintData;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/CompositableChild.cpp b/gfx/layers/client/CompositableChild.cpp
new file mode 100644
index 000000000..34a0e0696
--- /dev/null
+++ b/gfx/layers/client/CompositableChild.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableChild.h"
+#include "CompositableClient.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */ PCompositableChild*
+CompositableChild::CreateActor()
+{
+ CompositableChild* child = new CompositableChild();
+ child->AddRef();
+ return child;
+}
+
+/* static */ void
+CompositableChild::DestroyActor(PCompositableChild* aChild)
+{
+ static_cast<CompositableChild*>(aChild)->Release();
+}
+
+CompositableChild::CompositableChild()
+ : mCompositableClient(nullptr),
+ mAsyncID(0),
+ mCanSend(true)
+{
+ MOZ_COUNT_CTOR(CompositableChild);
+}
+
+CompositableChild::~CompositableChild()
+{
+ MOZ_COUNT_DTOR(CompositableChild);
+}
+
+bool
+CompositableChild::IsConnected() const
+{
+ return mCompositableClient && mCanSend;
+}
+
+void
+CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID)
+{
+ mCompositableClient = aCompositable;
+ mAsyncID = aAsyncID;
+}
+
+void
+CompositableChild::RevokeCompositableClient()
+{
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+CompositableChild::GetCompositableClient()
+{
+ return mCompositableClient;
+}
+
+void
+CompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mCanSend = false;
+
+ if (mCompositableClient) {
+ mCompositableClient->mCompositableChild = nullptr;
+ mCompositableClient = nullptr;
+ }
+}
+
+/* static */ PCompositableChild*
+AsyncCompositableChild::CreateActor()
+{
+ AsyncCompositableChild* child = new AsyncCompositableChild();
+ child->AddRef();
+ return child;
+}
+
+AsyncCompositableChild::AsyncCompositableChild()
+ : mLock("AsyncCompositableChild.mLock")
+{
+}
+
+AsyncCompositableChild::~AsyncCompositableChild()
+{
+}
+
+void
+AsyncCompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ mCanSend = false;
+
+ // We do not revoke CompositableClient::mCompositableChild here, since that
+ // could race with the main thread.
+ RevokeCompositableClient();
+}
+
+void
+AsyncCompositableChild::RevokeCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+AsyncCompositableChild::GetCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ return CompositableChild::GetCompositableClient();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CompositableChild.h b/gfx/layers/client/CompositableChild.h
new file mode 100644
index 000000000..381d8051b
--- /dev/null
+++ b/gfx/layers/client/CompositableChild.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_layers_client_CompositableChild_h
+#define mozilla_gfx_layers_client_CompositableChild_h
+
+#include <stdint.h>
+#include "IPDLActor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/layers/PCompositableChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class AsyncCompositableChild;
+
+/**
+ * IPDL actor used by CompositableClient to match with its corresponding
+ * CompositableHost on the compositor side.
+ *
+ * CompositableChild is owned by a CompositableClient.
+ */
+class CompositableChild : public PCompositableChild
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild)
+
+ static PCompositableChild* CreateActor();
+ static void DestroyActor(PCompositableChild* aChild);
+
+ void Init(CompositableClient* aCompositable, uint64_t aAsyncID);
+ virtual void RevokeCompositableClient();
+
+ virtual void ActorDestroy(ActorDestroyReason) override;
+
+ virtual RefPtr<CompositableClient> GetCompositableClient();
+
+ virtual AsyncCompositableChild* AsAsyncCompositableChild() {
+ return nullptr;
+ }
+
+ uint64_t GetAsyncID() const {
+ return mAsyncID;
+ }
+
+ // These should only be called on the IPDL thread.
+ bool IsConnected() const;
+ bool CanSend() const {
+ return mCanSend;
+ }
+
+protected:
+ CompositableChild();
+ virtual ~CompositableChild();
+
+protected:
+ CompositableClient* mCompositableClient;
+ uint64_t mAsyncID;
+ bool mCanSend;
+};
+
+// This CompositableChild can be used off the main thread.
+class AsyncCompositableChild final : public CompositableChild
+{
+public:
+ static PCompositableChild* CreateActor();
+
+ void RevokeCompositableClient() override;
+ RefPtr<CompositableClient> GetCompositableClient() override;
+
+ void ActorDestroy(ActorDestroyReason) override;
+
+ AsyncCompositableChild* AsAsyncCompositableChild() override {
+ return this;
+ }
+
+protected:
+ AsyncCompositableChild();
+ ~AsyncCompositableChild() override;
+
+private:
+ Mutex mLock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_client_CompositableChild_h
diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp
new file mode 100644
index 000000000..52b9a4637
--- /dev/null
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositableClient.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/layers/PCompositableChild.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureD3D9.h"
+#endif
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
+{
+ MOZ_ASSERT(aActor);
+
+ mForwarder->AssertInForwarderThread();
+
+ mCompositableChild = static_cast<CompositableChild*>(aActor);
+ mCompositableChild->Init(this, aAsyncID);
+}
+
+/* static */ RefPtr<CompositableClient>
+CompositableClient::FromIPDLActor(PCompositableChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ RefPtr<CompositableClient> client = static_cast<CompositableChild*>(aActor)->GetCompositableClient();
+ if (!client) {
+ return nullptr;
+ }
+
+ client->mForwarder->AssertInForwarderThread();
+ return client;
+}
+
+CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
+ TextureFlags aTextureFlags)
+: mForwarder(aForwarder)
+, mTextureFlags(aTextureFlags)
+{
+ MOZ_COUNT_CTOR(CompositableClient);
+}
+
+CompositableClient::~CompositableClient()
+{
+ MOZ_COUNT_DTOR(CompositableClient);
+ Destroy();
+}
+
+LayersBackend
+CompositableClient::GetCompositorBackendType() const
+{
+ return mForwarder->GetCompositorBackendType();
+}
+
+PCompositableChild*
+CompositableClient::GetIPDLActor() const
+{
+ return mCompositableChild;
+}
+
+bool
+CompositableClient::Connect(ImageContainer* aImageContainer)
+{
+ MOZ_ASSERT(!mCompositableChild);
+ if (!GetForwarder() || GetIPDLActor()) {
+ return false;
+ }
+
+ GetForwarder()->AssertInForwarderThread();
+ GetForwarder()->Connect(this, aImageContainer);
+ return true;
+}
+
+bool
+CompositableClient::IsConnected() const
+{
+ // CanSend() is only reliable in the same thread as the IPDL channel.
+ mForwarder->AssertInForwarderThread();
+ return mCompositableChild && mCompositableChild->IsConnected();
+}
+
+void
+CompositableClient::Destroy()
+{
+ if (!mCompositableChild) {
+ return;
+ }
+
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->Destroy();
+ }
+
+ // Take away our IPDL's actor reference back to us.
+ mCompositableChild->RevokeCompositableClient();
+
+ // Schedule the IPDL actor to be destroyed on the forwarder's thread.
+ mForwarder->Destroy(mCompositableChild);
+ mCompositableChild = nullptr;
+}
+
+uint64_t
+CompositableClient::GetAsyncID() const
+{
+ if (mCompositableChild) {
+ return mCompositableChild->GetAsyncID();
+ }
+ return 0; // zero is always an invalid async ID
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags)
+{
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aTextureFlags | mTextureFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(GetForwarder(),
+ aFormat, aSize, aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateFromSurface(GetForwarder(),
+ aSurface,
+ aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+bool
+CompositableClient::AddTextureClient(TextureClient* aClient)
+{
+ if(!aClient) {
+ return false;
+ }
+ aClient->SetAddedToCompositableClient();
+ return aClient->InitIPDLActor(mForwarder);
+}
+
+void
+CompositableClient::ClearCachedResources()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::HandleMemoryPressure()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::RemoveTexture(TextureClient* aTexture)
+{
+ mForwarder->RemoveTextureFromCompositable(this, aTexture);
+}
+
+TextureClientRecycleAllocator*
+CompositableClient::GetTextureClientRecycler()
+{
+ if (mTextureClientRecycler) {
+ return mTextureClientRecycler;
+ }
+
+ if (!mForwarder) {
+ return nullptr;
+ }
+
+ if(!mForwarder->GetTextureForwarder()->UsesImageBridge()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ // Handle a case that mForwarder is ImageBridge
+
+ if (InImageBridgeChildThread()) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ ReentrantMonitor barrier("CompositableClient::GetTextureClientRecycler");
+ ReentrantMonitorAutoEnter mainThreadAutoMon(barrier);
+ bool done = false;
+
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ if (!mTextureClientRecycler) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ }
+ ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
+ done = true;
+ barrier.NotifyAll();
+ });
+
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
+
+ // should stop the thread until done.
+ while (!done) {
+ barrier.Wait();
+ }
+
+ return mTextureClientRecycler;
+}
+
+void
+CompositableClient::DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress)
+{
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ if (aCompress == TextureDumpMode::Compress) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ } else {
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+ }
+}
+
+AutoRemoveTexture::~AutoRemoveTexture()
+{
+ if (mCompositable && mTexture && mCompositable->IsConnected()) {
+ mCompositable->RemoveTexture(mTexture);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h
new file mode 100644
index 000000000..07df59d59
--- /dev/null
+++ b/gfx/layers/client/CompositableClient.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BUFFERCLIENT_H
+#define MOZILLA_GFX_BUFFERCLIENT_H
+
+#include <stdint.h> // for uint64_t
+#include <vector> // for vector
+#include <map> // for map
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ImageBridgeChild;
+class ImageContainer;
+class CompositableForwarder;
+class CompositableChild;
+class PCompositableChild;
+class TextureClientRecycleAllocator;
+
+/**
+ * CompositableClient manages the texture-specific logic for composite layers,
+ * independently of the layer. It is the content side of a CompositableClient/
+ * CompositableHost pair.
+ *
+ * CompositableClient's purpose is to send texture data to the compositor side
+ * along with any extra information about how the texture is to be composited.
+ * Things like opacity or transformation belong to layer and not compositable.
+ *
+ * Since Compositables are independent of layers it is possible to create one,
+ * connect it to the compositor side, and start sending images to it. This alone
+ * is arguably not very useful, but it means that as long as a shadow layer can
+ * do the proper magic to find a reference to the right CompositableHost on the
+ * Compositor side, a Compositable client can be used outside of the main
+ * shadow layer forwarder machinery that is used on the main thread.
+ *
+ * The first step is to create a Compositable client and call Connect().
+ * Connect() creates the underlying IPDL actor (see CompositableChild) and the
+ * corresponding CompositableHost on the other side.
+ *
+ * To do in-transaction texture transfer (the default), call
+ * ShadowLayerForwarder::Attach(CompositableClient*, ShadowableLayer*). This
+ * will let the LayerComposite on the compositor side know which CompositableHost
+ * to use for compositing.
+ *
+ * To do async texture transfer (like async-video), the CompositableClient
+ * should be created with a different CompositableForwarder (like
+ * ImageBridgeChild) and attachment is done with
+ * CompositableForwarder::AttachAsyncCompositable that takes an identifier
+ * instead of a CompositableChild, since the CompositableClient is not managed
+ * by this layer forwarder (the matching uses a global map on the compositor side,
+ * see CompositableMap in ImageBridgeParent.cpp)
+ *
+ * Subclasses: Painted layers use ContentClients, ImageLayers use ImageClients,
+ * Canvas layers use CanvasClients (but ImageHosts). We have a different subclass
+ * where we have a different way of interfacing with the textures - in terms of
+ * drawing into the compositable and/or passing its contents to the compostior.
+ */
+class CompositableClient
+{
+protected:
+ virtual ~CompositableClient();
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableClient)
+
+ explicit CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = TextureFlags::NO_FLAGS);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) {};
+
+ virtual TextureInfo GetTextureInfo() const = 0;
+
+ LayersBackend GetCompositorBackendType() const;
+
+ already_AddRefed<TextureClient>
+ CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ /**
+ * Establishes the connection with compositor side through IPDL
+ */
+ virtual bool Connect(ImageContainer* aImageContainer = nullptr);
+
+ void Destroy();
+
+ bool IsConnected() const;
+
+ PCompositableChild* GetIPDLActor() const;
+
+ CompositableForwarder* GetForwarder() const
+ {
+ return mForwarder;
+ }
+
+ /**
+ * This identifier is what lets us attach async compositables with a shadow
+ * layer. It is not used if the compositable is used with the regular shadow
+ * layer forwarder.
+ *
+ * If this returns zero, it means the compositable is not async (it is used
+ * on the main thread).
+ */
+ uint64_t GetAsyncID() const;
+
+ /**
+ * Tells the Compositor to create a TextureHost for this TextureClient.
+ */
+ virtual bool AddTextureClient(TextureClient* aClient);
+
+ /**
+ * A hook for the when the Compositable is detached from it's layer.
+ */
+ virtual void OnDetach() {}
+
+ /**
+ * Clear any resources that are not immediately necessary. This may be called
+ * in low-memory conditions.
+ */
+ virtual void ClearCachedResources();
+
+ /**
+ * Shrink memory usage.
+ * Called when "memory-pressure" is observed.
+ */
+ virtual void HandleMemoryPressure();
+
+ /**
+ * Should be called when deataching a TextureClient from a Compositable, because
+ * some platforms need to do some extra book keeping when this happens (for
+ * example to properly keep track of fences on Gonk).
+ *
+ * See AutoRemoveTexture to automatically invoke this at the end of a scope.
+ */
+ virtual void RemoveTexture(TextureClient* aTexture);
+
+ static RefPtr<CompositableClient> FromIPDLActor(PCompositableChild* aActor);
+
+ void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
+
+ TextureFlags GetTextureFlags() const { return mTextureFlags; }
+
+ TextureClientRecycleAllocator* GetTextureClientRecycler();
+
+ bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
+
+ static void DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress);
+protected:
+ RefPtr<CompositableChild> mCompositableChild;
+ RefPtr<CompositableForwarder> mForwarder;
+ // Some layers may want to enforce some flags to all their textures
+ // (like disallowing tiling)
+ TextureFlags mTextureFlags;
+ RefPtr<TextureClientRecycleAllocator> mTextureClientRecycler;
+
+ friend class CompositableChild;
+};
+
+/**
+ * Helper to call RemoveTexture at the end of a scope.
+ */
+struct AutoRemoveTexture
+{
+ explicit AutoRemoveTexture(CompositableClient* aCompositable,
+ TextureClient* aTexture = nullptr)
+ : mTexture(aTexture)
+ , mCompositable(aCompositable)
+ {}
+
+ ~AutoRemoveTexture();
+
+ RefPtr<TextureClient> mTexture;
+private:
+ CompositableClient* mCompositable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp
new file mode 100644
index 000000000..3373230a9
--- /dev/null
+++ b/gfx/layers/client/ContentClient.cpp
@@ -0,0 +1,668 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentClient.h"
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxUtils.h" // for gfxUtils
+#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget, Factory
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "mozilla/layers/LayersTypes.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLayoutUtils.h"
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+#include "ReadbackLayer.h"
+
+#include <vector>
+
+using namespace std;
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
+{
+ TextureFlags result = TextureFlags::NO_FLAGS;
+
+ if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
+ result |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ return result;
+}
+
+/* static */ already_AddRefed<ContentClient>
+ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
+{
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (backend != LayersBackend::LAYERS_OPENGL &&
+ backend != LayersBackend::LAYERS_D3D9 &&
+ backend != LayersBackend::LAYERS_D3D11 &&
+ backend != LayersBackend::LAYERS_BASIC) {
+ return nullptr;
+ }
+
+ bool useDoubleBuffering = false;
+
+#ifdef XP_WIN
+ if (backend == LayersBackend::LAYERS_D3D11) {
+ useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
+ } else
+#endif
+#ifdef MOZ_WIDGET_GTK
+ // We can't use double buffering when using image content with
+ // Xrender support on Linux, as ContentHostDoubleBuffered is not
+ // suited for direct uploads to the server.
+ if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
+ !gfxVars::UseXRender())
+#endif
+ {
+ useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() &&
+ backend != LayersBackend::LAYERS_D3D9) ||
+ backend == LayersBackend::LAYERS_BASIC;
+ }
+
+ if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
+ return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
+ }
+ return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
+}
+
+void
+ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+}
+
+void
+ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+// We pass a null pointer for the ContentClient Forwarder argument, which means
+// this client will not have a ContentHost on the other side.
+ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
+ : ContentClient(nullptr)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mBackend(aBackend)
+{}
+
+void
+ContentClientBasic::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
+ }
+
+ IntSize size(aRect.width, aRect.height);
+#ifdef XP_WIN
+ if (mBackend == BackendType::CAIRO &&
+ (aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) {
+ RefPtr<gfxASurface> surf =
+ new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 :
+ gfxImageFormat::A8R8G8B8_UINT32);
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
+
+ if (*aBlackDT) {
+ return;
+ }
+ }
+#endif
+
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
+ mBackend, size,
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+}
+
+void
+ContentClientRemoteBuffer::DestroyBuffers()
+{
+ if (!mTextureClient) {
+ return;
+ }
+
+ mOldTextures.AppendElement(mTextureClient);
+ mTextureClient = nullptr;
+ if (mTextureClientOnWhite) {
+ mOldTextures.AppendElement(mTextureClientOnWhite);
+ mTextureClientOnWhite = nullptr;
+ }
+
+ DestroyFrontBuffer();
+}
+
+class RemoteBufferReadbackProcessor : public TextureReadbackSink
+{
+public:
+ RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
+ const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
+ : mReadbackUpdates(*aReadbackUpdates)
+ , mBufferRect(aBufferRect)
+ , mBufferRotation(aBufferRotation)
+ {
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ mLayerRefs.push_back(mReadbackUpdates[i].mLayer);
+ }
+ }
+
+ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface)
+ {
+ SourceRotatedBuffer rotBuffer(aSourceSurface, nullptr, mBufferRect, mBufferRotation);
+
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = mReadbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+
+ ReadbackSink* sink = update.mLayer->GetSink();
+
+ if (!sink) {
+ continue;
+ }
+
+ if (!aSourceSurface) {
+ sink->SetUnknown(update.mSequenceCounter);
+ continue;
+ }
+
+ RefPtr<DrawTarget> dt =
+ sink->BeginUpdate(update.mUpdateRect + offset, update.mSequenceCounter);
+ if (!dt) {
+ continue;
+ }
+
+ dt->SetTransform(Matrix::Translation(offset.x, offset.y));
+
+ rotBuffer.DrawBufferWithRotation(dt, RotatedBuffer::BUFFER_BLACK);
+
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+
+private:
+ nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
+ // This array is used to keep the layers alive until the callback.
+ vector<RefPtr<Layer>> mLayerRefs;
+
+ IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+};
+
+void
+ContentClientRemoteBuffer::BeginPaint()
+{
+ EnsureBackBufferIfFrontBuffer();
+
+ // XXX: So we might not have a TextureClient yet.. because it will
+ // only be created by CreateBuffer.. which will deliver a locked surface!.
+ if (mTextureClient) {
+ SetBufferProvider(mTextureClient);
+ }
+ if (mTextureClientOnWhite) {
+ SetBufferProviderOnWhite(mTextureClientOnWhite);
+ }
+}
+
+void
+ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+ MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
+
+ // XXX: We might still not have a texture client if PaintThebes
+ // decided we didn't need one yet because the region to draw was empty.
+ SetBufferProvider(nullptr);
+ SetBufferProviderOnWhite(nullptr);
+ for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
+ if (mOldTextures[i]->IsLocked()) {
+ mOldTextures[i]->Unlock();
+ }
+ }
+ mOldTextures.Clear();
+
+ if (mTextureClient && mTextureClient->IsLocked()) {
+ if (aReadbackUpdates->Length() > 0) {
+ RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
+
+ mTextureClient->SetReadbackSink(readbackSink);
+ }
+
+ mTextureClient->Unlock();
+ mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
+ }
+ if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
+ mTextureClientOnWhite->Unlock();
+ mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject());
+ }
+
+ ContentClientRemote::EndPaint(aReadbackUpdates);
+}
+
+void
+ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
+ const IntRect& aRect,
+ uint32_t aFlags)
+{
+ // If we hit this assertion, then it might be due to an empty transaction
+ // followed by a real transaction. Our buffers should be created (but not
+ // painted in the empty transaction) and then painted (but not created) in the
+ // real transaction. That is kind of fragile, and this assert will catch
+ // circumstances where we screw that up, e.g., by unnecessarily recreating our
+ // buffers.
+ MOZ_ASSERT(!mIsNewBuffer,
+ "Bad! Did we create a buffer twice without painting?");
+
+ mIsNewBuffer = true;
+
+ DestroyBuffers();
+
+ mSurfaceFormat = aFormat;
+ mSize = IntSize(aRect.width, aRect.height);
+ mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
+
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ mTextureFlags |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ CreateBackBuffer(mBufferRect);
+}
+
+void
+ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
+{
+ // gfx::BackendType::NONE means fallback to the content backend
+ TextureAllocationFlags textureAllocFlags
+ = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
+
+ mTextureClient = CreateTextureClientForDrawing(
+ mSurfaceFormat, mSize, BackendSelector::Content,
+ mTextureFlags | ExtraTextureFlags(),
+ textureAllocFlags
+ );
+ if (!mTextureClient || !AddTextureClient(mTextureClient)) {
+ AbortTextureClientCreation();
+ return;
+ }
+
+ if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
+ mTextureClientOnWhite = mTextureClient->CreateSimilar(
+ mForwarder->GetCompositorBackendType(),
+ mTextureFlags | ExtraTextureFlags(),
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
+ );
+ if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
+ AbortTextureClientCreation();
+ return;
+ }
+ }
+}
+
+void
+ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
+ if (!mTextureClient) {
+ return;
+ }
+
+ // We just created the textures and we are about to get their draw targets
+ // so we have to lock them here.
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the TextureClient");
+
+ *aBlackDT = mTextureClient->BorrowDrawTarget();
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
+
+ *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
+ }
+}
+
+nsIntRegion
+ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion;
+ if (mIsNewBuffer || aDidSelfCopy) {
+ // A buffer reallocation clears both buffers. The front buffer has all the
+ // content by now, but the back buffer is still clear. Here, in effect, we
+ // are saying to copy all of the pixels of the front buffer to the back.
+ // Also when we self-copied in the buffer, the buffer space
+ // changes and some changed buffer content isn't reflected in the
+ // draw or invalidate region (on purpose!). When this happens, we
+ // need to read back the entire buffer too.
+ updatedRegion = aVisibleRegion.GetBounds();
+ mIsNewBuffer = false;
+ } else {
+ updatedRegion = aRegionToDraw;
+ }
+
+ NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
+ "Update outside of buffer rect!");
+ MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
+
+ return updatedRegion;
+}
+
+void
+ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
+ aVisibleRegion,
+ aDidSelfCopy);
+
+ MOZ_ASSERT(mTextureClient);
+ if (mTextureClientOnWhite) {
+ mForwarder->UseComponentAlphaTextures(this, mTextureClient,
+ mTextureClientOnWhite);
+ } else {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mTextureClient;
+ IntSize size = mTextureClient->GetSize();
+ t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
+ GetForwarder()->UseTextures(this, textures);
+ }
+ mForwarder->UpdateTextureRegion(this,
+ ThebesBufferData(BufferRect(),
+ BufferRotation()),
+ updatedRegion);
+}
+
+void
+ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontAndBackBufferDiffer = true;
+}
+
+void
+ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml, TextureDumpMode aCompress)
+{
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(aStream, mTextureClient, aCompress);
+}
+
+void
+ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml, TextureDumpMode aCompress)
+{
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(aStream, mFrontClient, aCompress);
+}
+
+void
+ContentClientDoubleBuffered::DestroyFrontBuffer()
+{
+ if (mFrontClient) {
+ mOldTextures.AppendElement(mFrontClient);
+ mFrontClient = nullptr;
+ }
+
+ if (mFrontClientOnWhite) {
+ mOldTextures.AppendElement(mFrontClientOnWhite);
+ mFrontClientOnWhite = nullptr;
+ }
+}
+
+void
+ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
+}
+
+void
+ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontUpdatedRegion = aFrontUpdatedRegion;
+
+ RefPtr<TextureClient> oldBack = mTextureClient;
+ mTextureClient = mFrontClient;
+ mFrontClient = oldBack;
+
+ oldBack = mTextureClientOnWhite;
+ mTextureClientOnWhite = mFrontClientOnWhite;
+ mFrontClientOnWhite = oldBack;
+
+ IntRect oldBufferRect = mBufferRect;
+ mBufferRect = mFrontBufferRect;
+ mFrontBufferRect = oldBufferRect;
+
+ nsIntPoint oldBufferRotation = mBufferRotation;
+ mBufferRotation = mFrontBufferRotation;
+ mFrontBufferRotation = oldBufferRotation;
+
+ MOZ_ASSERT(mFrontClient);
+
+ ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion);
+}
+
+void
+ContentClientDoubleBuffered::BeginPaint()
+{
+ ContentClientRemoteBuffer::BeginPaint();
+
+ mIsNewBuffer = false;
+
+ if (!mFrontAndBackBufferDiffer) {
+ return;
+ }
+
+ if (mDidSelfCopy) {
+ // We can't easily draw our front buffer into us, since we're going to be
+ // copying stuff around anyway it's easiest if we just move our situation
+ // to non-rotated while we're at it. If this situation occurs we'll have
+ // hit a self-copy path in PaintThebes before as well anyway.
+ mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
+ mBufferRotation = nsIntPoint();
+ return;
+ }
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+}
+
+// Sync front/back buffers content
+// After executing, the new back buffer has the same (interesting) pixels as
+// the new front buffer, and mValidRegion et al. are correct wrt the new
+// back buffer (i.e. as they were for the old back buffer)
+void
+ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+
+ if (!mFrontAndBackBufferDiffer) {
+ MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
+ return;
+ }
+ MOZ_ASSERT(mFrontClient);
+ if (!mFrontClient) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
+ this,
+ mFrontUpdatedRegion.GetBounds().x,
+ mFrontUpdatedRegion.GetBounds().y,
+ mFrontUpdatedRegion.GetBounds().width,
+ mFrontUpdatedRegion.GetBounds().height));
+
+ mFrontAndBackBufferDiffer = false;
+
+ nsIntRegion updateRegion = mFrontUpdatedRegion;
+ if (mDidSelfCopy) {
+ mDidSelfCopy = false;
+ updateRegion = mBufferRect;
+ }
+
+ // No point in sync'ing what we are going to draw over anyway. And if there is
+ // nothing to sync at all, there is nothing to do and we can go home early.
+ updateRegion.Sub(updateRegion, aRegionToDraw);
+ if (updateRegion.IsEmpty()) {
+ return;
+ }
+
+ // We need to ensure that we lock these two buffers in the same
+ // order as the compositor to prevent deadlocks.
+ TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
+ if (!frontLock.Succeeded()) {
+ return;
+ }
+ Maybe<TextureClientAutoLock> frontOnWhiteLock;
+ if (mFrontClientOnWhite) {
+ frontOnWhiteLock.emplace(mFrontClientOnWhite, OpenMode::OPEN_READ_ONLY);
+ if (!frontOnWhiteLock->Succeeded()) {
+ return;
+ }
+ }
+
+ // Restrict the DrawTargets and frontBuffer to a scope to make
+ // sure there is no more external references to the DrawTargets
+ // when we Unlock the TextureClients.
+ gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget();
+ gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr;
+ if (dt && dt->IsValid()) {
+ RefPtr<SourceSurface> surf = dt->Snapshot();
+ RefPtr<SourceSurface> surfOnWhite = dtw ? dtw->Snapshot() : nullptr;
+ SourceRotatedBuffer frontBuffer(surf,
+ surfOnWhite,
+ mFrontBufferRect,
+ mFrontBufferRotation);
+ UpdateDestinationFrom(frontBuffer, updateRegion);
+ } else {
+ // We know this can happen, but we want to track it somewhat, in case it leads
+ // to other problems.
+ gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw);
+ }
+}
+
+void
+ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
+{
+ if (!mTextureClient && mFrontClient) {
+ CreateBackBuffer(mFrontBufferRect);
+
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+ }
+}
+
+void
+ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion)
+{
+ DrawIterator iter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+
+ if (aSource.HaveBufferOnWhite()) {
+ MOZ_ASSERT(HaveBufferOnWhite());
+ DrawIterator whiteIter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+ }
+}
+
+void
+ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ContentClient.h b/gfx/layers/client/ContentClient.h
new file mode 100644
index 000000000..d26f01464
--- /dev/null
+++ b/gfx/layers/client/ContentClient.h
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_CONTENTCLIENT_H
+#define MOZILLA_GFX_CONTENTCLIENT_H
+
+#include <stdint.h> // for uint32_t
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "gfxTypes.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "ReadbackProcessor.h" // For ReadbackProcessor::Update
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class PaintedLayer;
+
+/**
+ * A compositable client for PaintedLayers. These are different to Image/Canvas
+ * clients due to sending a valid region across IPC and because we do a lot more
+ * optimisation work, encapsualted in RotatedContentBuffers.
+ *
+ * We use content clients for OMTC and non-OMTC, basic rendering so that
+ * BasicPaintedLayer has only one interface to deal with. We support single and
+ * double buffered flavours. For tiled layers, we do not use a ContentClient
+ * although we do have a ContentHost, and we do use texture clients and texture
+ * hosts.
+ *
+ * The interface presented by ContentClient is used by the BasicPaintedLayer
+ * methods - PaintThebes, which is the same for MT and OMTC, and PaintBuffer
+ * which is different (the OMTC one does a little more). The 'buffer' in the
+ * names of a lot of these method is actually the TextureClient. But, 'buffer'
+ * for the RotatedContentBuffer (as in SetBuffer) means a gfxSurface. See the
+ * comments for SetBuffer and SetBufferProvider in RotatedContentBuffer. To keep
+ * these mapped buffers alive, we store a pointer in mOldTextures if the
+ * RotatedContentBuffer's surface is not the one from our texture client, once we
+ * are done painting we unmap the surface/texture client and don't need to keep
+ * it alive anymore, so we clear mOldTextures.
+ *
+ * The sequence for painting is: call BeginPaint on the content client;
+ * call BeginPaintBuffer on the content client. That will initialise the buffer
+ * for painting, by calling RotatedContentBuffer::BeginPaint (usually) which
+ * will call back to ContentClient::FinalizeFrame to finalize update of the
+ * buffers before drawing (i.e., it finalizes the previous frame). Then call
+ * BorrowDrawTargetForPainting to get a DrawTarget to paint into. Then paint.
+ * Then return that DrawTarget using ReturnDrawTarget.
+ * Call EndPaint on the content client;
+ *
+ * SwapBuffers is called in response to the transaction reply from the compositor.
+ */
+class ContentClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new content client. If necessary, a
+ * message will be sent to the compositor to create a corresponding content
+ * host.
+ */
+ static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
+
+ explicit ContentClient(CompositableForwarder* aForwarder)
+ : CompositableClient(aForwarder)
+ {}
+ virtual ~ContentClient()
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Clear() = 0;
+ virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) = 0;
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
+
+ // Called as part of the layers transation reply. Conveys data about our
+ // buffer(s) from the compositor. If appropriate we should swap references
+ // to our buffers.
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
+
+ // call before and after painting into this content client
+ virtual void BeginPaint() {}
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
+};
+
+/**
+ * A ContentClient for use with OMTC.
+ */
+class ContentClientRemote : public ContentClient
+{
+public:
+ explicit ContentClientRemote(CompositableForwarder* aForwarder)
+ : ContentClient(aForwarder)
+ {}
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) = 0;
+};
+
+// thin wrapper around RotatedContentBuffer, for on-mtc
+class ContentClientBasic final : public ContentClient
+ , protected RotatedContentBuffer
+{
+public:
+ explicit ContentClientBasic(gfx::BackendType aBackend);
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override { RotatedContentBuffer::Clear(); }
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ void DrawTo(PaintedLayer* aLayer,
+ gfx::DrawTarget* aTarget,
+ float aOpacity,
+ gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform)
+ {
+ RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp,
+ aMask, aMaskTransform);
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ MOZ_CRASH("GFX: Should not be called on non-remote ContentClient");
+ }
+
+private:
+ gfx::BackendType mBackend;
+};
+
+/**
+ * A ContentClientRemote backed by a RotatedContentBuffer.
+ *
+ * When using a ContentClientRemote, SurfaceDescriptors are created on
+ * the rendering side and destroyed on the compositing side. They are only
+ * passed from one side to the other when the TextureClient/Hosts are created.
+ * *Ownership* of the SurfaceDescriptor moves from the rendering side to the
+ * compositing side with the create message (send from CreateBuffer) which
+ * tells the compositor that TextureClients have been created and that the
+ * compositor should assign the corresponding TextureHosts to our corresponding
+ * ContentHost.
+ *
+ * If the size or type of our buffer(s) change(s), then we simply destroy and
+ * create them.
+ */
+// Version using new texture clients
+class ContentClientRemoteBuffer : public ContentClientRemote
+ , protected RotatedContentBuffer
+{
+ using RotatedContentBuffer::BufferRect;
+ using RotatedContentBuffer::BufferRotation;
+public:
+ explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder)
+ : ContentClientRemote(aForwarder)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mIsNewBuffer(false)
+ , mFrontAndBackBufferDiffer(false)
+ , mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8)
+ {}
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override
+ {
+ RotatedContentBuffer::Clear();
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ /**
+ * Begin/End Paint map a gfxASurface from the texture client
+ * into the buffer of RotatedBuffer. The surface is only
+ * valid when the texture client is locked, so is mapped out
+ * of RotatedContentBuffer when we are done painting.
+ * None of the underlying buffer attributes (rect, rotation)
+ * are affected by mapping/unmapping.
+ */
+ virtual void BeginPaint() override;
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ // Expose these protected methods from the superclass.
+ virtual const gfx::IntRect& BufferRect() const
+ {
+ return RotatedContentBuffer::BufferRect();
+ }
+ virtual const nsIntPoint& BufferRotation() const
+ {
+ return RotatedContentBuffer::BufferRotation();
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureFlags ExtraTextureFlags() const
+ {
+ return TextureFlags::NO_FLAGS;
+ }
+
+protected:
+ void DestroyBuffers();
+
+ virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy);
+
+ void BuildTextureClients(gfx::SurfaceFormat aFormat,
+ const gfx::IntRect& aRect,
+ uint32_t aFlags);
+
+ void CreateBackBuffer(const gfx::IntRect& aBufferRect);
+
+ // Ensure we have a valid back buffer if we have a valid front buffer (i.e.
+ // if a backbuffer has been created.)
+ virtual void EnsureBackBufferIfFrontBuffer() {}
+
+ // Create the front buffer for the ContentClient/Host pair if necessary
+ // and notify the compositor that we have created the buffer(s).
+ virtual void DestroyFrontBuffer() {}
+
+ virtual void AbortTextureClientCreation()
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mIsNewBuffer = false;
+ }
+
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<TextureClient> mTextureClientOnWhite;
+ // keep a record of texture clients we have created and need to keep around
+ // (for RotatedBuffer to access), then unlock and remove them when we are done
+ // painting.
+ nsTArray<RefPtr<TextureClient> > mOldTextures;
+
+ bool mIsNewBuffer;
+ bool mFrontAndBackBufferDiffer;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mSurfaceFormat;
+};
+
+/**
+ * A double buffered ContentClient. mTextureClient is the back buffer, which
+ * we draw into. mFrontClient is the front buffer which we may read from, but
+ * not write to, when the compositor does not have the 'soft' lock. We can write
+ * into mTextureClient at any time.
+ *
+ * The ContentHost keeps a reference to both corresponding texture hosts, in
+ * response to our UpdateTextureRegion message, the compositor swaps its
+ * references. In response to the compositor's reply we swap our references
+ * (in SwapBuffers).
+ */
+class ContentClientDoubleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientDoubleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {}
+
+ virtual ~ContentClientDoubleBuffered() {}
+
+ virtual void Clear() override
+ {
+ ContentClientRemoteBuffer::Clear();
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ virtual void BeginPaint() override;
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual void EnsureBackBufferIfFrontBuffer() override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+protected:
+ virtual void DestroyFrontBuffer() override;
+
+private:
+ void UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion);
+
+ virtual void AbortTextureClientCreation() override
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ RefPtr<TextureClient> mFrontClient;
+ RefPtr<TextureClient> mFrontClientOnWhite;
+ nsIntRegion mFrontUpdatedRegion;
+ gfx::IntRect mFrontBufferRect;
+ nsIntPoint mFrontBufferRotation;
+};
+
+/**
+ * A single buffered ContentClient. We have a single TextureClient/Host
+ * which we update and then send a message to the compositor that we are
+ * done updating. It is not safe for the compositor to use the corresponding
+ * TextureHost's memory directly, it must upload it to video memory of some
+ * kind. We are free to modify the TextureClient once we receive reply from
+ * the compositor.
+ */
+class ContentClientSingleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientSingleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {
+ }
+ virtual ~ContentClientSingleBuffered() {}
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_SINGLE, mTextureFlags | ExtraTextureFlags());
+ }
+
+ virtual TextureFlags ExtraTextureFlags() const override
+ {
+ return TextureFlags::IMMEDIATE_UPLOAD;
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/GPUVideoTextureClient.cpp b/gfx/layers/client/GPUVideoTextureClient.cpp
new file mode 100644
index 000000000..10d2bbf38
--- /dev/null
+++ b/gfx/layers/client/GPUVideoTextureClient.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUVideoTextureClient.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+GPUVideoTextureData::GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize)
+ : mManager(aManager)
+ , mSD(aSD)
+ , mSize(aSize)
+{}
+
+GPUVideoTextureData::~GPUVideoTextureData()
+{
+}
+
+bool
+GPUVideoTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = mSD;
+ return true;
+}
+
+void
+GPUVideoTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ // TODO: We should probably try do better for this.
+ // layers::Image doesn't expose a format, so it's hard
+ // to figure out in VideoDecoderParent.
+ aInfo.format = SurfaceFormat::B8G8R8X8;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<SourceSurface>
+GPUVideoTextureData::GetAsSourceSurface()
+{
+ return mManager->Readback(mSD);
+}
+
+void
+GPUVideoTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ mManager->DeallocateSurfaceDescriptorGPUVideo(mSD);
+ mSD = SurfaceDescriptorGPUVideo();
+}
+
+void
+GPUVideoTextureData::Forget(LayersIPCChannel* aAllocator)
+{
+ // We always need to manually deallocate on the client side.
+ // Ideally we'd set up our TextureClient with the DEALLOCATE_CLIENT
+ // flag, but that forces texture destruction to be synchronous.
+ // Instead let's just deallocate from here as well.
+ Deallocate(aAllocator);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/GPUVideoTextureClient.h b/gfx/layers/client/GPUVideoTextureClient.h
new file mode 100644
index 000000000..a445e2a7d
--- /dev/null
+++ b/gfx/layers/client/GPUVideoTextureClient.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+#define MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+}
+namespace dom {
+class VideoDecoderManagerChild;
+}
+namespace layers {
+
+class GPUVideoTextureData : public TextureData
+{
+public:
+ GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize);
+ ~GPUVideoTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override { return true; };
+
+ virtual void Unlock() override {};
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ virtual void Forget(LayersIPCChannel* aAllocator) override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() override
+ {
+ return this;
+ }
+
+protected:
+ RefPtr<dom::VideoDecoderManagerChild> mManager;
+ SurfaceDescriptorGPUVideo mSD;
+ gfx::IntSize mSize;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp
new file mode 100644
index 000000000..1ccb69502
--- /dev/null
+++ b/gfx/layers/client/ImageClient.cpp
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageClient.h"
+
+#include <stdint.h> // for uint32_t
+
+#include "ClientLayerManager.h" // for ClientLayer
+#include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc
+#include "GLImages.h" // for SurfaceTextureImage::Data, etc
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h" // for SurfaceTextureClient
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/* static */ already_AddRefed<ImageClient>
+ImageClient::CreateImageClient(CompositableType aCompositableHostType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ RefPtr<ImageClient> result = nullptr;
+ switch (aCompositableHostType) {
+ case CompositableType::IMAGE:
+ result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
+ break;
+ case CompositableType::IMAGE_BRIDGE:
+ result = new ImageClientBridge(aForwarder, aFlags);
+ break;
+ case CompositableType::UNKNOWN:
+ result = nullptr;
+ break;
+ default:
+ MOZ_CRASH("GFX: unhandled program type image");
+ }
+
+ NS_ASSERTION(result, "Failed to create ImageClient");
+
+ return result.forget();
+}
+
+void
+ImageClient::RemoveTexture(TextureClient* aTexture)
+{
+ GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
+}
+
+ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType)
+ : ImageClient(aFwd, aFlags, aType)
+{
+}
+
+TextureInfo ImageClientSingle::GetTextureInfo() const
+{
+ return TextureInfo(CompositableType::IMAGE);
+}
+
+void
+ImageClientSingle::FlushAllImages()
+{
+ MOZ_ASSERT(GetForwarder()->GetTextureForwarder()->UsesImageBridge());
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.Clear();
+}
+
+/* static */ already_AddRefed<TextureClient>
+ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder)
+{
+ RefPtr<TextureClient> texture;
+ if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+ PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
+ const PlanarYCbCrData* data = ycbcr->GetData();
+ if (!data) {
+ return nullptr;
+ }
+ texture = TextureClient::CreateForYCbCr(aForwarder,
+ data->mYSize, data->mCbCrSize, data->mStereoMode,
+ data->mYUVColorSpace,
+ TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ bool status = UpdateYCbCrTextureClient(texture, *data);
+ MOZ_ASSERT(status);
+ if (!status) {
+ return nullptr;
+ }
+ } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
+ aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ gfx::IntSize size = aImage->GetSize();
+
+ if (aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ EGLImageImage* typedImage = aImage->AsEGLImageImage();
+ texture = EGLImageTextureData::CreateTextureClient(
+ typedImage, size, aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
+#ifdef MOZ_WIDGET_ANDROID
+ } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE) {
+ SurfaceTextureImage* typedImage = aImage->AsSurfaceTextureImage();
+ texture = AndroidSurfaceTextureData::CreateTextureClient(
+ typedImage->GetSurfaceTexture(), size, typedImage->GetOriginPos(),
+ aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
+#endif
+ } else {
+ MOZ_ASSERT(false, "Bad ImageFormat.");
+ }
+ } else {
+ RefPtr<gfx::SourceSurface> surface = aImage->GetAsSourceSurface();
+ MOZ_ASSERT(surface);
+ texture = TextureClient::CreateForDrawing(aForwarder, surface->GetFormat(), aImage->GetSize(),
+ BackendSelector::Content, TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(texture->CanExposeDrawTarget());
+
+ if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+ return nullptr;
+ }
+
+ {
+ // We must not keep a reference to the DrawTarget after it has been unlocked.
+ DrawTarget* dt = texture->BorrowDrawTarget();
+ if (!dt) {
+ gfxWarning() << "ImageClientSingle::UpdateImage failed in BorrowDrawTarget";
+ return nullptr;
+ }
+ MOZ_ASSERT(surface.get());
+ dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
+ }
+
+ texture->Unlock();
+ }
+ return texture.forget();
+}
+
+bool
+ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ AutoTArray<ImageContainer::OwningImage,4> images;
+ uint32_t generationCounter;
+ aContainer->GetCurrentImages(&images, &generationCounter);
+
+ if (mLastUpdateGenerationCounter == generationCounter) {
+ return true;
+ }
+ mLastUpdateGenerationCounter = generationCounter;
+
+ for (int32_t i = images.Length() - 1; i >= 0; --i) {
+ if (!images[i].mImage->IsValid()) {
+ // Don't try to update to an invalid image.
+ images.RemoveElementAt(i);
+ }
+ }
+ if (images.IsEmpty()) {
+ // This can happen if a ClearAllImages raced with SetCurrentImages from
+ // another thread and ClearImagesFromImageBridge ran after the
+ // SetCurrentImages call but before UpdateImageClientNow.
+ // This can also happen if all images in the list are invalid.
+ // We return true because the caller would attempt to recreate the
+ // ImageClient otherwise, and that isn't going to help.
+ return true;
+ }
+
+ nsTArray<Buffer> newBuffers;
+ AutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
+
+ for (auto& img : images) {
+ Image* image = img.mImage;
+
+ RefPtr<TextureClient> texture = image->GetTextureClient(GetForwarder());
+ const bool hasTextureClient = !!texture;
+
+ for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
+ if (mBuffers[i].mImageSerial == image->GetSerial()) {
+ if (hasTextureClient) {
+ MOZ_ASSERT(image->GetTextureClient(GetForwarder()) == mBuffers[i].mTextureClient);
+ } else {
+ texture = mBuffers[i].mTextureClient;
+ }
+ // Remove this element from mBuffers so mBuffers only contains
+ // images that aren't present in 'images'
+ mBuffers.RemoveElementAt(i);
+ }
+ }
+
+ if (!texture) {
+ // Slow path, we should not be hitting it very often and if we do it means
+ // we are using an Image class that is not backed by textureClient and we
+ // should fix it.
+ texture = CreateTextureClientForImage(image, GetForwarder());
+ }
+ if (!texture || !AddTextureClient(texture)) {
+ return false;
+ }
+
+
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = texture;
+ t->mTimeStamp = img.mTimeStamp;
+ t->mPictureRect = image->GetPictureRect();
+ t->mFrameID = img.mFrameID;
+ t->mProducerID = img.mProducerID;
+
+ Buffer* newBuf = newBuffers.AppendElement();
+ newBuf->mImageSerial = image->GetSerial();
+ newBuf->mTextureClient = texture;
+
+ texture->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ GetForwarder()->UseTextures(this, textures);
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.SwapElements(newBuffers);
+
+ return true;
+}
+
+bool
+ImageClientSingle::AddTextureClient(TextureClient* aTexture)
+{
+ MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
+ return CompositableClient::AddTextureClient(aTexture);
+}
+
+void
+ImageClientSingle::OnDetach()
+{
+ mBuffers.Clear();
+}
+
+ImageClient::ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType)
+: CompositableClient(aFwd, aFlags)
+, mLayer(nullptr)
+, mType(aType)
+, mLastUpdateGenerationCounter(0)
+{}
+
+ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags)
+: ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
+, mAsyncContainerID(0)
+{
+}
+
+bool
+ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ if (!GetForwarder() || !mLayer) {
+ return false;
+ }
+ if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
+ return true;
+ }
+ mAsyncContainerID = aContainer->GetAsyncContainerID();
+ static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ImageClient.h b/gfx/layers/client/ImageClient.h
new file mode 100644
index 000000000..4c6e26400
--- /dev/null
+++ b/gfx/layers/client/ImageClient.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGECLIENT_H
+#define MOZILLA_GFX_IMAGECLIENT_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayer;
+class CompositableForwarder;
+class Image;
+class ImageContainer;
+class ShadowableLayer;
+class ImageClientSingle;
+
+/**
+ * Image clients are used by basic image layers on the content thread, they
+ * always match with an ImageHost on the compositor thread. See
+ * CompositableClient.h for information on connecting clients to hosts.
+ */
+class ImageClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new image client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ static already_AddRefed<ImageClient> CreateImageClient(CompositableType aImageHostType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual ~ImageClient() {}
+
+ /**
+ * Update this ImageClient from aContainer in aLayer
+ * returns false if this is the wrong kind of ImageClient for aContainer.
+ * Note that returning true does not necessarily imply success
+ */
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
+
+ void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
+ ClientLayer* GetLayer() const { return mLayer; }
+
+ /**
+ * asynchronously remove all the textures used by the image client.
+ *
+ */
+ virtual void FlushAllImages() {}
+
+ virtual void RemoveTexture(TextureClient* aTexture) override;
+
+ virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
+
+ static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
+
+protected:
+ ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType);
+
+ ClientLayer* mLayer;
+ CompositableType mType;
+ uint32_t mLastUpdateGenerationCounter;
+};
+
+/**
+ * An image client which uses a single texture client.
+ */
+class ImageClientSingle : public ImageClient
+{
+public:
+ ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+
+ virtual void OnDetach() override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override;
+
+ virtual TextureInfo GetTextureInfo() const override;
+
+ virtual void FlushAllImages() override;
+
+ ImageClientSingle* AsImageClientSingle() override { return this; }
+
+protected:
+ struct Buffer {
+ RefPtr<TextureClient> mTextureClient;
+ int32_t mImageSerial;
+ };
+ nsTArray<Buffer> mBuffers;
+};
+
+/**
+ * Image class to be used for async image uploads using the image bridge
+ * protocol.
+ * We store the ImageBridge id in the TextureClientIdentifier.
+ */
+class ImageClientBridge : public ImageClient
+{
+public:
+ ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+ virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(mType);
+ }
+
+protected:
+ uint64_t mAsyncContainerID;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/SingleTiledContentClient.cpp b/gfx/layers/client/SingleTiledContentClient.cpp
new file mode 100644
index 000000000..bcc8691cf
--- /dev/null
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/SingleTiledContentClient.h"
+
+#include "ClientTiledPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+
+SingleTiledContentClient::SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Single")
+{
+ MOZ_COUNT_CTOR(SingleTiledContentClient);
+
+ mTiledBuffer = new ClientSingleTiledLayerBuffer(aPaintedLayer, *this, aManager);
+}
+
+void
+SingleTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer->DiscardBuffers();
+}
+
+void
+SingleTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ mForwarder->UseTiledLayerBuffer(this, mTiledBuffer->GetSurfaceDescriptorTiles());
+ mTiledBuffer->ClearPaintedRegion();
+}
+
+/* static */ bool
+SingleTiledContentClient::ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager)
+{
+ int32_t maxTextureSize = aManager->GetMaxTextureSize();
+ return aSize.width <= maxTextureSize && aSize.height <= maxTextureSize;
+}
+
+ClientSingleTiledLayerBuffer::ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mWasLastPaintProgressive(false)
+ , mFormat(gfx::SurfaceFormat::UNKNOWN)
+{
+}
+
+void
+ClientSingleTiledLayerBuffer::ReleaseTiles()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardBuffers();
+ }
+ mTile.SetTextureAllocator(nullptr);
+}
+
+void
+ClientSingleTiledLayerBuffer::DiscardBuffers()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardFrontBuffer();
+ mTile.DiscardBackBuffer();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientSingleTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ TileDescriptor tileDesc = mTile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ mTile.mUpdateRect = gfx::IntRect();
+
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTilingOrigin,
+ mSize,
+ 0, 0, 1, 1,
+ 1.0,
+ mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+already_AddRefed<TextureClient>
+ClientSingleTiledLayerBuffer::GetTextureClient()
+{
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN);
+ return mCompositableClient.CreateTextureClientForDrawing(
+ gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content,
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD);
+}
+
+void
+ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ mWasLastPaintProgressive = aIsProgressive;
+
+ // Compare layer valid region size to current backbuffer size, discard if not matching.
+ gfx::IntSize size = aNewValidRegion.GetBounds().Size();
+ gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
+ nsIntRegion paintRegion = aPaintRegion;
+
+ RefPtr<TextureClient> discardedFrontBuffer = nullptr;
+ RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
+ nsIntRegion discardedValidRegion;
+
+ if (mSize != size ||
+ mTilingOrigin != origin) {
+ discardedFrontBuffer = mTile.mFrontBuffer;
+ discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
+ discardedValidRegion = mValidRegion;
+
+ TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
+;
+ ResetPaintedAndValidState();
+ mSize = size;
+ mTilingOrigin = origin;
+ paintRegion = aNewValidRegion;
+ }
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);
+
+ if (mTile.IsPlaceholderTile()) {
+ mTile.SetTextureAllocator(this);
+ }
+
+ // The dirty region relative to the top-left of the tile.
+ nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ mTile.GetBackBuffer(mCompositableClient,
+ tileDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(mTilingOrigin);
+ extraPainted.And(extraPainted, aNewValidRegion);
+ mPaintedRegion.OrWith(paintRegion);
+ mPaintedRegion.OrWith(extraPainted);
+
+ if (!backBuffer) {
+ return;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<gfx::DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ }
+
+ if (mode != SurfaceMode::SURFACE_OPAQUE) {
+ for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect& rect = iter.Get();
+ if (dtOnWhite) {
+ dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
+ } else {
+ dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
+ }
+ }
+ }
+
+ // If the old frontbuffer was discarded then attempt to copy what we
+ // can from it to the new backbuffer.
+ if (discardedFrontBuffer) {
+ nsIntRegion copyableRegion;
+ copyableRegion.And(aNewValidRegion, discardedValidRegion);
+ copyableRegion.SubOut(aDirtyRegion);
+
+ if (!copyableRegion.IsEmpty()) {
+ TextureClientAutoLock frontLock(discardedFrontBuffer,
+ OpenMode::OPEN_READ);
+ if (frontLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+ discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
+ }
+ }
+
+ if (discardedFrontBufferOnWhite && backBufferOnWhite) {
+ TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
+ OpenMode::OPEN_READ);
+ if (frontOnWhiteLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+
+ discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
+ &rect, &dest);
+ }
+ }
+ }
+
+ TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
+
+ // We don't need to repaint valid content that was just copied.
+ paintRegion.SubOut(copyableRegion);
+ }
+ }
+
+ if (dtOnWhite) {
+ dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ dtOnWhite = nullptr;
+ }
+
+ {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
+ if (!ctx) {
+ gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+ return;
+ }
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
+
+ aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
+ }
+
+ // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
+ // now out of sync.
+ mTile.mInvalidFront.OrWith(tileDirtyRegion);
+
+ // The new buffer is now validated, remove the dirty region from it.
+ mTile.mInvalidBack.SubOut(tileDirtyRegion);
+
+ dt = nullptr;
+
+ mTile.Flip();
+ UnlockTile(mTile);
+
+ if (backBuffer->HasIntermediateBuffer()) {
+ // If our new buffer has an internal buffer, we don't want to keep another
+ // TextureClient around unnecessarily, so discard the back-buffer.
+ mTile.DiscardBackBuffer();
+ }
+
+ mValidRegion = aNewValidRegion;
+ mLastPaintSurfaceMode = mode;
+ mLastPaintContentType = content;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/SingleTiledContentClient.h b/gfx/layers/client/SingleTiledContentClient.h
new file mode 100644
index 000000000..e1706bd90
--- /dev/null
+++ b/gfx/layers/client/SingleTiledContentClient.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+
+#include "TiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientSingleTiledLayerBuffer
+ : public ClientTiledLayerBuffer
+ , public TextureClientAllocator
+{
+ virtual ~ClientSingleTiledLayerBuffer() {}
+public:
+ ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager);
+
+ // TextureClientAllocator
+ already_AddRefed<TextureClient> GetTextureClient() override;
+ void ReturnTextureClientDeferred(TextureClient* aClient) override {}
+ void ReportClientLost() override {}
+
+ // ClientTiledLayerBuffer
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ bool SupportsProgressiveUpdate() override { return false; }
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override
+ {
+ MOZ_ASSERT(false, "ProgressiveUpdate not supported!");
+ return false;
+ }
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTile.DiscardBuffers();
+ }
+
+ const nsIntRegion& GetValidRegion() override {
+ return mValidRegion;
+ }
+
+ bool IsLowPrecision() const override {
+ return false;
+ }
+
+ void ReleaseTiles();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void ClearPaintedRegion() {
+ mPaintedRegion.SetEmpty();
+ }
+
+private:
+ TileClient mTile;
+
+ nsIntRegion mPaintedRegion;
+ nsIntRegion mValidRegion;
+ bool mWasLastPaintProgressive;
+
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ gfx::IntSize mSize;
+ gfxImageFormat mFormat;
+};
+
+class SingleTiledContentClient : public TiledContentClient
+{
+public:
+ SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~SingleTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(SingleTiledContentClient);
+
+ mTiledBuffer->ReleaseTiles();
+ }
+
+public:
+ static bool ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager);
+
+ virtual void ClearCachedResources() override;
+
+ virtual void UpdatedBuffer(TiledBufferType aType) override;
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() override { return mTiledBuffer; }
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override { return nullptr; }
+
+private:
+ RefPtr<ClientSingleTiledLayerBuffer> mTiledBuffer;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp
new file mode 100644
index 000000000..7182731bd
--- /dev/null
+++ b/gfx/layers/client/TextureClient.cpp
@@ -0,0 +1,1707 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/TextureClient.h"
+#include <stdint.h> // for uint8_t, uint32_t, etc
+#include "Layers.h" // for Layer, etc
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/Mutex.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "ImageContainer.h" // for PlanarYCbCrData, etc
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // for CreateDataSourceSurfaceByCloning
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "LayersLogging.h" // for AppendToString
+#include "gfxUtils.h" // for gfxUtils::GetAsLZ4Base64Str
+#include "IPDLActor.h"
+#include "BufferTexture.h"
+#include "gfxPrefs.h"
+#include "mozilla/layers/ShadowLayers.h"
+
+#ifdef XP_WIN
+#include "DeviceManagerD3D9.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/TextureD3D9.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureDIB.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#endif
+#ifdef MOZ_X11
+#include "mozilla/layers/TextureClientX11.h"
+#ifdef GL_PROVIDER_GLX
+#include "GLXLibrary.h"
+#endif
+#endif
+
+#ifdef XP_MACOSX
+#include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
+#endif
+
+#if 0
+#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#define RECYCLE_LOG(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+struct TextureDeallocParams
+{
+ TextureData* data;
+ RefPtr<TextureChild> actor;
+ RefPtr<LayersIPCChannel> allocator;
+ bool clientDeallocation;
+ bool syncDeallocation;
+ bool workAroundSharedSurfaceOwnershipIssue;
+};
+
+void DeallocateTextureClient(TextureDeallocParams params);
+
+/**
+ * TextureChild is the content-side incarnation of the PTexture IPDL actor.
+ *
+ * TextureChild is used to synchronize a texture client and its corresponding
+ * TextureHost if needed (a TextureClient that is not shared with the compositor
+ * does not have a TextureChild)
+ *
+ * During the deallocation phase, a TextureChild may hold its recently destroyed
+ * TextureClient's data until the compositor side confirmed that it is safe to
+ * deallocte or recycle the it.
+ */
+class TextureChild final : PTextureChild
+{
+ ~TextureChild()
+ {
+ // We should have deallocated mTextureData in ActorDestroy
+ MOZ_ASSERT(!mTextureData);
+ MOZ_ASSERT_IF(!mOwnerCalledDestroy, !mTextureClient);
+ }
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
+
+ TextureChild()
+ : mCompositableForwarder(nullptr)
+ , mTextureForwarder(nullptr)
+ , mTextureClient(nullptr)
+ , mTextureData(nullptr)
+ , mDestroyed(false)
+ , mMainThreadOnly(false)
+ , mIPCOpen(false)
+ , mOwnsTextureData(false)
+ , mOwnerCalledDestroy(false)
+ {}
+
+ bool Recv__delete__() override { return true; }
+
+ LayersIPCChannel* GetAllocator() { return mTextureForwarder; }
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool IPCOpen() const { return mIPCOpen; }
+
+ void Lock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Enter(); } }
+
+ void Unlock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Leave(); } }
+
+private:
+
+ // AddIPDLReference and ReleaseIPDLReference are only to be called by CreateIPDLActor
+ // and DestroyIPDLActor, respectively. We intentionally make them private to prevent misuse.
+ // The purpose of these methods is to be aware of when the IPC system around this
+ // actor goes down: mIPCOpen is then set to false.
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+
+ /// The normal way to destroy the actor.
+ ///
+ /// This will asynchronously send a Destroy message to the parent actor, whom
+ /// will send the delete message.
+ void Destroy(const TextureDeallocParams& aParams);
+
+ /// The ugly and slow way to destroy the actor.
+ ///
+ /// This will block until the Parent actor has handled the Destroy message,
+ /// and then start the asynchronous handshake (and destruction will already
+ /// be done on the parent side, when the async part happens).
+ void DestroySynchronously(const TextureDeallocParams& aParams);
+
+ // This lock is used order to prevent several threads to access the
+ // TextureClient's data concurrently. In particular, it prevents shutdown
+ // code to destroy a texture while another thread is reading or writing into
+ // it.
+ // In most places, the lock is held in short and bounded scopes in which we
+ // don't block on any other resource. There are few exceptions to this, which
+ // are discussed below.
+ //
+ // The locking pattern of TextureClient may in some case upset deadlock detection
+ // tools such as TSan.
+ // Typically our tile rendering code will lock all of its tiles, render into them
+ // and unlock them all right after that, which looks something like:
+ //
+ // Lock tile A
+ // Lock tile B
+ // Lock tile C
+ // Apply drawing commands to tiles A, B and C
+ // Unlock tile A
+ // Unlock tile B
+ // Unlock tile C
+ //
+ // And later, we may end up rendering a tile buffer that has the same tiles,
+ // in a different order, for example:
+ //
+ // Lock tile B
+ // Lock tile A
+ // Lock tile D
+ // Apply drawing commands to tiles A, B and D
+ // Unlock tile B
+ // Unlock tile A
+ // Unlock tile D
+ //
+ // This is because textures being expensive to create, we recycle them as much
+ // as possible and they may reappear in the tile buffer in a different order.
+ //
+ // Unfortunately this is not very friendly to TSan's analysis, which will see
+ // that B was once locked while A was locked, and then A locked while B was
+ // locked. TSan identifies this as a potential dead-lock which would be the
+ // case if this kind of inconsistent and dependent locking order was happening
+ // concurrently.
+ // In the case of TextureClient, dependent locking only ever happens on the
+ // thread that draws into the texture (let's call it the producer thread). Other
+ // threads may call into a method that can lock the texture in a short and
+ // bounded scope inside of which it is not allowed to do anything that could
+ // cause the thread to block. A given texture can only have one producer thread.
+ //
+ // Another example of TSan-unfriendly locking pattern is when copying a texture
+ // into another, which also never happens outside of the producer thread.
+ // Copying A into B looks like this:
+ //
+ // Lock texture B
+ // Lock texture A
+ // Copy A into B
+ // Unlock A
+ // Unlock B
+ //
+ // In a given frame we may need to copy A into B and in another frame copy
+ // B into A. For example A and B can be the Front and Back buffers, alternating
+ // roles and the copy is needed to avoid the cost of re-drawing the valid
+ // region.
+ //
+ // The important rule is that all of the dependent locking must occur only
+ // in the texture's producer thread to avoid deadlocks.
+ mutable gfx::CriticalSection mLock;
+
+ RefPtr<CompositableForwarder> mCompositableForwarder;
+ RefPtr<TextureForwarder> mTextureForwarder;
+
+ TextureClient* mTextureClient;
+ TextureData* mTextureData;
+ Atomic<bool> mDestroyed;
+ bool mMainThreadOnly;
+ bool mIPCOpen;
+ bool mOwnsTextureData;
+ bool mOwnerCalledDestroy;
+
+ friend class TextureClient;
+ friend void DeallocateTextureClient(TextureDeallocParams params);
+};
+
+
+static void DestroyTextureData(TextureData* aTextureData, LayersIPCChannel* aAllocator,
+ bool aDeallocate, bool aMainThreadOnly)
+{
+ if (!aTextureData) {
+ return;
+ }
+
+ if (aMainThreadOnly && !NS_IsMainThread()) {
+ RefPtr<LayersIPCChannel> allocatorRef = aAllocator;
+ NS_DispatchToMainThread(NS_NewRunnableFunction([aTextureData, allocatorRef, aDeallocate]() -> void {
+ DestroyTextureData(aTextureData, allocatorRef, aDeallocate, true);
+ }));
+ return;
+ }
+
+ if (aDeallocate) {
+ aTextureData->Deallocate(aAllocator);
+ } else {
+ aTextureData->Forget(aAllocator);
+ }
+ delete aTextureData;
+}
+
+void
+TextureChild::ActorDestroy(ActorDestroyReason why)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+
+ if (mTextureData) {
+ DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly);
+ mTextureData = nullptr;
+ }
+}
+
+void
+TextureChild::Destroy(const TextureDeallocParams& aParams)
+{
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ // DestroyTextureData will be called by TextureChild::ActorDestroy
+ mTextureData = aParams.data;
+ mOwnsTextureData = aParams.clientDeallocation;
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, false))
+ {
+ this->SendDestroy();
+ }
+}
+
+void
+TextureChild::DestroySynchronously(const TextureDeallocParams& aParams)
+{
+ MOZ_PERFORMANCE_WARNING("gfx", "TextureClient/Host pair requires synchronous deallocation");
+
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ DestroyTextureData(
+ aParams.data,
+ aParams.allocator,
+ aParams.clientDeallocation,
+ mMainThreadOnly);
+
+ if (!IPCOpen()) {
+ return;
+ }
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, true))
+ {
+ this->SendDestroySync();
+ this->SendDestroy();
+ }
+}
+
+/* static */ Atomic<uint64_t> TextureClient::sSerialCounter(0);
+
+void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
+ ReentrantMonitor* aBarrier, bool* aDone)
+{
+ DeallocateTextureClient(params);
+ ReentrantMonitorAutoEnter autoMon(*aBarrier);
+ *aDone = true;
+ aBarrier->NotifyAll();
+}
+
+/// The logic for synchronizing a TextureClient's deallocation goes here.
+///
+/// This funciton takes care of dispatching work to the right thread using
+/// a synchronous proxy if needed, and handles client/host deallocation.
+void
+DeallocateTextureClient(TextureDeallocParams params)
+{
+ if (!params.actor && !params.data) {
+ // Nothing to do
+ return;
+ }
+
+ TextureChild* actor = params.actor;
+ MessageLoop* ipdlMsgLoop = nullptr;
+
+ if (params.allocator) {
+ ipdlMsgLoop = params.allocator->GetMessageLoop();
+ if (!ipdlMsgLoop) {
+ // An allocator with no message loop means we are too late in the shutdown
+ // sequence.
+ gfxCriticalError() << "Texture deallocated too late during shutdown";
+ return;
+ }
+ }
+
+ // First make sure that the work is happening on the IPDL thread.
+ if (ipdlMsgLoop && MessageLoop::current() != ipdlMsgLoop) {
+ if (params.syncDeallocation) {
+ bool done = false;
+ ReentrantMonitor barrier("DeallocateTextureClient");
+ ReentrantMonitorAutoEnter autoMon(barrier);
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClientSyncProxy,
+ params, &barrier, &done));
+ while (!done) {
+ barrier.Wait();
+ }
+ } else {
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClient,
+ params));
+ }
+ // The work has been forwarded to the IPDL thread, we are done.
+ return;
+ }
+
+ // Below this line, we are either in the IPDL thread or ther is no IPDL
+ // thread anymore.
+
+ if (!ipdlMsgLoop) {
+ // If we don't have a message loop we can't know for sure that we are in
+ // the IPDL thread and use the LayersIPCChannel.
+ // This should ideally not happen outside of gtest, but some shutdown raciness
+ // could put us in this situation.
+ params.allocator = nullptr;
+ }
+
+ if (!actor) {
+ // We don't have an IPDL actor, probably because we destroyed the TextureClient
+ // before sharing it with the compositor. It means the data cannot be owned by
+ // the TextureHost since we never created the TextureHost...
+ // ..except if the lovely mWorkaroundAnnoyingSharedSurfaceOwnershipIssues member
+ // is set to true. In this case we are in a special situation where this
+ // TextureClient is in wrapped into another TextureClient which assumes it owns
+ // our data. This is specific to the gralloc SharedSurface.
+ bool shouldDeallocate = !params.workAroundSharedSurfaceOwnershipIssue;
+ DestroyTextureData(params.data, params.allocator,
+ shouldDeallocate,
+ false); // main-thread deallocation
+ return;
+ }
+
+ if (params.syncDeallocation || !actor->IPCOpen()) {
+ actor->DestroySynchronously(params);
+ } else {
+ actor->Destroy(params);
+ }
+}
+
+void TextureClient::Destroy(bool aForceSync)
+{
+ if (mActor && !mIsLocked) {
+ mActor->Lock();
+ }
+
+ mBorrowedDrawTarget = nullptr;
+ mReadLock = nullptr;
+
+ RefPtr<TextureChild> actor = mActor;
+ mActor = nullptr;
+
+ if (actor && !actor->mDestroyed.compareExchange(false, true)) {
+ actor->Unlock();
+ actor = nullptr;
+ }
+
+ TextureData* data = mData;
+ if (!mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ mData = nullptr;
+ }
+
+ if (data || actor) {
+ TextureDeallocParams params;
+ params.actor = actor;
+ params.allocator = mAllocator;
+ params.clientDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
+ params.workAroundSharedSurfaceOwnershipIssue = mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+ if (mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ params.data = nullptr;
+ } else {
+ params.data = data;
+ }
+ // At the moment we always deallocate synchronously when deallocating on the
+ // client side, but having asynchronous deallocate in some of the cases will
+ // be a worthwhile optimization.
+ params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT) || aForceSync;
+
+ // Release the lock before calling DeallocateTextureClient because the latter
+ // may wait for the main thread which could create a dead-lock.
+
+ if (actor) {
+ actor->Unlock();
+ }
+
+ DeallocateTextureClient(params);
+ }
+}
+
+void
+TextureClient::LockActor() const
+{
+ if (mActor) {
+ mActor->Lock();
+ }
+}
+
+void
+TextureClient::UnlockActor() const
+{
+ if (mActor) {
+ mActor->Unlock();
+ }
+}
+
+bool
+TextureClient::IsReadLocked() const
+{
+ return mReadLock && mReadLock->GetReadCount() > 1;
+}
+
+bool
+TextureClient::Lock(OpenMode aMode)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(!mIsLocked);
+ if (!IsValid()) {
+ return false;
+ }
+ if (mIsLocked) {
+ return mOpenMode == aMode;
+ }
+
+ if (aMode & OpenMode::OPEN_WRITE && IsReadLocked()) {
+ NS_WARNING("Attempt to Lock a texture that is being read by the compositor!");
+ return false;
+ }
+
+ LockActor();
+
+ mIsLocked = mData->Lock(aMode);
+ mOpenMode = aMode;
+
+ auto format = GetFormat();
+ if (mIsLocked && CanExposeDrawTarget() &&
+ aMode == OpenMode::OPEN_READ_WRITE &&
+ NS_IsMainThread() &&
+ // the formats that we apparently expect, in the cairo backend. Any other
+ // format will trigger an assertion in GfxFormatToCairoFormat.
+ (format == SurfaceFormat::A8R8G8B8_UINT32 ||
+ format == SurfaceFormat::X8R8G8B8_UINT32 ||
+ format == SurfaceFormat::A8 ||
+ format == SurfaceFormat::R5G6B5_UINT16)) {
+ if (!BorrowDrawTarget()) {
+ // Failed to get a DrawTarget, means we won't be able to write into the
+ // texture, might as well fail now.
+ Unlock();
+ return false;
+ }
+ }
+
+ if (!mIsLocked) {
+ UnlockActor();
+ }
+
+ return mIsLocked;
+}
+
+void
+TextureClient::Unlock()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ if (!IsValid() || !mIsLocked) {
+ return;
+ }
+
+ if (mBorrowedDrawTarget) {
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mBorrowedDrawTarget->Flush();
+ if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
+ // Fallback implementation for reading back, because mData does not
+ // have a backend-specific implementation and returned false.
+ RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
+ mReadbackSink->ProcessReadback(dataSurf);
+ }
+ }
+
+ mBorrowedDrawTarget->DetachAllSnapshots();
+ // If this assertion is hit, it means something is holding a strong reference
+ // to our DrawTarget externally, which is not allowed.
+ MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
+
+ mBorrowedDrawTarget = nullptr;
+ }
+
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mUpdated = true;
+ }
+
+ if (mData) {
+ mData->Unlock();
+ }
+ mIsLocked = false;
+ mOpenMode = OpenMode::OPEN_NONE;
+
+ UnlockActor();
+}
+
+void
+TextureClient::EnableReadLock()
+{
+ if (!mReadLock) {
+ mReadLock = TextureReadLock::Create(mAllocator);
+ }
+}
+
+void
+TextureClient::SerializeReadLock(ReadLockDescriptor& aDescriptor)
+{
+ if (mReadLock && mUpdated) {
+ // Take a read lock on behalf of the TextureHost. The latter will unlock
+ // after the shared data is available again for drawing.
+ mReadLock->ReadLock();
+ mReadLock->Serialize(aDescriptor);
+ mUpdated = false;
+ } else {
+ aDescriptor = null_t();
+ }
+}
+
+TextureClient::~TextureClient()
+{
+ mReadLock = nullptr;
+ Destroy(false);
+}
+
+void
+TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ MOZ_ASSERT(aSurface);
+ // If you run into this assertion, make sure the texture was locked write-only
+ // rather than read-write.
+ MOZ_ASSERT(!mBorrowedDrawTarget);
+
+ // XXX - It would be better to first try the DrawTarget approach and fallback
+ // to the backend-specific implementation because the latter will usually do
+ // an expensive read-back + cpu-side copy if the texture is on the gpu.
+ // There is a bug with the DrawTarget approach, though specific to reading back
+ // from WebGL (where R and B channel end up inverted) to figure out first.
+ if (mData->UpdateFromSurface(aSurface)) {
+ return;
+ }
+ if (CanExposeDrawTarget() && NS_IsMainThread()) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ MOZ_ASSERT(dt);
+ if (dt) {
+ dt->CopySurface(aSurface,
+ gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
+ gfx::IntPoint(0, 0));
+ return;
+ }
+ }
+ NS_WARNING("TextureClient::UpdateFromSurface failed");
+}
+
+
+already_AddRefed<TextureClient>
+TextureClient::CreateSimilar(LayersBackend aLayersBackend, TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
+{
+ MOZ_ASSERT(IsValid());
+
+ MOZ_ASSERT(!mIsLocked);
+ if (mIsLocked) {
+ return nullptr;
+ }
+
+ LockActor();
+ TextureData* data = mData->CreateSimilar(mAllocator, aLayersBackend, aFlags, aAllocFlags);
+ UnlockActor();
+
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aFlags, mAllocator);
+}
+
+gfx::DrawTarget*
+TextureClient::BorrowDrawTarget()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ // TODO- We can't really assert that at the moment because there is code that Borrows
+ // the DrawTarget, just to get a snapshot, which is legit in term of OpenMode
+ // but we should have a way to get a SourceSurface directly instead.
+ //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
+
+ if (!IsValid() || !mIsLocked) {
+ return nullptr;
+ }
+
+ if (!NS_IsMainThread()) {
+ return nullptr;
+ }
+
+ if (!mBorrowedDrawTarget) {
+ mBorrowedDrawTarget = mData->BorrowDrawTarget();
+#ifdef DEBUG
+ mExpectedDtRefs = mBorrowedDrawTarget ? mBorrowedDrawTarget->refCount() : 0;
+#endif
+ }
+
+ return mBorrowedDrawTarget;
+}
+
+bool
+TextureClient::BorrowMappedData(MappedTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ // TODO - SharedRGBImage just accesses the buffer without properly locking
+ // the texture. It's bad.
+ //MOZ_ASSERT(mIsLocked);
+ //if (!mIsLocked) {
+ // return nullptr;
+ //}
+
+ return mData ? mData->BorrowMappedData(aMap) : false;
+}
+
+bool
+TextureClient::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->BorrowMappedYCbCrData(aMap) : false;
+}
+
+bool
+TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->Serialize(aOutDescriptor) : false;
+}
+
+// static
+PTextureChild*
+TextureClient::CreateIPDLActor()
+{
+ TextureChild* c = new TextureChild();
+ c->AddIPDLReference();
+ return c;
+}
+
+// static
+bool
+TextureClient::DestroyIPDLActor(PTextureChild* actor)
+{
+ static_cast<TextureChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::AsTextureClient(PTextureChild* actor)
+{
+ if (!actor) {
+ return nullptr;
+ }
+
+ TextureChild* tc = static_cast<TextureChild*>(actor);
+
+ tc->Lock();
+
+ // Since TextureClient may be destroyed asynchronously with respect to its
+ // IPDL actor, we must acquire a reference within a lock. The mDestroyed bit
+ // tells us whether or not the main thread has disconnected the TextureClient
+ // from its actor.
+ if (tc->mDestroyed) {
+ tc->Unlock();
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> texture = tc->mTextureClient;
+ tc->Unlock();
+
+ return texture.forget();
+}
+
+bool
+TextureClient::IsSharedWithCompositor() const {
+ return mActor && mActor->IPCOpen();
+}
+
+void
+TextureClient::AddFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags |= aFlags;
+}
+
+void
+TextureClient::RemoveFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags &= ~aFlags;
+}
+
+void
+TextureClient::RecycleTexture(TextureFlags aFlags)
+{
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(!mIsLocked);
+
+ mAddedToCompositableClient = false;
+ if (mFlags != aFlags) {
+ mFlags = aFlags;
+ }
+}
+
+void
+TextureClient::SetAddedToCompositableClient()
+{
+ if (!mAddedToCompositableClient) {
+ mAddedToCompositableClient = true;
+ if(!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+ MOZ_ASSERT(!mIsLocked);
+ LockActor();
+ if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+ mActor->SendRecycleTexture(mFlags);
+ }
+ UnlockActor();
+ }
+}
+
+void CancelTextureClientRecycle(uint64_t aTextureId, LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return;
+ }
+ MessageLoop* msgLoop = nullptr;
+ msgLoop = aAllocator->GetMessageLoop();
+ if (!msgLoop) {
+ return;
+ }
+ if (MessageLoop::current() == msgLoop) {
+ aAllocator->CancelWaitForRecycle(aTextureId);
+ } else {
+ msgLoop->PostTask(NewRunnableFunction(CancelTextureClientRecycle,
+ aTextureId, aAllocator));
+ }
+}
+
+void
+TextureClient::CancelWaitForRecycle()
+{
+ if (GetFlags() & TextureFlags::RECYCLE) {
+ CancelTextureClientRecycle(mSerial, GetAllocator());
+ return;
+ }
+}
+
+/* static */ void
+TextureClient::TextureClientRecycleCallback(TextureClient* aClient, void* aClosure)
+{
+ MOZ_ASSERT(aClient->GetRecycleAllocator());
+ aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
+}
+
+void
+TextureClient::SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator)
+{
+ mRecycleAllocator = aAllocator;
+ if (aAllocator) {
+ SetRecycleCallback(TextureClientRecycleCallback, nullptr);
+ } else {
+ ClearRecycleCallback();
+ }
+}
+
+bool
+TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+ if (currentFwd != aForwarder) {
+ // It's a bit iffy but right now ShadowLayerForwarder inherits TextureForwarder
+ // even though it should not. ShadowLayerForwarder::GetTextureForwarder actually
+ // returns a pointer to the CompositorBridgeChild.
+ // It's Ok for a texture to move from a ShadowLayerForwarder to another, but
+ // not form a CompositorBridgeChild to another (they use different channels).
+ if (currentTexFwd && currentTexFwd != aForwarder->GetTextureForwarder()) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel CF.";
+ return false;
+ }
+ if (currentFwd && currentFwd->GetCompositorBackendType() != aForwarder->GetCompositorBackendType()) {
+ gfxCriticalError() << "Attempt to move a texture to different compositor backend.";
+ return false;
+ }
+ mActor->mCompositableForwarder = aForwarder;
+ }
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mCompositableForwarder = aForwarder;
+ mActor->mTextureForwarder = aForwarder->GetTextureForwarder();
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+bool
+TextureClient::InitIPDLActor(KnowsCompositor* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ TextureForwarder* fwd = aForwarder->GetTextureForwarder();
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+
+ if (currentFwd) {
+ gfxCriticalError() << "Attempt to remove a texture from a CompositableForwarder.";
+ return false;
+ }
+
+ if (currentTexFwd && currentTexFwd != fwd) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel TF.";
+ return false;
+ }
+ mActor->mTextureForwarder = fwd;
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = fwd->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mTextureForwarder = fwd;
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+PTextureChild*
+TextureClient::GetIPDLActor()
+{
+ return mActor;
+}
+
+static inline gfx::BackendType
+BackendTypeForBackendSelector(LayersBackend aLayersBackend, BackendSelector aSelector)
+{
+ switch (aSelector) {
+ case BackendSelector::Canvas:
+ return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ case BackendSelector::Content:
+ return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
+ return gfx::BackendType::NONE;
+ }
+};
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ return TextureClient::CreateForDrawing(aAllocator->GetTextureForwarder(),
+ aFormat, aSize,
+ layersBackend,
+ aAllocator->GetMaxTextureSize(),
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector);
+
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+
+#ifdef XP_WIN
+ if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ aSize.width <= aMaxTextureSize &&
+ aSize.height <= aMaxTextureSize &&
+ !(aAllocFlags & ALLOC_UPDATE_FROM_SURFACE))
+ {
+ data = DXGITextureData::Create(aSize, aFormat, aAllocFlags);
+ }
+ if (aLayersBackend == LayersBackend::LAYERS_D3D9 &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ aAllocator->IsSameProcess() &&
+ aSize.width <= aMaxTextureSize &&
+ aSize.height <= aMaxTextureSize &&
+ NS_IsMainThread() &&
+ DeviceManagerD3D9::GetDevice()) {
+ data = D3D9TextureData::Create(aSize, aFormat, aAllocFlags);
+ }
+
+ if (!data && aFormat == SurfaceFormat::B8G8R8X8 &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ NS_IsMainThread()) {
+ data = DIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+#endif
+
+#ifdef MOZ_X11
+ gfxSurfaceType type =
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
+
+ if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ type == gfxSurfaceType::Xlib)
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#ifdef GL_PROVIDER_GLX
+ if (!data && aLayersBackend == LayersBackend::LAYERS_OPENGL &&
+ type == gfxSurfaceType::Xlib &&
+ aFormat != SurfaceFormat::A8 &&
+ gl::sGLXLibrary.UseTextureFromPixmap())
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#endif
+#endif
+
+#ifdef XP_MACOSX
+ if (!data && gfxPrefs::UseIOSurfaceTextures()) {
+ data = MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
+ }
+
+ if (moz2DBackend == BackendType::SKIA && aFormat == SurfaceFormat::B8G8R8X8) {
+ // Skia doesn't support RGBX, so ensure we clear the buffer for the proper alpha values.
+ aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER);
+ }
+
+ // Can't do any better than a buffer texture client.
+ return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
+ moz2DBackend, aLayersBackend,
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ gfx::IntSize size = aSurface->GetSize();
+
+ if (!gfx::Factory::AllowedSurfaceSize(size)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+#if defined(XP_WIN)
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(layersBackend, aSelector);
+
+ int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
+
+ if (layersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ size.width <= maxTextureSize &&
+ size.height <= maxTextureSize)
+ {
+ data = D3D11TextureData::Create(aSurface, aAllocFlags);
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator->GetTextureForwarder());
+ }
+
+ // Fall back to using UpdateFromSurface
+
+ TextureAllocationFlags allocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE);
+ RefPtr<TextureClient> client = CreateForDrawing(aAllocator, aSurface->GetFormat(), size,
+ aSelector, aTextureFlags, allocFlags);
+ if (!client) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(client, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ client->UpdateFromSurface(aSurface);
+ return client.forget();
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return CreateForRawBufferAccess(aAllocator->GetTextureForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aAllocator->GetCompositorBackendType(),
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (aAllocFlags & ALLOC_DISALLOW_BUFFERTEXTURECLIENT) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ // D2D backend does not support CreateDrawTargetForData(). Use CAIRO instead.
+ if (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
+ aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) {
+ aMoz2DBackend = gfx::BackendType::CAIRO;
+ }
+
+ TextureData* texData = BufferTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aTextureFlags,
+ aAllocFlags, aAllocator);
+ if (!texData) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
+ aStereoMode, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ TextureData* data =
+ BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+: AtomicRefCountedWithFinalize("TextureClient")
+, mAllocator(aAllocator)
+, mActor(nullptr)
+, mData(aData)
+, mFlags(aFlags)
+, mOpenMode(OpenMode::OPEN_NONE)
+#ifdef DEBUG
+, mExpectedDtRefs(0)
+#endif
+, mIsLocked(false)
+, mUpdated(false)
+, mAddedToCompositableClient(false)
+, mWorkaroundAnnoyingSharedSurfaceLifetimeIssues(false)
+, mWorkaroundAnnoyingSharedSurfaceOwnershipIssues(false)
+, mFwdTransactionId(0)
+, mSerial(++sSerialCounter)
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+, mPoolTracker(nullptr)
+#endif
+{
+ mData->FillInfo(mInfo);
+ mFlags |= mData->GetTextureFlags();
+}
+
+bool TextureClient::CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint)
+{
+ MOZ_ASSERT(IsLocked());
+ MOZ_ASSERT(aTarget->IsLocked());
+
+ if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) {
+ return false;
+ }
+
+ RefPtr<DrawTarget> destinationTarget = aTarget->BorrowDrawTarget();
+ if (!destinationTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<DrawTarget> sourceTarget = BorrowDrawTarget();
+ if (!sourceTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
+ destinationTarget->CopySurface(source,
+ aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
+ aPoint ? *aPoint : gfx::IntPoint(0, 0));
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+TextureClient::GetAsSurface()
+{
+ if (!Lock(OpenMode::OPEN_READ)) {
+ return nullptr;
+ }
+ RefPtr<gfx::DataSourceSurface> data;
+ { // scope so that the DrawTarget is destroyed before Unlock()
+ RefPtr<gfx::DrawTarget> dt = BorrowDrawTarget();
+ if (dt) {
+ RefPtr<gfx::SourceSurface> surf = dt->Snapshot();
+ if (surf) {
+ data = surf->GetDataSurface();
+ }
+ }
+ }
+ Unlock();
+ return data.forget();
+}
+
+void
+TextureClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TextureClient (0x%p)", this).get();
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ AppendToString(aStream, mFlags, " [flags=", "]");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+class MemoryTextureReadLock : public TextureReadLock {
+public:
+ MemoryTextureReadLock();
+
+ ~MemoryTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual LockType GetType() override { return TYPE_MEMORY; }
+
+ virtual bool IsValid() const override { return true; };
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ int32_t mReadCount;
+};
+
+// The cross-prcess implementation of TextureReadLock.
+//
+// Since we don't use cross-process reference counting for the ReadLock objects,
+// we use the lock's internal counter as a way to know when to deallocate the
+// underlying shmem section: when the counter is equal to 1, it means that the
+// lock is not "held" (the texture is writable), when the counter is equal to 0
+// it means that we can safely deallocate the shmem section without causing a race
+// condition with the other process.
+class ShmemTextureReadLock : public TextureReadLock {
+public:
+ struct ShmReadLockInfo {
+ int32_t readCount;
+ };
+
+ explicit ShmemTextureReadLock(LayersIPCChannel* aAllocator);
+
+ ~ShmemTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual bool IsValid() const override { return mAllocSuccess; };
+
+ virtual LockType GetType() override { return TYPE_SHMEM; }
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
+
+ explicit ShmemTextureReadLock(const mozilla::layers::ShmemSection& aShmemSection)
+ : mShmemSection(aShmemSection)
+ , mAllocSuccess(true)
+ {
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ }
+
+ ShmReadLockInfo* GetShmReadLockInfoPtr()
+ {
+ return reinterpret_cast<ShmReadLockInfo*>
+ (mShmemSection.shmem().get<char>() + mShmemSection.offset());
+ }
+
+ RefPtr<LayersIPCChannel> mClientAllocator;
+ mozilla::layers::ShmemSection mShmemSection;
+ bool mAllocSuccess;
+};
+
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator)
+{
+ switch (aDescriptor.type()) {
+ case ReadLockDescriptor::TShmemSection: {
+ const ShmemSection& section = aDescriptor.get_ShmemSection();
+ MOZ_RELEASE_ASSERT(section.shmem().IsReadable());
+ return MakeAndAddRef<ShmemTextureReadLock>(section);
+ }
+ case ReadLockDescriptor::Tuintptr_t: {
+ if (!aAllocator->IsSameProcess()) {
+ // Trying to use a memory based lock instead of a shmem based one in
+ // the cross-process case is a bad security violation.
+ NS_ERROR("A client process may be trying to peek at the host's address space!");
+ return nullptr;
+ }
+ RefPtr<TextureReadLock> lock = reinterpret_cast<MemoryTextureReadLock*>(
+ aDescriptor.get_uintptr_t()
+ );
+
+ MOZ_ASSERT(lock);
+ if (lock) {
+ // The corresponding AddRef is in MemoryTextureReadLock::Serialize
+ lock.get()->Release();
+ }
+
+ return lock.forget();
+ }
+ case ReadLockDescriptor::Tnull_t: {
+ return nullptr;
+ }
+ default: {
+ // Invalid descriptor.
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ }
+ }
+ return nullptr;
+}
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Create(LayersIPCChannel* aAllocator)
+{
+ if (aAllocator->IsSameProcess()) {
+ // If our compositor is in the same process, we can save some cycles by not
+ // using shared memory.
+ return MakeAndAddRef<MemoryTextureReadLock>();
+ }
+
+ return MakeAndAddRef<ShmemTextureReadLock>(aAllocator);
+}
+
+MemoryTextureReadLock::MemoryTextureReadLock()
+: mReadCount(1)
+{
+ MOZ_COUNT_CTOR(MemoryTextureReadLock);
+}
+
+MemoryTextureReadLock::~MemoryTextureReadLock()
+{
+ // One read count that is added in constructor.
+ MOZ_ASSERT(mReadCount == 1);
+ MOZ_COUNT_DTOR(MemoryTextureReadLock);
+}
+
+bool
+MemoryTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ // AddRef here and Release when receiving on the host side to make sure the
+ // reference count doesn't go to zero before the host receives the message.
+ // see TextureReadLock::Deserialize
+ this->AddRef();
+ aOutput = ReadLockDescriptor(uintptr_t(this));
+ return true;
+}
+
+int32_t
+MemoryTextureReadLock::ReadLock()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+
+ return PR_ATOMIC_INCREMENT(&mReadCount);
+}
+
+int32_t
+MemoryTextureReadLock::ReadUnlock()
+{
+ int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
+ MOZ_ASSERT(readCount >= 0);
+
+ return readCount;
+}
+
+int32_t
+MemoryTextureReadLock::GetReadCount()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+ return mReadCount;
+}
+
+ShmemTextureReadLock::ShmemTextureReadLock(LayersIPCChannel* aAllocator)
+ : mClientAllocator(aAllocator)
+ , mAllocSuccess(false)
+{
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ MOZ_ASSERT(mClientAllocator);
+#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
+ if (mClientAllocator->GetTileLockAllocator()->AllocShmemSection(
+ MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ info->readCount = 1;
+ mAllocSuccess = true;
+ }
+}
+
+ShmemTextureReadLock::~ShmemTextureReadLock()
+{
+ if (mClientAllocator) {
+ // Release one read count that is added in constructor.
+ // The count is kept for calling GetReadCount() by TextureClientPool.
+ ReadUnlock();
+ }
+ MOZ_COUNT_DTOR(ShmemTextureReadLock);
+}
+
+bool
+ShmemTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ aOutput = ReadLockDescriptor(GetShmemSection());
+ return true;
+}
+
+int32_t
+ShmemTextureReadLock::ReadLock() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return PR_ATOMIC_INCREMENT(&info->readCount);
+}
+
+int32_t
+ShmemTextureReadLock::ReadUnlock() {
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
+ MOZ_ASSERT(readCount >= 0);
+ if (readCount <= 0) {
+ if (mClientAllocator) {
+ mClientAllocator->GetTileLockAllocator()->DeallocShmemSection(mShmemSection);
+ } else {
+ // we are on the compositor process
+ FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mShmemSection);
+ }
+ }
+ return readCount;
+}
+
+int32_t
+ShmemTextureReadLock::GetReadCount() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return info->readCount;
+}
+
+bool
+UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData)
+{
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsLocked());
+ MOZ_ASSERT(aTexture->GetFormat() == gfx::SurfaceFormat::YUV, "This textureClient can only use YCbCr data");
+ MOZ_ASSERT(!aTexture->IsImmutable());
+ MOZ_ASSERT(aTexture->IsValid());
+ MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
+
+ MappedYCbCrTextureData mapped;
+ if (!aTexture->BorrowMappedYCbCrData(mapped)) {
+ NS_WARNING("Failed to extract YCbCr info!");
+ return false;
+ }
+
+ MappedYCbCrTextureData srcData;
+ srcData.y.data = aData.mYChannel;
+ srcData.y.size = aData.mYSize;
+ srcData.y.stride = aData.mYStride;
+ srcData.y.skip = aData.mYSkip;
+ srcData.cb.data = aData.mCbChannel;
+ srcData.cb.size = aData.mCbCrSize;
+ srcData.cb.stride = aData.mCbCrStride;
+ srcData.cb.skip = aData.mCbSkip;
+ srcData.cr.data = aData.mCrChannel;
+ srcData.cr.size = aData.mCbCrSize;
+ srcData.cr.stride = aData.mCbCrStride;
+ srcData.cr.skip = aData.mCrSkip;
+ srcData.metadata = nullptr;
+
+ if (!srcData.CopyInto(mapped)) {
+ NS_WARNING("Failed to copy image data!");
+ return false;
+ }
+
+ if (TextureRequiresLocking(aTexture->GetFlags())) {
+ // We don't have support for proper locking yet, so we'll
+ // have to be immutable instead.
+ aTexture->MarkImmutable();
+ }
+ return true;
+}
+
+already_AddRefed<SyncObject>
+SyncObject::CreateSyncObject(SyncHandle aHandle)
+{
+ if (!aHandle) {
+ return nullptr;
+ }
+
+#ifdef XP_WIN
+ return MakeAndAddRef<SyncObjectD3D11>(aHandle);
+#else
+ MOZ_ASSERT_UNREACHABLE();
+ return nullptr;
+#endif
+}
+
+already_AddRefed<TextureClient>
+TextureClient::CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ if (!aData) {
+ return nullptr;
+ }
+ return MakeAndAddRef<TextureClient>(aData, aFlags, aAllocator);
+}
+
+bool
+MappedYCbCrChannelData::CopyInto(MappedYCbCrChannelData& aDst)
+{
+ if (!data || !aDst.data || size != aDst.size) {
+ return false;
+ }
+
+ if (stride == aDst.stride) {
+ // fast path!
+ // We assume that the padding in the destination is there for alignment
+ // purposes and doesn't contain useful data.
+ memcpy(aDst.data, data, stride * size.height);
+ return true;
+ }
+
+ for (int32_t i = 0; i < size.height; ++i) {
+ if (aDst.skip == 0 && skip == 0) {
+ // fast-ish path
+ memcpy(aDst.data + i * aDst.stride,
+ data + i * stride,
+ size.width);
+ } else {
+ // slow path
+ uint8_t* src = data + i * stride;
+ uint8_t* dst = aDst.data + i * aDst.stride;
+ for (int32_t j = 0; j < size.width; ++j) {
+ *dst = *src;
+ src += 1 + skip;
+ dst += 1 + aDst.skip;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h
new file mode 100644
index 000000000..d28f37cf5
--- /dev/null
+++ b/gfx/layers/client/TextureClient.h
@@ -0,0 +1,826 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_H
+#define MOZILLA_GFX_TEXTURECLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageTypes.h" // for StereoMode
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/gfx/CriticalSection.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "GfxTexturesReporter.h"
+#include "pratom.h"
+#include "nsThreadUtils.h"
+
+class gfxImageSurface;
+
+namespace mozilla {
+
+// When defined, we track which pool the tile came from and test for
+// any inconsistencies. This can be defined in release build as well.
+#ifdef DEBUG
+#define GFX_DEBUG_TRACK_CLIENTS_IN_POOL 1
+#endif
+
+namespace layers {
+
+class AsyncTransactionWaiter;
+class BufferTextureData;
+class CompositableForwarder;
+class KnowsCompositor;
+class LayersIPCChannel;
+class CompositableClient;
+struct PlanarYCbCrData;
+class Image;
+class PTextureChild;
+class TextureChild;
+class TextureData;
+class GPUVideoTextureData;
+struct RawTextureBuffer;
+class RawYCbCrTextureBuffer;
+class TextureClient;
+class ITextureClientRecycleAllocator;
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+class TextureClientPool;
+#endif
+class TextureForwarder;
+class KeepAlive;
+
+/**
+ * TextureClient is the abstraction that allows us to share data between the
+ * content and the compositor side.
+ */
+
+enum TextureAllocationFlags {
+ ALLOC_DEFAULT = 0,
+ ALLOC_CLEAR_BUFFER = 1 << 1, // Clear the buffer to whatever is best for the draw target
+ ALLOC_CLEAR_BUFFER_WHITE = 1 << 2, // explicit all white
+ ALLOC_CLEAR_BUFFER_BLACK = 1 << 3, // explicit all black
+ ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
+
+ // Allocate the texture for out-of-band content updates. This is mostly for
+ // TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
+ // surfaces when used on the main thread.
+ ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
+
+ // Disable any cross-device synchronization. This is also for TextureClientD3D11,
+ // and creates a texture without KeyedMutex.
+ ALLOC_MANUAL_SYNCHRONIZATION = 1 << 6,
+
+ // The texture is going to be updated using UpdateFromSurface and needs to support
+ // that call.
+ ALLOC_UPDATE_FROM_SURFACE = 1 << 7,
+};
+
+#ifdef XP_WIN
+typedef void* SyncHandle;
+#else
+typedef uintptr_t SyncHandle;
+#endif // XP_WIN
+
+class SyncObject : public RefCounted<SyncObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObject)
+ virtual ~SyncObject() { }
+
+ static already_AddRefed<SyncObject> CreateSyncObject(SyncHandle aHandle);
+
+ enum class SyncType {
+ D3D11,
+ };
+
+ virtual SyncType GetSyncType() = 0;
+ virtual void FinalizeFrame() = 0;
+ virtual bool IsSyncObjectValid() = 0;
+
+protected:
+ SyncObject() { }
+};
+
+/**
+ * This class may be used to asynchronously receive an update when the content
+ * drawn to this texture client is available for reading in CPU memory. This
+ * can only be used on texture clients that support draw target creation.
+ */
+class TextureReadbackSink
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadbackSink)
+public:
+ /**
+ * Callback function to implement in order to receive a DataSourceSurface
+ * containing the data read back from the texture client. This will always
+ * be called on the main thread, and this may not hold on to the
+ * DataSourceSurface beyond the execution of this function.
+ */
+ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0;
+
+protected:
+ virtual ~TextureReadbackSink() {}
+};
+
+enum class BackendSelector
+{
+ Content,
+ Canvas
+};
+
+/// Temporary object providing direct access to a Texture's memory.
+///
+/// see TextureClient::CanExposeMappedData() and TextureClient::BorrowMappedData().
+struct MappedTextureData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ gfx::SurfaceFormat format;
+};
+
+struct MappedYCbCrChannelData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ int32_t skip;
+
+ bool CopyInto(MappedYCbCrChannelData& aDst);
+};
+
+struct MappedYCbCrTextureData {
+ MappedYCbCrChannelData y;
+ MappedYCbCrChannelData cb;
+ MappedYCbCrChannelData cr;
+ // Sad but because of how SharedPlanarYCbCrData is used we have to expose this for now.
+ uint8_t* metadata;
+ StereoMode stereoMode;
+
+ bool CopyInto(MappedYCbCrTextureData& aDst)
+ {
+ return y.CopyInto(aDst.y)
+ && cb.CopyInto(aDst.cb)
+ && cr.CopyInto(aDst.cr);
+ }
+};
+
+class ReadLockDescriptor;
+
+// A class to help implement copy-on-write semantics for shared textures.
+//
+// A TextureClient/Host pair can opt into using a ReadLock by calling
+// TextureClient::EnableReadLock. This will equip the TextureClient with a
+// ReadLock object that will be automatically ReadLock()'ed by the texture itself
+// when it is written into (see TextureClient::Unlock).
+// A TextureReadLock's counter starts at 1 and is expected to be equal to 1 when the
+// lock is destroyed. See ShmemTextureReadLock for explanations about why we use
+// 1 instead of 0 as the initial state.
+// TextureReadLock is mostly internally managed by the TextureClient/Host pair,
+// and the compositable only has to forward it during updates. If an update message
+// contains a null_t lock, it means that the texture was not written into on the
+// content side, and there is no synchronization required on the compositor side
+// (or it means that the texture pair did not opt into using ReadLocks).
+// On the compositor side, the TextureHost can receive a ReadLock during a
+// transaction, and will both ReadUnlock() it and drop it as soon as the shared
+// data is available again for writing (the texture upload is done, or the compositor
+// not reading the texture anymore). The lock is dropped to make sure it is
+// ReadUnlock()'ed only once.
+class TextureReadLock {
+protected:
+ virtual ~TextureReadLock() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadLock)
+
+ virtual int32_t ReadLock() = 0;
+ virtual int32_t ReadUnlock() = 0;
+ virtual int32_t GetReadCount() = 0;
+ virtual bool IsValid() const = 0;
+
+ static already_AddRefed<TextureReadLock>
+ Create(LayersIPCChannel* aAllocator);
+
+ static already_AddRefed<TextureReadLock>
+ Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator);
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) = 0;
+
+ enum LockType {
+ TYPE_MEMORY,
+ TYPE_SHMEM
+ };
+ virtual LockType GetType() = 0;
+
+protected:
+ NS_DECL_OWNINGTHREAD
+};
+
+#ifdef XP_WIN
+class D3D11TextureData;
+#endif
+
+class TextureData {
+public:
+ struct Info {
+ gfx::IntSize size;
+ gfx::SurfaceFormat format;
+ bool hasIntermediateBuffer;
+ bool hasSynchronization;
+ bool supportsMoz2D;
+ bool canExposeMappedData;
+
+ Info()
+ : format(gfx::SurfaceFormat::UNKNOWN)
+ , hasIntermediateBuffer(false)
+ , hasSynchronization(false)
+ , supportsMoz2D(false)
+ , canExposeMappedData(false)
+ {}
+ };
+
+ TextureData() { MOZ_COUNT_CTOR(TextureData); }
+
+ virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
+
+ virtual void FillInfo(TextureData::Info& aInfo) const = 0;
+
+ virtual bool Lock(OpenMode aMode) = 0;
+
+ virtual void Unlock() = 0;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
+
+ virtual bool BorrowMappedData(MappedTextureData&) { return false; }
+
+ virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) = 0;
+
+ /// Depending on the texture's flags either Deallocate or Forget is called.
+ virtual void Forget(LayersIPCChannel* aAllocator) {}
+
+ virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
+
+ virtual bool ReadBack(TextureReadbackSink* aReadbackSink) { return false; }
+
+ virtual void SyncWithObject(SyncObject* aFence) {};
+
+ virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
+
+#ifdef XP_WIN
+ virtual D3D11TextureData* AsD3D11TextureData() {
+ return nullptr;
+ }
+#endif
+
+ virtual BufferTextureData* AsBufferTextureData() { return nullptr; }
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() { return nullptr; }
+};
+
+/**
+ * TextureClient is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * content-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureHost lives on the compositor-side.
+ *
+ * TextureClient's primary purpose is to present texture data in a way that is
+ * understood by the IPC system. There are two ways to use it:
+ * - Use it to serialize image data that is not IPC-friendly (most likely
+ * involving a copy into shared memory)
+ * - preallocate it and paint directly into it, which avoids copy but requires
+ * the painting code to be aware of TextureClient (or at least the underlying
+ * shared memory).
+ *
+ * There is always one and only one TextureClient per TextureHost, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * responsibility of the compositable (which would use two Texture pairs).
+ * In order to send several different buffers to the compositor side, use
+ * several TextureClients.
+ */
+class TextureClient
+ : public AtomicRefCountedWithFinalize<TextureClient>
+{
+public:
+ explicit TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual ~TextureClient();
+
+ static already_AddRefed<TextureClient>
+ CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ // Creates and allocates a TextureClient usable with Moz2D.
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ // Creates and allocates a TextureClient supporting the YCbCr format.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient (can be accessed through raw
+ // pointers).
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ // Creates and allocates a TextureClient (can beaccessed through raw
+ // pointers) with a certain buffer size. It's unfortunate that we need this.
+ // providing format and sizes could let us do more optimization.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient of the same type.
+ already_AddRefed<TextureClient>
+ CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
+
+ /**
+ * Locks the shared data, allowing the caller to get access to it.
+ *
+ * Please always lock/unlock when accessing the shared data.
+ * If Lock() returns false, you should not attempt to access the shared data.
+ */
+ bool Lock(OpenMode aMode);
+
+ void Unlock();
+
+ bool IsLocked() const { return mIsLocked; }
+
+ gfx::IntSize GetSize() const { return mInfo.size; }
+
+ gfx::SurfaceFormat GetFormat() const { return mInfo.format; }
+
+ /**
+ * Returns true if this texture has a synchronization mechanism (mutex, fence, etc.).
+ * Textures that do not implement synchronization should be immutable or should
+ * use immediate uploads (see TextureFlags in CompositorTypes.h)
+ * Even if a texture does not implement synchronization, Lock and Unlock need
+ * to be used appropriately since the latter are also there to map/numap data.
+ */
+ bool HasSynchronization() const { return mInfo.hasSynchronization; }
+
+ /**
+ * Indicates whether the TextureClient implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureClient does not contend with locking the texture on the host side.
+ */
+ bool HasIntermediateBuffer() const { return mInfo.hasIntermediateBuffer; }
+
+ bool CanExposeDrawTarget() const { return mInfo.supportsMoz2D; }
+
+ bool CanExposeMappedData() const { return mInfo.canExposeMappedData; }
+
+ /**
+ * Returns a DrawTarget to draw into the TextureClient.
+ * This function should never be called when not on the main thread!
+ *
+ * This must never be called on a TextureClient that is not sucessfully locked.
+ * When called several times within one Lock/Unlock pair, this method should
+ * return the same DrawTarget.
+ * The DrawTarget is automatically flushed by the TextureClient when the latter
+ * is unlocked, and the DrawTarget that will be returned within the next
+ * lock/unlock pair may or may not be the same object.
+ * Do not keep references to the DrawTarget outside of the lock/unlock pair.
+ *
+ * This is typically used as follows:
+ *
+ * if (!texture->Lock(OpenMode::OPEN_READ_WRITE)) {
+ * return false;
+ * }
+ * {
+ * // Restrict this code's scope to ensure all references to dt are gone
+ * // when Unlock is called.
+ * DrawTarget* dt = texture->BorrowDrawTarget();
+ * // use the draw target ...
+ * }
+ * texture->Unlock();
+ *
+ */
+ gfx::DrawTarget* BorrowDrawTarget();
+
+ /**
+ * Similar to BorrowDrawTarget but provides direct access to the texture's bits
+ * instead of a DrawTarget.
+ */
+ bool BorrowMappedData(MappedTextureData&);
+ bool BorrowMappedYCbCrData(MappedYCbCrTextureData&);
+
+ /**
+ * This function can be used to update the contents of the TextureClient
+ * off the main thread.
+ */
+ void UpdateFromSurface(gfx::SourceSurface* aSurface);
+
+ /**
+ * This method is strictly for debugging. It causes locking and
+ * needless copies.
+ */
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Copies a rectangle from this texture client to a position in aTarget.
+ * It is assumed that the necessary locks are in place; so this should at
+ * least have a read lock and aTarget should at least have a write lock.
+ */
+ bool CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint);
+
+ /**
+ * Allocate and deallocate a TextureChild actor.
+ *
+ * TextureChild is an implementation detail of TextureClient that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPextureChild and DeallocPTextureChild).
+ */
+ static PTextureChild* CreateIPDLActor();
+ static bool DestroyIPDLActor(PTextureChild* actor);
+
+ /**
+ * Get the TextureClient corresponding to the actor passed in parameter.
+ */
+ static already_AddRefed<TextureClient> AsTextureClient(PTextureChild* actor);
+
+ /**
+ * TextureFlags contain important information about various aspects
+ * of the texture, like how its liferime is managed, and how it
+ * should be displayed.
+ * See TextureFlags in CompositorTypes.h.
+ */
+ TextureFlags GetFlags() const { return mFlags; }
+
+ bool HasFlags(TextureFlags aFlags) const
+ {
+ return (mFlags & aFlags) == aFlags;
+ }
+
+ void AddFlags(TextureFlags aFlags);
+
+ void RemoveFlags(TextureFlags aFlags);
+
+ // Must not be called when TextureClient is in use by CompositableClient.
+ void RecycleTexture(TextureFlags aFlags);
+
+ /**
+ * After being shared with the compositor side, an immutable texture is never
+ * modified, it can only be read. It is safe to not Lock/Unlock immutable
+ * textures.
+ */
+ bool IsImmutable() const { return !!(mFlags & TextureFlags::IMMUTABLE); }
+
+ void MarkImmutable() { AddFlags(TextureFlags::IMMUTABLE); }
+
+ bool IsSharedWithCompositor() const;
+
+ /**
+ * If this method returns false users of TextureClient are not allowed
+ * to access the shared data.
+ */
+ bool IsValid() const { return !!mData; }
+
+ /**
+ * Called when TextureClient is added to CompositableClient.
+ */
+ void SetAddedToCompositableClient();
+
+ /**
+ * If this method retuns false, TextureClient is already added to CompositableClient,
+ * since its creation or recycling.
+ */
+ bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; }
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a CompositableForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(CompositableForwarder* aForwarder);
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a TextureForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(KnowsCompositor* aForwarder);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureChild* GetIPDLActor();
+
+ /**
+ * Triggers the destruction of the shared data and the corresponding TextureHost.
+ *
+ * If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction
+ * will be synchronously coordinated with the compositor side, otherwise it
+ * will be done asynchronously.
+ * If sync is true, the destruction will be synchronous regardless of the
+ * texture's flags (bad for performance, use with care).
+ */
+ void Destroy(bool sync = false);
+
+ /**
+ * Track how much of this texture is wasted.
+ * For example we might allocate a 256x256 tile but only use 10x10.
+ */
+ void SetWaste(int aWasteArea) {
+ mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
+ }
+
+ /**
+ * This sets the readback sink that this texture is to use. This will
+ * receive the data for this texture as soon as it becomes available after
+ * texture unlock.
+ */
+ virtual void SetReadbackSink(TextureReadbackSink* aReadbackSink) {
+ mReadbackSink = aReadbackSink;
+ }
+
+ void SyncWithObject(SyncObject* aFence) { mData->SyncWithObject(aFence); }
+
+ LayersIPCChannel* GetAllocator() { return mAllocator; }
+
+ ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
+ void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
+
+ /// If you add new code that uses this method, you are probably doing something wrong.
+ TextureData* GetInternalData() { return mData; }
+ const TextureData* GetInternalData() const { return mData; }
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ void CancelWaitForRecycle();
+
+ /**
+ * Set last transaction id of CompositableForwarder.
+ *
+ * Called when TextureClient has TextureFlags::RECYCLE flag.
+ * When CompositableForwarder forwards the TextureClient with
+ * TextureFlags::RECYCLE, it holds TextureClient's ref until host side
+ * releases it. The host side sends TextureClient release message.
+ * The id is used to check if the message is for the last TextureClient
+ * forwarding.
+ */
+ void SetLastFwdTransactionId(uint64_t aTransactionId)
+ {
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetLastFwdTransactionId() { return mFwdTransactionId; }
+
+ void EnableReadLock();
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ bool IsReadLocked() const;
+
+ void SerializeReadLock(ReadLockDescriptor& aDescriptor);
+
+private:
+ static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
+
+ // Internal helpers for creating texture clients using the actual forwarder instead
+ // of KnowsCompositor. TextureClientPool uses these to let it cache texture clients
+ // per-process instead of per ShadowLayerForwarder, but everyone else should
+ // use the public functions instead.
+ friend class TextureClientPool;
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ /**
+ * Called once, during the destruction of the Texture, on the thread in which
+ * texture's reference count reaches 0 (could be any thread).
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize() {}
+
+ friend class AtomicRefCountedWithFinalize<TextureClient>;
+protected:
+ /**
+ * Should only be called *once* per texture, in TextureClient::InitIPDLActor.
+ * Some texture implementations rely on the fact that the descriptor will be
+ * deserialized.
+ * Calling ToSurfaceDescriptor again after it has already returned true,
+ * or never constructing a TextureHost with aDescriptor may result in a memory
+ * leak (see TextureClientD3D9 for example).
+ */
+ bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
+
+ void LockActor() const;
+ void UnlockActor() const;
+
+ TextureData::Info mInfo;
+
+ RefPtr<LayersIPCChannel> mAllocator;
+ RefPtr<TextureChild> mActor;
+ RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
+ RefPtr<TextureReadLock> mReadLock;
+
+ TextureData* mData;
+ RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
+
+ TextureFlags mFlags;
+
+ gl::GfxTextureWasteTracker mWasteTracker;
+
+ OpenMode mOpenMode;
+#ifdef DEBUG
+ uint32_t mExpectedDtRefs;
+#endif
+ bool mIsLocked;
+ // This member tracks that the texture was written into until the update
+ // is sent to the compositor. We need this remember to lock mReadLock on
+ // behalf of the compositor just before sending the notification.
+ bool mUpdated;
+
+ // Used when TextureClient is recycled with TextureFlags::RECYCLE flag.
+ bool mAddedToCompositableClient;
+
+ bool mWorkaroundAnnoyingSharedSurfaceLifetimeIssues;
+ bool mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+
+ RefPtr<TextureReadbackSink> mReadbackSink;
+
+ uint64_t mFwdTransactionId;
+
+ // Serial id of TextureClient. It is unique in current process.
+ const uint64_t mSerial;
+ // Used to assign serial ids of TextureClient.
+ static mozilla::Atomic<uint64_t> sSerialCounter;
+
+ friend class TextureChild;
+ friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
+ friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+public:
+ // Pointer to the pool this tile came from.
+ TextureClientPool* mPoolTracker;
+#endif
+};
+
+/**
+ * Task that releases TextureClient pointer on a specified thread.
+ */
+class TextureClientReleaseTask : public Runnable
+{
+public:
+ explicit TextureClientReleaseTask(TextureClient* aClient)
+ : mTextureClient(aClient) {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mTextureClient = nullptr;
+ return NS_OK;
+ }
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+};
+
+// Automatically lock and unlock a texture. Since texture locking is fallible,
+// Succeeded() must be checked on the guard object before proceeding.
+class MOZ_RAII TextureClientAutoLock
+{
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+public:
+ TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mTexture(aTexture),
+ mSucceeded(false)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+ mSucceeded = mTexture->Lock(aMode);
+#ifdef DEBUG
+ mChecked = false;
+#endif
+ }
+ ~TextureClientAutoLock() {
+ MOZ_ASSERT(mChecked);
+ if (mSucceeded) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Succeeded() {
+#ifdef DEBUG
+ mChecked = true;
+#endif
+ return mSucceeded;
+ }
+
+private:
+ TextureClient* mTexture;
+#ifdef DEBUG
+ bool mChecked;
+#endif
+ bool mSucceeded;
+};
+
+class KeepAlive
+{
+public:
+ virtual ~KeepAlive() {}
+};
+
+template<typename T>
+class TKeepAlive : public KeepAlive
+{
+public:
+ explicit TKeepAlive(T* aData) : mData(aData) {}
+protected:
+ RefPtr<T> mData;
+};
+
+/// Convenience function to set the content of ycbcr texture.
+bool UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/TextureClientPool.cpp b/gfx/layers/client/TextureClientPool.cpp
new file mode 100644
index 000000000..c556a791e
--- /dev/null
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureClientPool.h"
+#include "CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/TiledContentClient.h"
+
+#include "gfxPrefs.h"
+
+#include "nsComponentManagerUtils.h"
+
+#define TCP_LOG(...)
+//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
+
+namespace mozilla {
+namespace layers {
+
+// We want to shrink to our maximum size of N unused tiles
+// after a timeout to allow for short-term budget requirements
+static void
+ShrinkCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
+}
+
+// After a certain amount of inactivity, let's clear the pool so that
+// we don't hold onto tiles needlessly. In general, allocations are
+// cheap enough that re-allocating isn't an issue unless we're allocating
+// at an inopportune time (e.g. mid-animation).
+static void
+ClearCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->Clear();
+}
+
+TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator)
+ : mBackend(aLayersBackend)
+ , mMaxTextureSize(aMaxTextureSize)
+ , mFormat(aFormat)
+ , mSize(aSize)
+ , mFlags(aFlags)
+ , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+ , mClearTimeoutMsec(aClearTimeoutMsec)
+ , mInitialPoolSize(aInitialPoolSize)
+ , mPoolUnusedSize(aPoolUnusedSize)
+ , mOutstandingClients(0)
+ , mSurfaceAllocator(aAllocator)
+ , mDestroyed(false)
+{
+ TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
+ this, mInitialPoolSize);
+ mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1");
+ mClearTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
+ }
+}
+
+TextureClientPool::~TextureClientPool()
+{
+ mShrinkTimer->Cancel();
+ mClearTimer->Cancel();
+}
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+static bool TestClientPool(const char* what,
+ TextureClient* aClient,
+ TextureClientPool* aPool)
+{
+ if (!aClient || !aPool) {
+ return false;
+ }
+
+ TextureClientPool* actual = aClient->mPoolTracker;
+ bool ok = (actual == aPool);
+ if (ok) {
+ ok = (aClient->GetFormat() == aPool->GetFormat());
+ }
+
+ if (!ok) {
+ if (actual) {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", "
+ << actual << "-" << actual->GetFormat() << ", "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing with actual");
+ } else {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", nullptr, "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing without actual");
+ }
+ }
+ return ok;
+}
+#endif
+
+already_AddRefed<TextureClient>
+TextureClientPool::GetTextureClient()
+{
+ // Try to fetch a client from the pool
+ RefPtr<TextureClient> textureClient;
+
+ // We initially allocate mInitialPoolSize for our pool. If we run
+ // out of TextureClients, we allocate additional TextureClients to try and keep around
+ // mPoolUnusedSize
+ if (!mTextureClients.size()) {
+ AllocateTextureClient();
+ }
+
+ if (!mTextureClients.size()) {
+ // All our allocations failed, return nullptr
+ return nullptr;
+ }
+
+ mOutstandingClients++;
+ textureClient = mTextureClients.top();
+ mTextureClients.pop();
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ if (textureClient) {
+ textureClient->mPoolTracker = this;
+ }
+ DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
+ this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
+
+ return textureClient.forget();
+}
+
+void
+TextureClientPool::AllocateTextureClient()
+{
+ TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
+ this, mOutstandingClients);
+
+ RefPtr<TextureClient> newClient;
+ if (gfxPrefs::ForceShmemTiles()) {
+ // gfx::BackendType::NONE means use the content backend
+ newClient =
+ TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+ mFormat, mSize,
+ gfx::BackendType::NONE,
+ mBackend,
+ mFlags, ALLOC_DEFAULT);
+ } else {
+ newClient =
+ TextureClient::CreateForDrawing(mSurfaceAllocator,
+ mFormat, mSize,
+ mBackend,
+ mMaxTextureSize,
+ BackendSelector::Content,
+ mFlags);
+ }
+
+ if (newClient) {
+ mTextureClients.push(newClient);
+ }
+}
+
+void
+TextureClientPool::ResetTimers()
+{
+ // Shrink down if we're beyond our maximum size
+ if (mShrinkTimeoutMsec &&
+ mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
+ TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
+ mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+
+ // Clear pool after a period of inactivity to reduce memory consumption
+ if (mClearTimeoutMsec) {
+ TCP_LOG("TexturePool %p scheduling a clear\n", this);
+ mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+void
+TextureClientPool::ReturnTextureClient(TextureClient *aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("return", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ // Add the client to the pool:
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ mTextureClients.push(aClient);
+ TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
+ this, aClient, mTextureClients.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+ MOZ_ASSERT(aClient->GetReadLock());
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ mTextureClientsDeferred.push_back(aClient);
+ TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
+ this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ShrinkToMaximumSize()
+{
+ // We're over our desired maximum size, immediately shrink down to the
+ // maximum.
+ //
+ // We cull from the deferred TextureClients first, as we can't reuse those
+ // until they get returned.
+ uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
+
+ // If we have > mInitialPoolSize outstanding, then we want to keep around
+ // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
+ // outstanding, then keep around the entire initial pool size.
+ uint32_t targetUnusedClients;
+ if (mOutstandingClients > mInitialPoolSize) {
+ targetUnusedClients = mPoolUnusedSize;
+ } else {
+ targetUnusedClients = mInitialPoolSize;
+ }
+
+ TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
+ this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
+
+ while (totalUnusedTextureClients > targetUnusedClients) {
+ if (mTextureClientsDeferred.size()) {
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
+ this, mTextureClientsDeferred.front().get(),
+ mTextureClientsDeferred.size() - 1);
+ mTextureClientsDeferred.pop_front();
+ } else {
+ TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
+ this, mTextureClients.top().get(), mTextureClients.size() - 1);
+ mTextureClients.pop();
+ }
+ totalUnusedTextureClients--;
+ }
+}
+
+void
+TextureClientPool::ReturnDeferredClients()
+{
+ if (mTextureClientsDeferred.empty()) {
+ return;
+ }
+
+ TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+ this, mTextureClientsDeferred.size());
+
+ ReturnUnlockedClients();
+ ShrinkToMaximumSize();
+}
+
+void
+TextureClientPool::ReturnUnlockedClients()
+{
+ for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
+ MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
+ // Last count is held by the lock itself.
+ if (!(*it)->IsReadLocked()) {
+ mTextureClients.push(*it);
+ it = mTextureClientsDeferred.erase(it);
+
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ } else {
+ it++;
+ }
+ }
+}
+
+void
+TextureClientPool::ReportClientLost()
+{
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
+ this, mOutstandingClients);
+}
+
+void
+TextureClientPool::Clear()
+{
+ TCP_LOG("TexturePool %p getting cleared\n", this);
+ while (!mTextureClients.empty()) {
+ TCP_LOG("TexturePool %p releasing client %p\n",
+ this, mTextureClients.top().get());
+ mTextureClients.pop();
+ }
+ while (!mTextureClientsDeferred.empty()) {
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p releasing deferred client %p\n",
+ this, mTextureClientsDeferred.front().get());
+ mTextureClientsDeferred.pop_front();
+ }
+}
+
+void TextureClientPool::Destroy()
+{
+ Clear();
+ mDestroyed = true;
+ mInitialPoolSize = 0;
+ mPoolUnusedSize = 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientPool.h b/gfx/layers/client/TextureClientPool.h
new file mode 100644
index 000000000..642c14bf5
--- /dev/null
+++ b/gfx/layers/client/TextureClientPool.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTPOOL_H
+#define MOZILLA_GFX_TEXTURECLIENTPOOL_H
+
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "nsITimer.h"
+#include <stack>
+#include <list>
+
+namespace mozilla {
+namespace layers {
+
+class ISurfaceAllocator;
+class TextureForwarder;
+class TextureReadLock;
+
+class TextureClientAllocator
+{
+protected:
+ virtual ~TextureClientAllocator() {}
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator)
+
+ virtual already_AddRefed<TextureClient> GetTextureClient() = 0;
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ virtual void ReturnTextureClientDeferred(TextureClient *aClient) = 0;
+
+ virtual void ReportClientLost() = 0;
+};
+
+class TextureClientPool final : public TextureClientAllocator
+{
+ ~TextureClientPool();
+
+public:
+ TextureClientPool(LayersBackend aBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator);
+
+ /**
+ * Gets an allocated TextureClient of size and format that are determined
+ * by the initialisation parameters given to the pool. This will either be
+ * a cached client that was returned to the pool, or a newly allocated
+ * client if one isn't available.
+ *
+ * All clients retrieved by this method should be returned using the return
+ * functions, or reported lost so that the pool can manage its size correctly.
+ */
+ already_AddRefed<TextureClient> GetTextureClient() override;
+
+ /**
+ * Return a TextureClient that is no longer being used and is ready for
+ * immediate re-use or destruction.
+ */
+ void ReturnTextureClient(TextureClient *aClient);
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ void ReturnTextureClientDeferred(TextureClient *aClient) override;
+
+ /**
+ * Return any clients to the pool that were previously returned in
+ * ReturnTextureClientDeferred.
+ */
+ void ReturnDeferredClients();
+
+ /**
+ * Attempt to shrink the pool so that there are no more than
+ * mInitialPoolSize outstanding.
+ */
+ void ShrinkToMaximumSize();
+
+ /**
+ * Report that a client retrieved via GetTextureClient() has become
+ * unusable, so that it will no longer be tracked.
+ */
+ virtual void ReportClientLost() override;
+
+ /**
+ * Calling this will cause the pool to attempt to relinquish any unused
+ * clients.
+ */
+ void Clear();
+
+ LayersBackend GetBackend() const { return mBackend; }
+ int32_t GetMaxTextureSize() const { return mMaxTextureSize; }
+ gfx::SurfaceFormat GetFormat() { return mFormat; }
+ TextureFlags GetFlags() const { return mFlags; }
+
+ /**
+ * Clear the pool and put it in a state where it won't recycle any new texture.
+ */
+ void Destroy();
+
+private:
+ void ReturnUnlockedClients();
+
+ /// Allocate a single TextureClient to be returned from the pool.
+ void AllocateTextureClient();
+
+ /// Reset and/or initialise timers for shrinking/clearing the pool.
+ void ResetTimers();
+
+ /// Backend passed to the TextureClient for buffer creation.
+ LayersBackend mBackend;
+
+ // Max texture size passed to the TextureClient for buffer creation.
+ int32_t mMaxTextureSize;
+
+ /// Format is passed to the TextureClient for buffer creation.
+ gfx::SurfaceFormat mFormat;
+
+ /// The width and height of the tiles to be used.
+ gfx::IntSize mSize;
+
+ /// Flags passed to the TextureClient for buffer creation.
+ const TextureFlags mFlags;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to shrink the pool to its maximum size of mPoolUnusedSize.
+ uint32_t mShrinkTimeoutMsec;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to clear the pool.
+ uint32_t mClearTimeoutMsec;
+
+ // The initial number of unused texture clients to seed the pool with
+ // on construction
+ uint32_t mInitialPoolSize;
+
+ // How many unused texture clients to try and keep around if we go over
+ // the initial allocation
+ uint32_t mPoolUnusedSize;
+
+ /// This is a total number of clients in the wild and in the stack of
+ /// deferred clients (see below). So, the total number of clients in
+ /// existence is always mOutstandingClients + the size of mTextureClients.
+ uint32_t mOutstandingClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ std::stack<RefPtr<TextureClient> > mTextureClients;
+
+ std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
+ RefPtr<nsITimer> mShrinkTimer;
+ RefPtr<nsITimer> mClearTimer;
+ // This mSurfaceAllocator owns us, so no need to hold a ref to it
+ TextureForwarder* mSurfaceAllocator;
+
+ // Keep track of whether this pool has been destroyed or not. If it has,
+ // we won't accept returns of TextureClients anymore, and the refcounting
+ // should take care of their destruction.
+ bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENTPOOL_H */
diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp
new file mode 100644
index 000000000..6a06d3f68
--- /dev/null
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxPlatform.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+// Used to keep TextureClient's reference count stable as not to disrupt recycling.
+class TextureClientHolder
+{
+ ~TextureClientHolder() {}
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
+
+ explicit TextureClientHolder(TextureClient* aClient)
+ : mTextureClient(aClient)
+ , mWillRecycle(true)
+ {}
+
+ TextureClient* GetTextureClient()
+ {
+ return mTextureClient;
+ }
+
+ bool WillRecycle()
+ {
+ return mWillRecycle;
+ }
+
+ void ClearWillRecycle()
+ {
+ mWillRecycle = false;
+ }
+
+ void ClearTextureClient() { mTextureClient = nullptr; }
+protected:
+ RefPtr<TextureClient> mTextureClient;
+ bool mWillRecycle;
+};
+
+class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : ITextureClientAllocationHelper(aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocationFlags)
+ , mAllocator(aAllocator)
+ {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override
+ {
+ if (aTextureClient->GetFormat() != mFormat ||
+ aTextureClient->GetSize() != mSize) {
+ return false;
+ }
+ return true;
+ }
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
+ {
+ return mAllocator->Allocate(mFormat,
+ mSize,
+ mSelector,
+ mTextureFlags,
+ mAllocationFlags);
+ }
+
+protected:
+ TextureClientRecycleAllocator* mAllocator;
+};
+
+YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
+ aData.mYSize,
+ BackendSelector::Content,
+ aTextureFlags,
+ ALLOC_DEFAULT)
+ , mData(aData)
+{
+}
+
+bool
+YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
+{
+ MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
+
+ BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
+ if (!bufferData ||
+ aTextureClient->GetSize() != mData.mYSize ||
+ bufferData->GetCbCrSize().isNothing() ||
+ bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
+ bufferData->GetYUVColorSpace().isNothing() ||
+ bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
+ bufferData->GetStereoMode().isNothing() ||
+ bufferData->GetStereoMode().ref() != mData.mStereoMode) {
+ return false;
+ }
+ return true;
+}
+
+already_AddRefed<TextureClient>
+YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
+{
+ return TextureClient::CreateForYCbCr(aAllocator,
+ mData.mYSize, mData.mCbCrSize,
+ mData.mStereoMode,
+ mData.mYUVColorSpace,
+ mTextureFlags);
+}
+
+TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
+ : mSurfaceAllocator(aAllocator)
+ , mMaxPooledSize(kMaxPooledSized)
+ , mLock("TextureClientRecycleAllocatorImp.mLock")
+ , mIsDestroyed(false)
+{
+}
+
+TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ MOZ_ASSERT(mInUseClients.empty());
+}
+
+void
+TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
+{
+ mMaxPooledSize = aMax;
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
+ DefaultTextureClientAllocationHelper helper(this,
+ aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+ return CreateOrRecycle(helper);
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
+{
+ MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
+
+ RefPtr<TextureClientHolder> textureHolder;
+
+ {
+ MutexAutoLock lock(mLock);
+ if (mIsDestroyed) {
+ return nullptr;
+ }
+ if (!mPooledClients.empty()) {
+ textureHolder = mPooledClients.top();
+ mPooledClients.pop();
+ // If a pooled TextureClient is not compatible, release it.
+ if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
+ // Release TextureClient.
+ RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
+ textureHolder->ClearTextureClient();
+ textureHolder = nullptr;
+ mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
+ } else {
+ textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
+ }
+ }
+ }
+
+ if (!textureHolder) {
+ // Allocate new TextureClient
+ RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
+ if (!texture) {
+ return nullptr;
+ }
+ textureHolder = new TextureClientHolder(texture);
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
+ // Register TextureClient
+ mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
+ }
+ RefPtr<TextureClient> client(textureHolder->GetTextureClient());
+
+ // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
+ // ref count drops to 1.
+ client->SetRecycleAllocator(this);
+ return client.forget();
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
+ aSelector, aTextureFlags, aAllocFlags);
+}
+
+void
+TextureClientRecycleAllocator::ShrinkToMinimumSize()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ // We can not clear using TextureClients safely.
+ // Just clear WillRecycle here.
+ std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
+ for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
+ RefPtr<TextureClientHolder> holder = it->second;
+ holder->ClearWillRecycle();
+ }
+}
+
+void
+TextureClientRecycleAllocator::Destroy()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ mIsDestroyed = true;
+}
+
+void
+TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
+{
+ // Clearing the recycle allocator drops a reference, so make sure we stay alive
+ // for the duration of this function.
+ RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
+ aClient->SetRecycleAllocator(nullptr);
+
+ RefPtr<TextureClientHolder> textureHolder;
+ {
+ MutexAutoLock lock(mLock);
+ if (mInUseClients.find(aClient) != mInUseClients.end()) {
+ textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
+ if (textureHolder->WillRecycle() &&
+ !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
+ mPooledClients.push(textureHolder);
+ }
+ mInUseClients.erase(aClient);
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientRecycleAllocator.h b/gfx/layers/client/TextureClientRecycleAllocator.h
new file mode 100644
index 000000000..23ba78991
--- /dev/null
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+#define MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+
+#include <map>
+#include <stack>
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureClientHolder;
+struct PlanarYCbCrData;
+
+class ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~ITextureClientRecycleAllocator() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ITextureClientRecycleAllocator)
+
+protected:
+ friend class TextureClient;
+ virtual void RecycleTextureClient(TextureClient* aClient) = 0;
+};
+
+class ITextureClientAllocationHelper
+{
+public:
+ ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : mFormat(aFormat)
+ , mSize(aSize)
+ , mSelector(aSelector)
+ , mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag
+ , mAllocationFlags(aAllocationFlags)
+ {}
+
+ virtual already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) = 0;
+ virtual bool IsCompatible(TextureClient* aTextureClient) = 0;
+
+ const gfx::SurfaceFormat mFormat;
+ const gfx::IntSize mSize;
+ const BackendSelector mSelector;
+ const TextureFlags mTextureFlags;
+ const TextureAllocationFlags mAllocationFlags;
+};
+
+class YCbCrTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags);
+
+ bool IsCompatible(TextureClient* aTextureClient) override;
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override;
+
+protected:
+ const PlanarYCbCrData& mData;
+};
+
+
+/**
+ * TextureClientRecycleAllocator provides TextureClients allocation and
+ * recycling capabilities. It expects allocations of same sizes and
+ * attributres. If a recycled TextureClient is different from
+ * requested one, the recycled one is dropped and new TextureClient is allocated.
+ *
+ * By default this uses TextureClient::CreateForDrawing to allocate new texture
+ * clients.
+ */
+class TextureClientRecycleAllocator : public ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~TextureClientRecycleAllocator();
+
+public:
+ explicit TextureClientRecycleAllocator(KnowsCompositor* aAllocator);
+
+ void SetMaxPoolSize(uint32_t aMax);
+
+ // Creates and allocates a TextureClient.
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(ITextureClientAllocationHelper& aHelper);
+
+ void ShrinkToMinimumSize();
+
+ void Destroy();
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ RefPtr<KnowsCompositor> mSurfaceAllocator;
+
+ friend class DefaultTextureClientAllocationHelper;
+ void RecycleTextureClient(TextureClient* aClient) override;
+
+ static const uint32_t kMaxPooledSized = 2;
+ uint32_t mMaxPooledSize;
+
+ std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ // stack is good from Graphics cache usage point of view.
+ std::stack<RefPtr<TextureClientHolder> > mPooledClients;
+ Mutex mLock;
+ bool mIsDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */
diff --git a/gfx/layers/client/TextureClientSharedSurface.cpp b/gfx/layers/client/TextureClientSharedSurface.cpp
new file mode 100644
index 000000000..b0cbfec21
--- /dev/null
+++ b/gfx/layers/client/TextureClientSharedSurface.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureClientSharedSurface.h"
+
+#include "GLContext.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/Unused.h"
+#include "nsThreadUtils.h"
+#include "SharedSurface.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+
+SharedSurfaceTextureData::SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf)
+ : mSurf(Move(surf))
+{}
+
+SharedSurfaceTextureData::~SharedSurfaceTextureData()
+{}
+
+void
+SharedSurfaceTextureData::Deallocate(LayersIPCChannel*)
+{}
+
+void
+SharedSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSurf->mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+SharedSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ return mSurf->ToSurfaceDescriptor(&aOutDescriptor);
+}
+
+
+SharedSurfaceTextureClient::SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator)
+: TextureClient(aData, aFlags, aAllocator)
+{
+ mWorkaroundAnnoyingSharedSurfaceLifetimeIssues = true;
+}
+
+already_AddRefed<SharedSurfaceTextureClient>
+SharedSurfaceTextureClient::Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags)
+{
+ if (!surf) {
+ return nullptr;
+ }
+ TextureFlags flags = aFlags | TextureFlags::RECYCLE | surf->GetTextureFlags();
+ SharedSurfaceTextureData* data = new SharedSurfaceTextureData(Move(surf));
+ return MakeAndAddRef<SharedSurfaceTextureClient>(data, flags, aAllocator);
+}
+
+SharedSurfaceTextureClient::~SharedSurfaceTextureClient()
+{
+ // XXX - Things break when using the proper destruction handshake with
+ // SharedSurfaceTextureData because the TextureData outlives its gl
+ // context. Having a strong reference to the gl context creates a cycle.
+ // This needs to be fixed in a better way, though, because deleting
+ // the TextureData here can race with the compositor and cause flashing.
+ TextureData* data = mData;
+ mData = nullptr;
+
+ Destroy();
+
+ if (data) {
+ // Destroy mData right away without doing the proper deallocation handshake,
+ // because SharedSurface depends on things that may not outlive the texture's
+ // destructor so we can't wait until we know the compositor isn't using the
+ // texture anymore.
+ // It goes without saying that this is really bad and we should fix the bugs
+ // that block doing the right thing such as bug 1224199 sooner rather than
+ // later.
+ delete data;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientSharedSurface.h b/gfx/layers/client/TextureClientSharedSurface.h
new file mode 100644
index 000000000..cfe5085f8
--- /dev/null
+++ b/gfx/layers/client/TextureClientSharedSurface.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+#define MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+
+#include <cstddef> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLContextTypes.h" // for GLContext (ptr only), etc
+#include "TextureClient.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class SharedSurface;
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class SharedSurfaceTextureClient;
+
+class SharedSurfaceTextureData : public TextureData
+{
+protected:
+ const UniquePtr<gl::SharedSurface> mSurf;
+
+ friend class SharedSurfaceTextureClient;
+
+ explicit SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf);
+public:
+
+ ~SharedSurfaceTextureData();
+
+ virtual bool Lock(OpenMode) override { return false; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ gl::SharedSurface* Surf() const { return mSurf.get(); }
+};
+
+class SharedSurfaceTextureClient : public TextureClient
+{
+public:
+ SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator);
+
+ ~SharedSurfaceTextureClient();
+
+ static already_AddRefed<SharedSurfaceTextureClient>
+ Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags);
+
+ gl::SharedSurface* Surf() const {
+ return static_cast<const SharedSurfaceTextureData*>(GetInternalData())->Surf();
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
new file mode 100644
index 000000000..c5677a8a3
--- /dev/null
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -0,0 +1,1430 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/TiledContentClient.h"
+#include <math.h> // for ceil, ceilf, floor
+#include <algorithm>
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/MathAlgorithms.h" // for Abs
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Tools.h" // for BytesPerPixel
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "TextureClientPool.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsExpirationTracker.h" // for nsExpirationTracker
+#include "nsMathUtils.h" // for NS_lroundf
+#include "LayersLogging.h"
+#include "UnitTransforms.h" // for TransformTo
+#include "mozilla/UniquePtr.h"
+
+// This is the minimum area that we deem reasonable to copy from the front buffer to the
+// back buffer on tile updates. If the valid region is smaller than this, we just
+// redraw it and save on the copy (and requisite surface-locking involved).
+#define MINIMUM_TILE_COPY_AREA (1.f/16.f)
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+#include "cairo.h"
+#include <sstream>
+using mozilla::layers::Layer;
+static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height)
+{
+ gfxContext c(dt);
+
+ // Draw border
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(0, 0, width, height));
+ c.Stroke();
+
+ // Build tile description
+ std::stringstream ss;
+ ss << x << ", " << y;
+
+ // Draw text using cairo toy text API
+ // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
+ cairo_t* cr = gfxFont::RefCairo(dt);
+ cairo_set_font_size(cr, 25);
+ cairo_text_extents_t extents;
+ cairo_text_extents(cr, ss.str().c_str(), &extents);
+
+ int textWidth = extents.width + 6;
+
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Fill();
+
+ c.NewPath();
+ c.SetDeviceColor(Color(1.0, 0.0, 0.0));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Stroke();
+
+ c.NewPath();
+ cairo_move_to(cr, 4, 28);
+ cairo_show_text(cr, ss.str().c_str());
+
+}
+
+#endif
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+
+MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Multi")
+ , mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+ , mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+{
+ MOZ_COUNT_CTOR(MultiTiledContentClient);
+ mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
+ mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
+}
+
+void
+MultiTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+}
+
+void
+MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
+ ? &mLowPrecisionTiledBuffer
+ : &mTiledBuffer;
+
+ MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
+
+ mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
+ buffer->ClearPaintedRegion();
+}
+
+SharedFrameMetricsHelper::SharedFrameMetricsHelper()
+ : mLastProgressiveUpdateWasLowPrecision(false)
+ , mProgressiveUpdateWasInDanger(false)
+{
+ MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
+}
+
+SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
+{
+ MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
+}
+
+static inline bool
+FuzzyEquals(float a, float b) {
+ return (fabsf(a - b) < 1e-6);
+}
+
+static AsyncTransform
+ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aCompositorMetrics)
+{
+ // This is basically the same code as AsyncPanZoomController::GetCurrentAsyncTransform
+ // but with aContentMetrics used in place of mLastContentPaintMetrics, because they
+ // should be equivalent, modulo race conditions while transactions are inflight.
+
+ ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
+ * aCompositorMetrics.GetZoom();
+ return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
+}
+
+bool
+SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
+ const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform)
+{
+ MOZ_ASSERT(aLayer);
+
+ CompositorBridgeChild* compositor = nullptr;
+ if (aLayer.Manager() &&
+ aLayer.Manager()->AsClientLayerManager()) {
+ compositor = aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ const FrameMetrics& contentMetrics = aLayer.Metrics();
+ FrameMetrics compositorMetrics;
+
+ if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
+
+ // Reset the checkerboard risk flag when switching to low precision
+ // rendering.
+ if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
+ // Skip low precision rendering until we're at risk of checkerboarding.
+ if (!mProgressiveUpdateWasInDanger) {
+ TILING_LOG("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n");
+ return true;
+ }
+ mProgressiveUpdateWasInDanger = false;
+ }
+ mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
+
+ // Always abort updates if the resolution has changed. There's no use
+ // in drawing at the incorrect resolution.
+ if (!FuzzyEquals(compositorMetrics.GetZoom().xScale, contentMetrics.GetZoom().xScale) ||
+ !FuzzyEquals(compositorMetrics.GetZoom().yScale, contentMetrics.GetZoom().yScale)) {
+ TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
+ ToString(contentMetrics.GetZoom()).c_str(), ToString(compositorMetrics.GetZoom()).c_str());
+ return true;
+ }
+
+ // Never abort drawing if we can't be sure we've sent a more recent
+ // display-port. If we abort updating when we shouldn't, we can end up
+ // with blank regions on the screen and we open up the risk of entering
+ // an endless updating cycle.
+ if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
+ fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().x - compositorMetrics.GetDisplayPort().x) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().y - compositorMetrics.GetDisplayPort().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().width - compositorMetrics.GetDisplayPort().width) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().height - compositorMetrics.GetDisplayPort().height) <= 2) {
+ return false;
+ }
+
+ // When not a low precision pass and the page is in danger of checker boarding
+ // abort update.
+ if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
+ bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
+ contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
+ // If scrollUpdatePending is true, then that means the content-side
+ // metrics has a new scroll offset that is going to be forced into the
+ // compositor but it hasn't gotten there yet.
+ // Even though right now comparing the metrics might indicate we're
+ // about to checkerboard (and that's true), the checkerboarding will
+ // disappear as soon as the new scroll offset update is processed
+ // on the compositor side. To avoid leaving things in a low-precision
+ // paint, we need to detect and handle this case (bug 1026756).
+ if (!scrollUpdatePending && AboutToCheckerboard(contentMetrics, compositorMetrics)) {
+ mProgressiveUpdateWasInDanger = true;
+ return true;
+ }
+ }
+
+ // Abort drawing stale low-precision content if there's a more recent
+ // display-port in the pipeline.
+ if (aLowPrecision && !aHasPendingNewThebesContent) {
+ TILING_LOG("TILING: Aborting low-precision because of new pending content\n");
+ return true;
+ }
+
+ return false;
+}
+
+bool
+SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics)
+{
+ // The size of the painted area is originally computed in layer pixels in layout, but then
+ // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
+ // This process can introduce some rounding error, so we inflate the rect by one app unit
+ // to account for that.
+ CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
+ ? aContentMetrics.GetDisplayPort()
+ : aContentMetrics.GetCriticalDisplayPort())
+ + aContentMetrics.GetScrollOffset();
+ painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
+
+ // Inflate the rect by the danger zone. See the description of the danger zone prefs
+ // in AsyncPanZoomController.cpp for an explanation of this.
+ CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
+ aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
+ showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
+ / aCompositorMetrics.LayersPixelsPerCSSPixel());
+
+ // Clamp both rects to the scrollable rect, because having either of those
+ // exceed the scrollable rect doesn't make sense, and could lead to false
+ // positives.
+ painted = painted.Intersect(aContentMetrics.GetScrollableRect());
+ showing = showing.Intersect(aContentMetrics.GetScrollableRect());
+
+ if (!painted.Contains(showing)) {
+ TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
+ TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
+ return true;
+ }
+ return false;
+}
+
+ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mManager(aManager)
+ , mCallback(nullptr)
+ , mCallbackData(nullptr)
+ , mSharedFrameMetricsHelper(aHelper)
+{
+}
+
+bool
+ClientTiledLayerBuffer::HasFormatChanged() const
+{
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ return content != mLastPaintContentType ||
+ mode != mLastPaintSurfaceMode;
+}
+
+
+gfxContentType
+ClientTiledLayerBuffer::GetContentType(SurfaceMode* aMode) const
+{
+ gfxContentType content =
+ mPaintedLayer.CanUseOpaqueSurface() ? gfxContentType::COLOR :
+ gfxContentType::COLOR_ALPHA;
+ SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+ if (!mPaintedLayer.GetParent() ||
+ !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ content = gfxContentType::COLOR;
+ }
+#endif
+ } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ if (IsLowPrecision()) {
+ // If we're in low-res mode, drawing can sample from outside the visible
+ // region. Make sure that we only sample transparency if that happens.
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#else
+ if (mPaintedLayer.MayResample()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#endif
+ }
+
+ if (aMode) {
+ *aMode = mode;
+ }
+ return content;
+}
+
+class TileExpiry final : public nsExpirationTracker<TileClient, 3>
+{
+ public:
+ TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
+
+ static void AddTile(TileClient* aTile)
+ {
+ if (!sTileExpiry) {
+ sTileExpiry = MakeUnique<TileExpiry>();
+ }
+
+ sTileExpiry->AddObject(aTile);
+ }
+
+ static void RemoveTile(TileClient* aTile)
+ {
+ MOZ_ASSERT(sTileExpiry);
+ sTileExpiry->RemoveObject(aTile);
+ }
+
+ static void Shutdown() {
+ sTileExpiry = nullptr;
+ }
+ private:
+ virtual void NotifyExpired(TileClient* aTile) override
+ {
+ aTile->DiscardBackBuffer();
+ }
+
+ static UniquePtr<TileExpiry> sTileExpiry;
+};
+UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
+
+void ShutdownTileCache()
+{
+ TileExpiry::Shutdown();
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
+{
+ if (mBuffer) {
+ TileExpiry::RemoveTile(aContainer);
+ }
+ mBuffer = aNewValue;
+ if (mBuffer) {
+ TileExpiry::AddTile(aContainer);
+ }
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
+{
+ Set(aContainer, RefPtr<TextureClient>(aNewValue));
+}
+
+// Placeholder
+TileClient::TileClient()
+ : mWasPlaceholder(false)
+{
+}
+
+TileClient::~TileClient()
+{
+ if (mExpirationState.IsTracked()) {
+ MOZ_ASSERT(mBackBuffer);
+ TileExpiry::RemoveTile(this);
+ }
+}
+
+TileClient::TileClient(const TileClient& o)
+{
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+}
+
+TileClient&
+TileClient::operator=(const TileClient& o)
+{
+ if (this == &o) return *this;
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+ return *this;
+}
+
+void
+TileClient::Dump(std::stringstream& aStream)
+{
+ aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
+ if (mBackBufferOnWhite) {
+ aStream << " bbow=" << mBackBufferOnWhite.get();
+ }
+ if (mFrontBufferOnWhite) {
+ aStream << " fbow=" << mFrontBufferOnWhite.get();
+ }
+ aStream << ")";
+}
+
+void
+TileClient::Flip()
+{
+ RefPtr<TextureClient> frontBuffer = mFrontBuffer;
+ RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
+ mFrontBuffer = mBackBuffer;
+ mFrontBufferOnWhite = mBackBufferOnWhite;
+ mBackBuffer.Set(this, frontBuffer);
+ mBackBufferOnWhite = frontBufferOnWhite;
+ nsIntRegion invalidFront = mInvalidFront;
+ mInvalidFront = mInvalidBack;
+ mInvalidBack = invalidFront;
+}
+
+static bool
+CopyFrontToBack(TextureClient* aFront,
+ TextureClient* aBack,
+ const gfx::IntRect& aRectToCopy)
+{
+ TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
+ if (!frontLock.Succeeded()) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
+ return false;
+ }
+
+ if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
+ return false;
+ }
+
+ gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
+ aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+ return true;
+}
+
+void
+TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion)
+{
+ if (mBackBuffer && mFrontBuffer) {
+ gfx::IntSize tileSize = mFrontBuffer->GetSize();
+ const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
+
+ if (aDirtyRegion.Contains(tileRect)) {
+ // The dirty region means that we no longer need the front buffer, so
+ // discard it.
+ DiscardFrontBuffer();
+ } else {
+ // Region that needs copying.
+ nsIntRegion regionToCopy = mInvalidBack;
+
+ regionToCopy.Sub(regionToCopy, aDirtyRegion);
+
+ aAddPaintedRegion = regionToCopy;
+
+ if (regionToCopy.IsEmpty()) {
+ // Just redraw it all.
+ return;
+ }
+
+ // Copy the bounding rect of regionToCopy. As tiles are quite small, it
+ // is unlikely that we'd save much by copying each individual rect of the
+ // region, but we can reevaluate this if it becomes an issue.
+ const IntRect rectToCopy = regionToCopy.GetBounds();
+ gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height);
+ CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy);
+
+ if (mBackBufferOnWhite) {
+ MOZ_ASSERT(mFrontBufferOnWhite);
+ CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy);
+ }
+
+ mInvalidBack.SetEmpty();
+ }
+ }
+}
+
+void
+TileClient::DiscardFrontBuffer()
+{
+ if (mFrontBuffer) {
+ MOZ_ASSERT(mFrontBuffer->GetReadLock());
+
+ MOZ_ASSERT(mAllocator);
+ if (mAllocator) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
+ }
+ }
+
+ if (mFrontBuffer->IsLocked()) {
+ mFrontBuffer->Unlock();
+ }
+ if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
+ mFrontBufferOnWhite->Unlock();
+ }
+ mFrontBuffer = nullptr;
+ mFrontBufferOnWhite = nullptr;
+ }
+}
+
+static void
+DiscardTexture(TextureClient* aTexture, TextureClientAllocator* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ if (aTexture && aAllocator) {
+ MOZ_ASSERT(aTexture->GetReadLock());
+ if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ } else {
+ aAllocator->ReturnTextureClientDeferred(aTexture);
+ }
+ if (aTexture->IsLocked()) {
+ aTexture->Unlock();
+ }
+ }
+}
+
+void
+TileClient::DiscardBackBuffer()
+{
+ if (mBackBuffer) {
+ DiscardTexture(mBackBuffer, mAllocator);
+ mBackBuffer.Set(this, nullptr);
+ DiscardTexture(mBackBufferOnWhite, mAllocator);
+ mBackBufferOnWhite = nullptr;
+ }
+}
+
+static already_AddRefed<TextureClient>
+CreateBackBufferTexture(TextureClient* aCurrentTexture,
+ CompositableClient& aCompositable,
+ TextureClientAllocator* aAllocator)
+{
+ if (aCurrentTexture) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ }
+
+ RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
+
+ if (!texture) {
+ gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
+ return nullptr;
+ }
+
+ texture->EnableReadLock();
+
+ if (!aCompositable.AddTextureClient(texture)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
+ return nullptr;
+ }
+
+ return texture.forget();
+}
+
+TextureClient*
+TileClient::GetBackBuffer(CompositableClient& aCompositable,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent,
+ SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aBackBufferOnWhite)
+{
+ if (!mAllocator) {
+ gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
+ return nullptr;
+ }
+ if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ // It can happen that a component-alpha layer stops being on component alpha
+ // on the next frame, just drop the buffers on white if that happens.
+ if (mBackBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mBackBufferOnWhite = nullptr;
+ }
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mFrontBufferOnWhite = nullptr;
+ }
+ }
+
+ // Try to re-use the front-buffer if possible
+ if (mFrontBuffer &&
+ mFrontBuffer->HasIntermediateBuffer() &&
+ !mFrontBuffer->IsReadLocked() &&
+ (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
+ mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
+ // If we had a backbuffer we no longer care about it since we'll
+ // re-use the front buffer.
+ DiscardBackBuffer();
+ Flip();
+ } else {
+ if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
+ mBackBuffer.Set(this,
+ CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
+ );
+ if (!mBackBuffer) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
+ }
+
+ if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
+ && (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
+ mBackBufferOnWhite = CreateBackBufferTexture(
+ mBackBufferOnWhite, aCompositable, mAllocator
+ );
+ if (!mBackBufferOnWhite) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
+ }
+
+ ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
+ }
+
+ if (!mBackBuffer->IsLocked()) {
+ if (!mBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
+ if (!mBackBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ *aBackBufferOnWhite = mBackBufferOnWhite;
+ return mBackBuffer;
+}
+
+TileDescriptor
+TileClient::GetTileDescriptor()
+{
+ if (IsPlaceholderTile()) {
+ mWasPlaceholder = true;
+ return PlaceholderTileDescriptor();
+ }
+ bool wasPlaceholder = mWasPlaceholder;
+ mWasPlaceholder = false;
+
+ ReadLockDescriptor lock;
+ mFrontBuffer->SerializeReadLock(lock);
+
+ ReadLockDescriptor lockOnWhite = null_t();
+ if (mFrontBufferOnWhite) {
+ mFrontBufferOnWhite->SerializeReadLock(lockOnWhite);
+ }
+
+ return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+ mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+ mUpdateRect,
+ lock, lockOnWhite,
+ wasPlaceholder);
+}
+
+void
+ClientMultiTiledLayerBuffer::DiscardBuffers()
+{
+ for (TileClient& tile : mRetainedTiles) {
+ tile.DiscardBuffers();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ for (TileClient& tile : mRetainedTiles) {
+ TileDescriptor tileDesc = tile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ // Reset the update rect
+ tile.mUpdateRect = IntRect();
+ }
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTileOrigin, mTileSize,
+ mTiles.mFirst.x, mTiles.mFirst.y,
+ mTiles.mSize.width, mTiles.mSize.height,
+ mResolution, mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+void
+ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str());
+ TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str());
+
+ mCallback = aCallback;
+ mCallbackData = aCallbackData;
+ mWasLastPaintProgressive = aIsProgressive;
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ long start = PR_IntervalNow();
+#endif
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 30) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ if (aPaintRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ printf_stderr(" rect %i, %i, %i, %i\n",
+ rect.x, rect.y, rect.width, rect.height);
+ }
+ }
+ }
+ start = PR_IntervalNow();
+#endif
+
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "PaintThebesUpdate",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mNewValidRegion = aNewValidRegion;
+ Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 10) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+#endif
+
+ mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
+ mCallback = nullptr;
+ mCallbackData = nullptr;
+}
+
+void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion &region)
+{
+ struct LockedBits {
+ uint8_t *data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ static int clamp(int x, int min, int max)
+ {
+ if (x < min)
+ x = min;
+ if (x > max)
+ x = max;
+ return x;
+ }
+
+ static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
+ {
+ if (src + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long src memcpy");
+ }
+ if (src < bitmap) {
+ MOZ_CRASH("GFX: short src memcpy");
+ }
+ if (dst + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long dst mempcy");
+ }
+ if (dst < bitmap) {
+ MOZ_CRASH("GFX: short dst mempcy");
+ }
+ }
+
+ static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
+ LockedBits *lb = static_cast<LockedBits*>(closure);
+ uint8_t *bitmap = lb->data;
+ const int bpp = gfx::BytesPerPixel(lb->format);
+ const int stride = lb->stride;
+ const int width = lb->size.width;
+ const int height = lb->size.height;
+
+ if (side == VisitSide::TOP) {
+ if (y1 > 0) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ if (y1 < height) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::LEFT) {
+ if (x1 > 0) {
+ while (y1 != y2) {
+ memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ } else if (side == VisitSide::RIGHT) {
+ if (x1 < width) {
+ while (y1 != y2) {
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ }
+
+ }
+ } lb;
+
+ if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
+ // we can only pad software targets so if we can't lock the bits don't pad
+ region.VisitEdges(lb.visitor, &lb);
+ drawTarget->ReleaseBits(lb.data);
+ }
+}
+
+void
+ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
+{
+ // We locked the back buffer, and flipped so we now need to unlock the front
+ if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
+ aTile.mFrontBuffer->Unlock();
+ aTile.mFrontBuffer->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
+ aTile.mFrontBufferOnWhite->Unlock();
+ aTile.mFrontBufferOnWhite->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
+ aTile.mBackBuffer->Unlock();
+ }
+ if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
+ aTile.mBackBufferOnWhite->Unlock();
+ }
+}
+
+void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion)
+{
+ const IntSize scaledTileSize = GetScaledTileSize();
+ const gfx::IntRect newBounds = newValidRegion.GetBounds();
+
+ const TilesPlacement oldTiles = mTiles;
+ const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
+ floor_div(newBounds.y, scaledTileSize.height),
+ floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
+ + newBounds.width, scaledTileSize.width) + 1,
+ floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
+ + newBounds.height, scaledTileSize.height) + 1);
+
+ const size_t oldTileCount = mRetainedTiles.Length();
+ const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
+
+ nsTArray<TileClient> oldRetainedTiles;
+ mRetainedTiles.SwapElements(oldRetainedTiles);
+ mRetainedTiles.SetLength(newTileCount);
+
+ for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
+ const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
+ const size_t newIndex = newTiles.TileIndex(tilePosition);
+ // First, get the already existing tiles to the right place in the new array.
+ // Leave placeholders (default constructor) where there was no tile.
+ if (newTiles.HasTile(tilePosition)) {
+ mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
+ } else {
+ // release tiles that we are not going to reuse before allocating new ones
+ // to avoid allocating unnecessarily.
+ oldRetainedTiles[oldIndex].DiscardBuffers();
+ }
+ }
+
+ oldRetainedTiles.Clear();
+
+ if (!aPaintRegion.IsEmpty()) {
+ for (size_t i = 0; i < newTileCount; ++i) {
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ TileClient& tile = mRetainedTiles[i];
+ if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
+ gfxCriticalError() << "ValidateTile failed";
+ }
+ }
+
+ if (mMoz2DTiles.size() > 0) {
+ gfx::TileSet tileset;
+ for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
+ mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
+ }
+ tileset.mTiles = &mMoz2DTiles[0];
+ tileset.mTileCount = mMoz2DTiles.size();
+ RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+ if (!drawTarget || !drawTarget->IsValid()) {
+ gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
+ return;
+ }
+ drawTarget->SetTransform(Matrix());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+ MOZ_ASSERT(ctx); // already checked the draw target above
+ ctx->SetMatrix(
+ ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
+
+ mCallback(&mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+ DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+ mMoz2DTiles.clear();
+ // Reset:
+ mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ }
+
+ bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
+
+ for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
+ TileClient& tile = mRetainedTiles[i];
+
+ // Only worry about padding when not doing low-res because it simplifies
+ // the math and the artifacts won't be noticable
+ // Edge padding prevents sampling artifacts when compositing.
+ if (edgePaddingEnabled && mResolution == 1 &&
+ tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
+
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ // Strictly speakig we want the unscaled rect here, but it doesn't matter
+ // because we only run this code when the resolution is equal to 1.
+ IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
+ GetTileSize().width, GetTileSize().height);
+
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ nsIntRegion tileValidRegion = mValidRegion;
+ tileValidRegion.OrWith(tileDrawRegion);
+
+ // We only need to pad out if the tile has area that's not valid
+ if (!tileValidRegion.Contains(tileRect)) {
+ tileValidRegion = tileValidRegion.Intersect(tileRect);
+ // translate the region into tile space and pad
+ tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
+ RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
+ PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+ }
+ }
+ UnlockTile(tile);
+ }
+ }
+
+ mTiles = newTiles;
+ mValidRegion = newValidRegion;
+ mPaintedRegion.OrWith(aPaintRegion);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileOrigin,
+ const nsIntRegion& aDirtyRegion)
+{
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ValidateTile",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (aDirtyRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ }
+#endif
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+
+ if (!aTile.mAllocator) {
+ aTile.SetTextureAllocator(mManager->GetCompositorBridgeChild()->GetTexturePool(
+ mManager->AsShadowForwarder(),
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD));
+ MOZ_ASSERT(aTile.mAllocator);
+ }
+
+ nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
+ offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ aTile.GetBackBuffer(mCompositableClient,
+ offsetScaledDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(aTileOrigin);
+ extraPainted.And(extraPainted, mNewValidRegion);
+ mPaintedRegion.Or(mPaintedRegion, extraPainted);
+
+ if (!backBuffer) {
+ return false;
+ }
+
+ gfx::Tile moz2DTile;
+ RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ } else {
+ moz2DTile.mDrawTarget = dt;
+ }
+ moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+ if (!dt || (backBufferOnWhite && !dtOnWhite)) {
+ aTile.DiscardBuffers();
+ return false;
+ }
+
+ mMoz2DTiles.push_back(moz2DTile);
+ mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
+ mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
+
+ for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& dirtyRect = iter.Get();
+ gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
+ dirtyRect.y - aTileOrigin.y,
+ dirtyRect.width,
+ dirtyRect.height);
+ drawRect.Scale(mResolution);
+
+ // Mark the newly updated area as invalid in the front buffer
+ aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ } else if (content == gfxContentType::COLOR_ALPHA) {
+ dt->ClearRect(drawRect);
+ }
+ }
+
+ // The new buffer is now validated, remove the dirty region from it.
+ aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
+
+ aTile.Flip();
+
+ return true;
+}
+
+/**
+ * This function takes the transform stored in aTransformToCompBounds
+ * (which was generated in GetTransformToAncestorsParentLayer), and
+ * modifies it with the ViewTransform from the compositor side so that
+ * it reflects what the compositor is actually rendering. This operation
+ * basically adds in the layer's async transform.
+ * This function then returns the scroll ancestor's composition bounds,
+ * transformed into the painted layer's LayerPixel coordinates, accounting
+ * for the compositor state.
+ */
+static Maybe<LayerRect>
+GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor,
+ const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
+ const AsyncTransform& aAPZTransform,
+ const LayerRect& aClip)
+{
+ LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds *
+ AsyncTransformComponentMatrix(aAPZTransform);
+
+ return UntransformBy(transform.Inverse(),
+ aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated)
+{
+ aRegionToPaint = aInvalidRegion;
+
+ // If the composition bounds rect is empty, we can't make any sensible
+ // decision about how to update coherently. In this case, just update
+ // everything in one transaction.
+ if (aPaintData->mCompositionBounds.IsEmpty()) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ // If this is a low precision buffer, we force progressive updates. The
+ // assumption is that the contents is less important, so visual coherency
+ // is lower priority than speed.
+ bool drawingLowPrecision = IsLowPrecision();
+
+ // Find out if we have any non-stale content to update.
+ nsIntRegion staleRegion;
+ staleRegion.And(aInvalidRegion, aOldValidRegion);
+
+ TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer, Stringify(staleRegion).c_str());
+
+ LayerMetricsWrapper scrollAncestor;
+ mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+
+ // Find out the current view transform to determine which tiles to draw
+ // first, and see if we should just abort this paint. Aborting is usually
+ // caused by there being an incoming, more relevant paint.
+ AsyncTransform viewTransform;
+ MOZ_ASSERT(mSharedFrameMetricsHelper);
+
+ bool abortPaint =
+ mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
+ scrollAncestor,
+ !staleRegion.Contains(aInvalidRegion),
+ drawingLowPrecision,
+ viewTransform);
+
+ TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n",
+ &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint);
+
+ if (abortPaint) {
+ // We ignore if front-end wants to abort if this is the first,
+ // non-low-precision paint, as in that situation, we're about to override
+ // front-end's page/viewport metrics.
+ if (!aPaintData->mFirstPaint || drawingLowPrecision) {
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ComputeProgressiveUpdateRegion",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ aRegionToPaint.SetEmpty();
+ return aIsRepeated;
+ }
+ }
+
+ Maybe<LayerRect> transformedCompositionBounds =
+ GetCompositorSideCompositionBounds(scrollAncestor,
+ aPaintData->mTransformToCompBounds,
+ viewTransform,
+ ViewAs<LayerPixel>(Rect(mPaintedLayer.GetLayerBounds())));
+
+ if (!transformedCompositionBounds) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
+
+ // Compute a "coherent update rect" that we should paint all at once in a
+ // single transaction. This is to avoid rendering glitches on animated
+ // page content, and when layers change size/shape.
+ // On Fennec uploads are more expensive because we're not using gralloc, so
+ // we use a coherent update rect that is intersected with the screen at the
+ // time of issuing the draw command. This will paint faster but also potentially
+ // make the progressive paint more visible to the user while scrolling.
+ // On B2G uploads are cheaper and we value coherency more, especially outside
+ // the browser, so we always use the entire user-visible area.
+ IntRect coherentUpdateRect(RoundedOut(
+#ifdef MOZ_WIDGET_ANDROID
+ transformedCompositionBounds->Intersect(aPaintData->mCompositionBounds)
+#else
+ *transformedCompositionBounds
+#endif
+ ).ToUnknownRect());
+
+ TILING_LOG("TILING %p: Progressive update final coherency rect %s\n", &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
+
+ aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
+ aRegionToPaint.Or(aRegionToPaint, staleRegion);
+ bool drawingStale = !aRegionToPaint.IsEmpty();
+ if (!drawingStale) {
+ aRegionToPaint = aInvalidRegion;
+ }
+
+ // Prioritise tiles that are currently visible on the screen.
+ bool paintingVisible = false;
+ if (aRegionToPaint.Intersects(coherentUpdateRect)) {
+ aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
+ paintingVisible = true;
+ }
+
+ TILING_LOG("TILING %p: Progressive update final paint region %s\n", &mPaintedLayer, Stringify(aRegionToPaint).c_str());
+
+ // Paint area that's visible and overlaps previously valid content to avoid
+ // visible glitches in animated elements, such as gifs.
+ bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint);
+
+ TILING_LOG("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
+ &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction);
+
+ // The following code decides what order to draw tiles in, based on the
+ // current scroll direction of the primary scrollable layer.
+ NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
+ IntRect paintBounds = aRegionToPaint.GetBounds();
+
+ int startX, incX, startY, incY;
+ gfx::IntSize scaledTileSize = GetScaledTileSize();
+ if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
+ startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width);
+ incX = scaledTileSize.width;
+ } else {
+ startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
+ incX = -scaledTileSize.width;
+ }
+
+ if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
+ startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height);
+ incY = scaledTileSize.height;
+ } else {
+ startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
+ incY = -scaledTileSize.height;
+ }
+
+ // Find a tile to draw.
+ IntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height);
+ int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
+ int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
+ // This loop will always terminate, as there is at least one tile area
+ // along the first/last row/column intersecting with regionToPaint, or its
+ // bounds would have been smaller.
+ while (true) {
+ aRegionToPaint.And(aInvalidRegion, tileBounds);
+ if (!aRegionToPaint.IsEmpty()) {
+ if (mResolution != 1) {
+ // Paint the entire tile for low-res. This is aimed to fixing low-res resampling
+ // and to avoid doing costly region accurate painting for a small area.
+ aRegionToPaint = tileBounds;
+ }
+ break;
+ }
+ if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
+ tileBounds.x += incX;
+ } else {
+ tileBounds.y += incY;
+ }
+ }
+
+ if (!aRegionToPaint.Contains(aInvalidRegion)) {
+ // The region needed to paint is larger then our progressive chunk size
+ // therefore update what we want to paint and ask for a new paint transaction.
+
+ // If we need to draw more than one tile to maintain coherency, make
+ // sure it happens in the same transaction by requesting this work be
+ // repeated immediately.
+ // If this is unnecessary, the remaining work will be done tile-by-tile in
+ // subsequent transactions. The caller code is responsible for scheduling
+ // the subsequent transactions as long as we don't set the mPaintFinished
+ // flag to true.
+ return (!drawingLowPrecision && paintInSingleTransaction);
+ }
+
+ // We're not repeating painting and we've not requested a repeat transaction,
+ // so the paint is finished. If there's still a separate low precision
+ // paint to do, it will get marked as unfinished later.
+ aPaintData->mPaintFinished = true;
+ return false;
+}
+
+bool
+ClientMultiTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer, Stringify(aValidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update old valid region %s\n", &mPaintedLayer, Stringify(aOldValidRegion).c_str());
+
+ bool repeat = false;
+ bool isBufferChanged = false;
+ do {
+ // Compute the region that should be updated. Repeat as many times as
+ // is required.
+ nsIntRegion regionToPaint;
+ repeat = ComputeProgressiveUpdateRegion(aInvalidRegion,
+ aOldValidRegion,
+ regionToPaint,
+ aPaintData,
+ repeat);
+
+ TILING_LOG("TILING %p: Progressive update computed paint region %s repeat %d\n", &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
+
+ // There's no further work to be done.
+ if (regionToPaint.IsEmpty()) {
+ break;
+ }
+
+ isBufferChanged = true;
+
+ // Keep track of what we're about to refresh.
+ aValidRegion.Or(aValidRegion, regionToPaint);
+
+ // aValidRegion may have been altered by InvalidateRegion, but we still
+ // want to display stale content until it gets progressively updated.
+ // Create a region that includes stale content.
+ nsIntRegion validOrStale;
+ validOrStale.Or(aValidRegion, aOldValidRegion);
+
+ // Paint the computed region and subtract it from the invalid region.
+ PaintThebes(validOrStale, regionToPaint, aInvalidRegion,
+ aCallback, aCallbackData, true);
+ aInvalidRegion.Sub(aInvalidRegion, regionToPaint);
+ } while (repeat);
+
+ TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(aValidRegion).c_str(), isBufferChanged);
+ TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+
+ // Return false if nothing has been drawn, or give what has been drawn
+ // to the shadow layer to upload.
+ return isBufferChanged;
+}
+
+void
+TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+void
+TiledContentClient::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress)
+{
+ GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
+}
+
+void
+BasicTiledLayerPaintData::ResetPaintData()
+{
+ mLowPrecisionPaintCount = 0;
+ mPaintFinished = false;
+ mHasTransformAnimation = false;
+ mCompositionBounds.SetEmpty();
+ mCriticalDisplayPort = Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h
new file mode 100644
index 000000000..cf55450f8
--- /dev/null
+++ b/gfx/layers/client/TiledContentClient.h
@@ -0,0 +1,545 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_TILEDCONTENTCLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t
+#include <algorithm> // for swap
+#include <limits>
+#include "Layers.h" // for LayerManager, etc
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer
+#include "Units.h" // for CSSPoint
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersMessages.h" // for TileDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientPool.h"
+#include "ClientLayerManager.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for MOZ_COUNT_DTOR
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsExpirationTracker.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Represent a single tile in tiled buffer. The buffer keeps tiles,
+ * each tile keeps a reference to a texture client and a read-lock. This
+ * read-lock is used to help implement a copy-on-write mechanism. The tile
+ * should be locked before being sent to the compositor. The compositor should
+ * unlock the read-lock as soon as it has finished with the buffer in the
+ * TextureHost to prevent more textures being created than is necessary.
+ * Ideal place to store per tile debug information.
+ */
+struct TileClient
+{
+ // Placeholder
+ TileClient();
+ ~TileClient();
+
+ TileClient(const TileClient& o);
+
+ TileClient& operator=(const TileClient& o);
+
+ bool operator== (const TileClient& o) const
+ {
+ return mFrontBuffer == o.mFrontBuffer;
+ }
+
+ bool operator!= (const TileClient& o) const
+ {
+ return mFrontBuffer != o.mFrontBuffer;
+ }
+
+ void SetTextureAllocator(TextureClientAllocator* aAllocator)
+ {
+ mAllocator = aAllocator;
+ }
+
+ bool IsPlaceholderTile() const
+ {
+ return mBackBuffer == nullptr && mFrontBuffer == nullptr;
+ }
+
+ void DiscardBuffers()
+ {
+ DiscardFrontBuffer();
+ DiscardBackBuffer();
+ }
+
+ nsExpirationState *GetExpirationState() { return &mExpirationState; }
+
+ TileDescriptor GetTileDescriptor();
+
+ /**
+ * For debugging.
+ */
+ void Dump(std::stringstream& aStream);
+
+ /**
+ * Swaps the front and back buffers.
+ */
+ void Flip();
+
+ void DumpTexture(std::stringstream& aStream, TextureDumpMode aCompress) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ CompositableClient::DumpTextureClient(aStream, mFrontBuffer, aCompress);
+ }
+
+ /**
+ * Returns an unlocked TextureClient that can be used for writing new
+ * data to the tile. This may flip the front-buffer to the back-buffer if
+ * the front-buffer is still locked by the host, or does not have an
+ * internal buffer (and so will always be locked).
+ *
+ * If getting the back buffer required copying pixels from the front buffer
+ * then the copied region is stored in aAddPaintedRegion so the host side
+ * knows to upload it.
+ *
+ * If nullptr is returned, aTextureClientOnWhite is undefined.
+ */
+ TextureClient* GetBackBuffer(CompositableClient&,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent, SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aTextureClientOnWhite);
+
+ void DiscardFrontBuffer();
+
+ void DiscardBackBuffer();
+
+ /* We wrap the back buffer in a class that disallows assignment
+ * so that we can track when ever it changes so that we can update
+ * the expiry tracker for expiring the back buffers */
+ class PrivateProtector {
+ public:
+ void Set(TileClient * container, RefPtr<TextureClient>);
+ void Set(TileClient * container, TextureClient*);
+ // Implicitly convert to TextureClient* because we can't chain
+ // implicit conversion that would happen on RefPtr<TextureClient>
+ operator TextureClient*() const { return mBuffer; }
+ RefPtr<TextureClient> operator ->() { return mBuffer; }
+ private:
+ PrivateProtector& operator=(const PrivateProtector &);
+ RefPtr<TextureClient> mBuffer;
+ } mBackBuffer;
+ RefPtr<TextureClient> mBackBufferOnWhite;
+ RefPtr<TextureClient> mFrontBuffer;
+ RefPtr<TextureClient> mFrontBufferOnWhite;
+ RefPtr<TextureClientAllocator> mAllocator;
+ gfx::IntRect mUpdateRect;
+ bool mWasPlaceholder;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ TimeStamp mLastUpdate;
+#endif
+ nsIntRegion mInvalidFront;
+ nsIntRegion mInvalidBack;
+ nsExpirationState mExpirationState;
+private:
+ // Copies dirty pixels from the front buffer into the back buffer,
+ // and records the copied region in aAddPaintedRegion.
+ void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion);
+};
+
+/**
+ * This struct stores all the data necessary to perform a paint so that it
+ * doesn't need to be recalculated on every repeated transaction.
+ */
+struct BasicTiledLayerPaintData {
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set.
+ */
+ ParentLayerPoint mScrollOffset;
+
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set, for the last
+ * layer update transaction.
+ */
+ ParentLayerPoint mLastScrollOffset;
+
+ /*
+ * The transform matrix to go from this layer's Layer units to
+ * the scroll ancestor's ParentLayer units. The "scroll ancestor" is
+ * the closest ancestor layer which scrolls, and is used to obtain
+ * the composition bounds that are relevant for this layer.
+ */
+ LayerToParentLayerMatrix4x4 mTransformToCompBounds;
+
+ /*
+ * The critical displayport of the content from the nearest ancestor layer
+ * that represents scrollable content with a display port set. isNothing()
+ * if a critical displayport is not set.
+ */
+ Maybe<LayerIntRect> mCriticalDisplayPort;
+
+ /*
+ * The render resolution of the document that the content this layer
+ * represents is in.
+ */
+ CSSToParentLayerScale2D mResolution;
+
+ /*
+ * The composition bounds of the layer, in Layer coordinates. This is
+ * used to make sure that tiled updates to regions that are visible to the
+ * user are grouped coherently.
+ */
+ LayerRect mCompositionBounds;
+
+ /*
+ * Low precision updates are always executed a tile at a time in repeated
+ * transactions. This counter is set to 1 on the first transaction of a low
+ * precision update, and incremented for each subsequent transaction.
+ */
+ uint16_t mLowPrecisionPaintCount;
+
+ /*
+ * Whether this is the first time this layer is painting
+ */
+ bool mFirstPaint : 1;
+
+ /*
+ * Whether there is further work to complete this paint. This is used to
+ * determine whether or not to repeat the transaction when painting
+ * progressively.
+ */
+ bool mPaintFinished : 1;
+
+ /*
+ * Whether or not there is an async transform animation active
+ */
+ bool mHasTransformAnimation : 1;
+
+ /*
+ * Initializes/clears data to prepare for paint action.
+ */
+ void ResetPaintData();
+};
+
+class SharedFrameMetricsHelper
+{
+public:
+ SharedFrameMetricsHelper();
+ ~SharedFrameMetricsHelper();
+
+ /**
+ * This is called by the BasicTileLayer to determine if it is still interested
+ * in the update of this display-port to continue. We can return true here
+ * to abort the current update and continue with any subsequent ones. This
+ * is useful for slow-to-render pages when the display-port starts lagging
+ * behind enough that continuing to draw it is wasted effort.
+ */
+ bool UpdateFromCompositorFrameMetrics(const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform);
+
+ /**
+ * Determines if the compositor's upcoming composition bounds has fallen
+ * outside of the contents display port. If it has then the compositor
+ * will start to checker board. Checker boarding is when the compositor
+ * tries to composite a tile and it is not available. Historically
+ * a tile with a checker board pattern was used. Now a blank tile is used.
+ */
+ bool AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics);
+private:
+ bool mLastProgressiveUpdateWasLowPrecision;
+ bool mProgressiveUpdateWasInDanger;
+};
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientTiledLayerBuffer
+{
+public:
+ ClientTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient)
+ : mPaintedLayer(aPaintedLayer)
+ , mCompositableClient(aCompositableClient)
+ , mLastPaintContentType(gfxContentType::COLOR)
+ , mLastPaintSurfaceMode(SurfaceMode::SURFACE_OPAQUE)
+ , mWasLastPaintProgressive(false)
+ {}
+
+ virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) = 0;
+
+ virtual bool SupportsProgressiveUpdate() = 0;
+ virtual bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) = 0;
+ virtual void ResetPaintedAndValidState() = 0;
+
+ virtual const nsIntRegion& GetValidRegion() = 0;
+
+ virtual bool IsLowPrecision() const = 0;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) {}
+
+ const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
+ void SetFrameResolution(const CSSToParentLayerScale2D& aResolution) { mFrameResolution = aResolution; }
+
+ bool HasFormatChanged() const;
+
+protected:
+ void UnlockTile(TileClient& aTile);
+ gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
+
+ ClientTiledPaintedLayer& mPaintedLayer;
+ CompositableClient& mCompositableClient;
+
+ gfxContentType mLastPaintContentType;
+ SurfaceMode mLastPaintSurfaceMode;
+ CSSToParentLayerScale2D mFrameResolution;
+
+ bool mWasLastPaintProgressive;
+};
+
+class ClientMultiTiledLayerBuffer
+ : public TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>
+ , public ClientTiledLayerBuffer
+{
+ friend class TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>;
+public:
+ ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper);
+
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ virtual bool SupportsProgressiveUpdate() override { return true; }
+ /**
+ * Performs a progressive update of a given tiled buffer.
+ * See ComputeProgressiveUpdateRegion below for parameter documentation.
+ */
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTiles.mSize.width = 0;
+ mTiles.mSize.height = 0;
+ DiscardBuffers();
+ mRetainedTiles.Clear();
+ }
+
+
+ const nsIntRegion& GetValidRegion() override {
+ return TiledLayerBuffer::GetValidRegion();
+ }
+
+ bool IsLowPrecision() const override {
+ return TiledLayerBuffer::IsLowPrecision();
+ }
+
+ void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) override {
+ TiledLayerBuffer::Dump(aStream, aPrefix, aDumpHtml, aCompress);
+ }
+
+ void ReadLock();
+
+ void Release();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void SetResolution(float aResolution) {
+ if (mResolution == aResolution) {
+ return;
+ }
+
+ Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
+ mResolution = aResolution;
+ }
+
+protected:
+ bool ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileRect,
+ const nsIntRegion& dirtyRect);
+
+ void Update(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion);
+
+ TileClient GetPlaceholderTile() const { return TileClient(); }
+
+private:
+ RefPtr<ClientLayerManager> mManager;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+
+ // The region that will be made valid during Update(). Once Update() is
+ // completed then this is identical to mValidRegion.
+ nsIntRegion mNewValidRegion;
+
+ SharedFrameMetricsHelper* mSharedFrameMetricsHelper;
+ // When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
+ std::vector<gfx::Tile> mMoz2DTiles;
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ /**
+ * Calculates the region to update in a single progressive update transaction.
+ * This employs some heuristics to update the most 'sensible' region to
+ * update at this point in time, and how large an update should be performed
+ * at once to maintain visual coherency.
+ *
+ * aInvalidRegion is the current invalid region.
+ * aOldValidRegion is the valid region of mTiledBuffer at the beginning of the
+ * current transaction.
+ * aRegionToPaint will be filled with the region to update. This may be empty,
+ * which indicates that there is no more work to do.
+ * aIsRepeated should be true if this function has already been called during
+ * this transaction.
+ *
+ * Returns true if it should be called again, false otherwise. In the case
+ * that aRegionToPaint is empty, this will return aIsRepeated for convenience.
+ */
+ bool ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated);
+};
+
+class TiledContentClient : public CompositableClient
+{
+public:
+ TiledContentClient(ClientLayerManager* aManager,
+ const char* aName = "")
+ : CompositableClient(aManager->AsShadowForwarder())
+ , mName(aName)
+ {}
+
+protected:
+ ~TiledContentClient()
+ {}
+
+public:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_TILED);
+ }
+
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() = 0;
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() = 0;
+
+ enum TiledBufferType {
+ TILED_BUFFER,
+ LOW_PRECISION_TILED_BUFFER
+ };
+ virtual void UpdatedBuffer(TiledBufferType aType) = 0;
+
+private:
+ const char* mName;
+};
+
+/**
+ * An implementation of TiledContentClient that supports
+ * multiple tiles and a low precision buffer.
+ */
+class MultiTiledContentClient : public TiledContentClient
+{
+public:
+ MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~MultiTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(MultiTiledContentClient);
+
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+ }
+
+public:
+ void ClearCachedResources() override;
+ void UpdatedBuffer(TiledBufferType aType) override;
+
+ ClientTiledLayerBuffer* GetTiledBuffer() override { return &mTiledBuffer; }
+ ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override {
+ if (mHasLowPrecision) {
+ return &mLowPrecisionTiledBuffer;
+ }
+ return nullptr;
+ }
+
+private:
+ SharedFrameMetricsHelper mSharedFrameMetricsHelper;
+ ClientMultiTiledLayerBuffer mTiledBuffer;
+ ClientMultiTiledLayerBuffer mLowPrecisionTiledBuffer;
+ bool mHasLowPrecision;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp
new file mode 100644
index 000000000..69eafceca
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -0,0 +1,1495 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include <stdint.h> // for uint32_t
+#include "apz/src/AsyncPanZoomController.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "gfxPoint.h" // for gfxPoint, gfxSize
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
+#include "mozilla/dom/KeyframeEffectReadOnly.h"
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
+#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
+#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels, etc
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsDeviceContext.h" // for nsDeviceContext
+#include "nsDisplayList.h" // for nsDisplayTransform, etc
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "UnitTransforms.h" // for TransformTo
+#include "gfxPrefs.h"
+#if defined(MOZ_WIDGET_ANDROID)
+# include <android/log.h>
+# include "mozilla/widget/AndroidCompositorWidget.h"
+#endif
+#include "GeckoProfiler.h"
+#include "FrameUniformityData.h"
+#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch
+#include "VsyncSource.h"
+
+struct nsCSSValueSharedList;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static bool
+IsSameDimension(dom::ScreenOrientationInternal o1, dom::ScreenOrientationInternal o2)
+{
+ bool isO1portrait = (o1 == dom::eScreenOrientation_PortraitPrimary || o1 == dom::eScreenOrientation_PortraitSecondary);
+ bool isO2portrait = (o2 == dom::eScreenOrientation_PortraitPrimary || o2 == dom::eScreenOrientation_PortraitSecondary);
+ return !(isO1portrait ^ isO2portrait);
+}
+
+static bool
+ContentMightReflowOnOrientationChange(const IntRect& rect)
+{
+ return rect.width != rect.height;
+}
+
+AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager)
+ : mLayerManager(aManager)
+ , mIsFirstPaint(true)
+ , mLayersUpdated(false)
+ , mPaintSyncId(0)
+ , mReadyForCompose(true)
+{
+}
+
+AsyncCompositionManager::~AsyncCompositionManager()
+{
+}
+
+void
+AsyncCompositionManager::ResolveRefLayers(CompositorBridgeParent* aCompositor,
+ bool* aHasRemoteContent,
+ bool* aResolvePlugins)
+{
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = false;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If valid *aResolvePlugins indicates if we need to update plugin geometry
+ // when we walk the tree.
+ bool resolvePlugins = (aCompositor && aResolvePlugins && *aResolvePlugins);
+#endif
+
+ if (!mLayerManager->GetRoot()) {
+ // Updated the return value since this result controls completing composition.
+ if (aResolvePlugins) {
+ *aResolvePlugins = false;
+ }
+ return;
+ }
+
+ mReadyForCompose = true;
+ bool hasRemoteContent = false;
+ bool didResolvePlugins = false;
+
+ ForEachNode<ForwardIterator>(
+ mLayerManager->GetRoot(),
+ [&](Layer* layer)
+ {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ hasRemoteContent = true;
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (!referent) {
+ return;
+ }
+
+ if (!refLayer->GetLocalVisibleRegion().IsEmpty()) {
+ dom::ScreenOrientationInternal chromeOrientation =
+ mTargetConfig.orientation();
+ dom::ScreenOrientationInternal contentOrientation =
+ state->mTargetConfig.orientation();
+ if (!IsSameDimension(chromeOrientation, contentOrientation) &&
+ ContentMightReflowOnOrientationChange(mTargetConfig.naturalBounds())) {
+ mReadyForCompose = false;
+ }
+ }
+
+ refLayer->ConnectReferentLayer(referent);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (resolvePlugins) {
+ didResolvePlugins |=
+ aCompositor->UpdatePluginWindowState(refLayer->GetReferentId());
+ }
+#endif
+ });
+
+ if (aHasRemoteContent) {
+ *aHasRemoteContent = hasRemoteContent;
+ }
+ if (aResolvePlugins) {
+ *aResolvePlugins = didResolvePlugins;
+ }
+}
+
+void
+AsyncCompositionManager::DetachRefLayers()
+{
+ if (!mLayerManager->GetRoot()) {
+ return;
+ }
+
+ mReadyForCompose = false;
+
+ ForEachNodePostOrder<ForwardIterator>(mLayerManager->GetRoot(),
+ [&](Layer* layer)
+ {
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return;
+ }
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(refLayer->GetReferentId());
+ if (!state) {
+ return;
+ }
+
+ Layer* referent = state->mRoot;
+ if (referent) {
+ refLayer->DetachReferentLayer(referent);
+ }
+ });
+}
+
+void
+AsyncCompositionManager::ComputeRotation()
+{
+ if (!mTargetConfig.naturalBounds().IsEmpty()) {
+ mWorldTransform =
+ ComputeTransformForRotation(mTargetConfig.naturalBounds(),
+ mTargetConfig.rotation());
+ }
+}
+
+#ifdef DEBUG
+static void
+GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform)
+{
+ // Start with the animated transform if there is one
+ *aTransform =
+ (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation()
+ ? aLayer->GetLocalTransform()
+ : aLayer->GetTransform());
+}
+#endif
+
+static void
+TransformClipRect(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+ if (clipRect) {
+ ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
+ aLayer->AsLayerComposite()->SetShadowClipRect(Some(transformed));
+ }
+}
+
+// Similar to TransformFixedClip(), but only transforms the fixed part of the
+// clip.
+static void
+TransformFixedClip(Layer* aLayer,
+ const ParentLayerToParentLayerMatrix4x4& aTransform,
+ AsyncCompositionManager::ClipParts& aClipParts)
+{
+ MOZ_ASSERT(aTransform.Is2D());
+ if (aClipParts.mFixedClip) {
+ *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
+ aLayer->AsLayerComposite()->SetShadowClipRect(aClipParts.Intersect());
+ }
+}
+
+/**
+ * Set the given transform as the shadow transform on the layer, assuming
+ * that the given transform already has the pre- and post-scales applied.
+ * That is, this function cancels out the pre- and post-scales from aTransform
+ * before setting it as the shadow transform on the layer, so that when
+ * the layer's effective transform is computed, the pre- and post-scales will
+ * only be applied once.
+ */
+static void
+SetShadowTransform(Layer* aLayer, LayerToParentLayerMatrix4x4 aTransform)
+{
+ if (ContainerLayer* c = aLayer->AsContainerLayer()) {
+ aTransform.PreScale(1.0f / c->GetPreXScale(),
+ 1.0f / c->GetPreYScale(),
+ 1);
+ }
+ aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
+ 1.0f / aLayer->GetPostYScale(),
+ 1);
+ aLayer->AsLayerComposite()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
+}
+
+static void
+TranslateShadowLayer(Layer* aLayer,
+ const ParentLayerPoint& aTranslation,
+ bool aAdjustClipRect,
+ AsyncCompositionManager::ClipPartsCache* aClipPartsCache)
+{
+ // This layer might also be a scrollable layer and have an async transform.
+ // To make sure we don't clobber that, we start with the shadow transform.
+ // (i.e. GetLocalTransform() instead of GetTransform()).
+ // Note that the shadow transform is reset on every frame of composition so
+ // we don't have to worry about the adjustments compounding over successive
+ // frames.
+ LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
+
+ // Apply the translation to the layer transform.
+ layerTransform.PostTranslate(aTranslation);
+
+ SetShadowTransform(aLayer, layerTransform);
+ aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
+
+ if (aAdjustClipRect) {
+ auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
+ // If we're passed a clip parts cache, only transform the fixed part of
+ // the clip.
+ if (aClipPartsCache) {
+ auto iter = aClipPartsCache->find(aLayer);
+ MOZ_ASSERT(iter != aClipPartsCache->end());
+ TransformFixedClip(aLayer, transform, iter->second);
+ } else {
+ TransformClipRect(aLayer, transform);
+ }
+
+ // If a fixed- or sticky-position layer has a mask layer, that mask should
+ // move along with the layer, so apply the translation to the mask layer too.
+ if (Layer* maskLayer = aLayer->GetMaskLayer()) {
+ TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
+ }
+ }
+}
+
+#ifdef DEBUG
+static void
+AccumulateLayerTransforms(Layer* aLayer,
+ Layer* aAncestor,
+ Matrix4x4& aMatrix)
+{
+ // Accumulate the transforms between this layer and the subtree root layer.
+ for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
+ Matrix4x4 transform;
+ GetBaseTransform(l, &transform);
+ aMatrix *= transform;
+ }
+}
+#endif
+
+static LayerPoint
+GetLayerFixedMarginsOffset(Layer* aLayer,
+ const ScreenMargin& aFixedLayerMargins)
+{
+ // Work out the necessary translation, in root scrollable layer space.
+ // Because fixed layer margins are stored relative to the root scrollable
+ // layer, we can just take the difference between these values.
+ LayerPoint translation;
+ int32_t sides = aLayer->GetFixedPositionSides();
+
+ if ((sides & eSideBitsLeftRight) == eSideBitsLeftRight) {
+ translation.x += (aFixedLayerMargins.left - aFixedLayerMargins.right) / 2;
+ } else if (sides & eSideBitsRight) {
+ translation.x -= aFixedLayerMargins.right;
+ } else if (sides & eSideBitsLeft) {
+ translation.x += aFixedLayerMargins.left;
+ }
+
+ if ((sides & eSideBitsTopBottom) == eSideBitsTopBottom) {
+ translation.y += (aFixedLayerMargins.top - aFixedLayerMargins.bottom) / 2;
+ } else if (sides & eSideBitsBottom) {
+ translation.y -= aFixedLayerMargins.bottom;
+ } else if (sides & eSideBitsTop) {
+ translation.y += aFixedLayerMargins.top;
+ }
+
+ return translation;
+}
+
+static gfxFloat
+IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
+{
+ // Determine the amount of overlap between the 1D vector |aTranslation|
+ // and the interval [aMin, aMax].
+ if (aTranslation > 0) {
+ return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
+ } else {
+ return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
+ }
+}
+
+/**
+ * Finds the metrics on |aLayer| with scroll id |aScrollId|, and returns a
+ * LayerMetricsWrapper representing the (layer, metrics) pair, or the null
+ * LayerMetricsWrapper if no matching metrics could be found.
+ */
+static LayerMetricsWrapper
+FindMetricsWithScrollId(Layer* aLayer, FrameMetrics::ViewID aScrollId)
+{
+ for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) {
+ if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
+ return LayerMetricsWrapper(aLayer, i);
+ }
+ }
+ return LayerMetricsWrapper();
+}
+
+/**
+ * Checks whether the (layer, metrics) pair (aTransformedLayer, aTransformedMetrics)
+ * is on the path from |aFixedLayer| to the metrics with scroll id
+ * |aFixedWithRespectTo|, inclusive.
+ */
+static bool
+AsyncTransformShouldBeUnapplied(Layer* aFixedLayer,
+ FrameMetrics::ViewID aFixedWithRespectTo,
+ Layer* aTransformedLayer,
+ FrameMetrics::ViewID aTransformedMetrics)
+{
+ LayerMetricsWrapper transformed = FindMetricsWithScrollId(aTransformedLayer, aTransformedMetrics);
+ if (!transformed.IsValid()) {
+ return false;
+ }
+ // It's important to start at the bottom, because the fixed layer itself
+ // could have the transformed metrics, and they can be at the bottom.
+ LayerMetricsWrapper current(aFixedLayer, LayerMetricsWrapper::StartAt::BOTTOM);
+ bool encounteredTransformedLayer = false;
+ // The transformed layer is on the path from |aFixedLayer| to the fixed-to
+ // layer if as we walk up the (layer, metrics) tree starting from
+ // |aFixedLayer|, we *first* encounter the transformed layer, and *then* (or
+ // at the same time) the fixed-to layer.
+ while (current) {
+ if (!encounteredTransformedLayer && current == transformed) {
+ encounteredTransformedLayer = true;
+ }
+ if (current.Metrics().GetScrollId() == aFixedWithRespectTo) {
+ return encounteredTransformedLayer;
+ }
+ current = current.GetParent();
+ // It's possible that we reach a layers id boundary before we reach an
+ // ancestor with the scroll id |aFixedWithRespectTo| (this could happen
+ // e.g. if the scroll frame with that scroll id uses containerless
+ // scrolling). In such a case, stop the walk, as a new layers id could
+ // have a different layer with scroll id |aFixedWithRespectTo| which we
+ // don't intend to match.
+ if (current && current.AsRefLayer() != nullptr) {
+ break;
+ }
+ }
+ return false;
+}
+
+// If |aLayer| is fixed or sticky, returns the scroll id of the scroll frame
+// that it's fixed or sticky to. Otherwise, returns Nothing().
+static Maybe<FrameMetrics::ViewID>
+IsFixedOrSticky(Layer* aLayer)
+{
+ bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
+ !aLayer->GetParent()->GetIsFixedPosition();
+ if (isRootOfFixedSubtree) {
+ return Some(aLayer->GetFixedPositionScrollContainerId());
+ }
+ if (aLayer->GetIsStickyPosition()) {
+ return Some(aLayer->GetStickyScrollContainerId());
+ }
+ return Nothing();
+}
+
+void
+AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+ Layer* aStartTraversalAt,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache)
+{
+ // We're going to be inverting |aCurrentTransformForRoot|.
+ // If it's singular, there's nothing we can do.
+ if (aCurrentTransformForRoot.IsSingular()) {
+ return;
+ }
+
+ Layer* layer = aStartTraversalAt;
+ bool needsAsyncTransformUnapplied = false;
+ if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
+ needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(layer,
+ *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
+ }
+
+ // We want to process all the fixed and sticky descendants of
+ // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
+ // need to recurse any deeper because the adjustment to the fixed or sticky
+ // layer will apply to its subtree.
+ if (!needsAsyncTransformUnapplied) {
+ for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
+ aTransformScrollId, aPreviousTransformForRoot,
+ aCurrentTransformForRoot, aFixedLayerMargins, aClipPartsCache);
+ }
+ return;
+ }
+
+ // Insert a translation so that the position of the anchor point is the same
+ // before and after the change to the transform of aTransformedSubtreeRoot.
+
+ // A transform creates a containing block for fixed-position descendants,
+ // so there shouldn't be a transform in between the fixed layer and
+ // the subtree root layer.
+#ifdef DEBUG
+ Matrix4x4 ancestorTransform;
+ if (layer != aTransformedSubtreeRoot) {
+ AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
+ ancestorTransform);
+ }
+ ancestorTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(ancestorTransform.IsIdentity());
+#endif
+
+ // Since we create container layers for fixed layers, there shouldn't
+ // a local CSS or OMTA transform on the fixed layer, either (any local
+ // transform would go onto a descendant layer inside the container
+ // layer).
+#ifdef DEBUG
+ Matrix4x4 localTransform;
+ GetBaseTransform(layer, &localTransform);
+ localTransform.NudgeToIntegersFixedEpsilon();
+ MOZ_ASSERT(localTransform.IsIdentity());
+#endif
+
+ // Now work out the translation necessary to make sure the layer doesn't
+ // move given the new sub-tree root transform.
+
+ // Get the layer's fixed anchor point, in the layer's local coordinate space
+ // (before any transform is applied).
+ LayerPoint anchor = layer->GetFixedPositionAnchor();
+
+ // Offset the layer's anchor point to make sure fixed position content
+ // respects content document fixed position margins.
+ LayerPoint offsetAnchor = anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
+
+ // Additionally transform the anchor to compensate for the change
+ // from the old transform to the new transform. We do
+ // this by using the old transform to take the offset anchor back into
+ // subtree root space, and then the inverse of the new transform
+ // to bring it back to layer space.
+ ParentLayerPoint offsetAnchorInSubtreeRootSpace =
+ aPreviousTransformForRoot.TransformPoint(offsetAnchor);
+ LayerPoint transformedAnchor = aCurrentTransformForRoot.Inverse()
+ .TransformPoint(offsetAnchorInSubtreeRootSpace);
+
+ // We want to translate the layer by the difference between
+ // |transformedAnchor| and |anchor|.
+ LayerPoint translation = transformedAnchor - anchor;
+
+ // A fixed layer will "consume" (be unadjusted by) the entire translation
+ // calculated above. A sticky layer may consume all, part, or none of it,
+ // depending on where we are relative to its sticky scroll range.
+ // The remainder of the translation (the unconsumed portion) needs to
+ // be propagated to descendant fixed/sticky layers.
+ LayerPoint unconsumedTranslation;
+
+ if (layer->GetIsStickyPosition()) {
+ // For sticky positioned layers, the difference between the two rectangles
+ // defines a pair of translation intervals in each dimension through which
+ // the layer should not move relative to the scroll container. To
+ // accomplish this, we limit each dimension of the |translation| to that
+ // part of it which overlaps those intervals.
+ const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
+ const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
+
+ LayerPoint originalTranslation = translation;
+ translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
+ IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
+ translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
+ IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
+ unconsumedTranslation = translation - originalTranslation;
+ }
+
+ // Finally, apply the translation to the layer transform. Note that in cases
+ // where the async transform on |aTransformedSubtreeRoot| affects this layer's
+ // clip rect, we need to apply the same translation to said clip rect, so
+ // that the effective transform on the clip rect takes it back to where it was
+ // originally, had there been no async scroll.
+ TranslateShadowLayer(layer, ViewAs<ParentLayerPixel>(translation,
+ PixelCastJustification::NoTransformOnLayer), true, aClipPartsCache);
+
+ // Propragate the unconsumed portion of the translation to descendant
+ // fixed/sticky layers.
+ if (unconsumedTranslation != LayerPoint()) {
+ // Take the computations we performed to derive |translation| from
+ // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
+ // quantities fixed, to come up with a new transform |newTransform| that
+ // would produce |unconsumedTranslation|.
+ LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
+ ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
+ aPreviousTransformForRoot.TransformPoint(newTransformedAnchor);
+ LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
+ newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
+ offsetAnchorInSubtreeRootSpace);
+
+ // Propagate this new transform to our descendants as the new value of
+ // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
+ // translation.
+ for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+ AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child, aTransformScrollId,
+ aPreviousTransformForRoot, newTransform, aFixedLayerMargins, aClipPartsCache);
+ }
+ }
+
+ return;
+}
+
+static void
+SampleValue(float aPortion, Animation& aAnimation,
+ const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
+ const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
+ Animatable* aValue, Layer* aLayer)
+{
+ NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
+ aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
+ aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
+ "Must have same unit");
+
+ StyleAnimationValue startValue = aStart;
+ StyleAnimationValue endValue = aEnd;
+ // Iteration composition for accumulate
+ if (static_cast<dom::IterationCompositeOperation>
+ (aAnimation.iterationComposite()) ==
+ dom::IterationCompositeOperation::Accumulate &&
+ aCurrentIteration > 0) {
+ // FIXME: Bug 1293492: Add a utility function to calculate both of
+ // below StyleAnimationValues.
+ DebugOnly<bool> accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ startValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ endValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ }
+
+ StyleAnimationValue interpolatedValue;
+ // This should never fail because we only pass transform and opacity values
+ // to the compositor and they should never fail to interpolate.
+ DebugOnly<bool> uncomputeResult =
+ StyleAnimationValue::Interpolate(aAnimation.property(),
+ startValue, endValue,
+ aPortion, interpolatedValue);
+ MOZ_ASSERT(uncomputeResult, "could not uncompute value");
+
+ if (aAnimation.property() == eCSSProperty_opacity) {
+ *aValue = interpolatedValue.GetFloatValue();
+ return;
+ }
+
+ nsCSSValueSharedList* interpolatedList =
+ interpolatedValue.GetCSSValueSharedListValue();
+
+ TransformData& data = aAnimation.data().get_TransformData();
+ nsPoint origin = data.origin();
+ // we expect all our transform data to arrive in device pixels
+ Point3D transformOrigin = data.transformOrigin();
+ nsDisplayTransform::FrameTransformProperties props(interpolatedList,
+ transformOrigin);
+
+ // If our parent layer is a perspective layer, then the offset into reference
+ // frame coordinates is already on that layer. If not, then we need to ask
+ // for it to be added here.
+ uint32_t flags = 0;
+ if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
+ flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
+ }
+
+ Matrix4x4 transform =
+ nsDisplayTransform::GetResultingTransformMatrix(props, origin,
+ data.appUnitsPerDevPixel(),
+ flags, &data.bounds());
+
+ InfallibleTArray<TransformFunction> functions;
+ functions.AppendElement(TransformMatrix(transform));
+ *aValue = functions;
+}
+
+static bool
+SampleAnimations(Layer* aLayer, TimeStamp aPoint)
+{
+ bool activeAnimations = false;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&activeAnimations, &aPoint] (Layer* layer)
+ {
+ AnimationArray& animations = layer->GetAnimations();
+ InfallibleTArray<AnimData>& animationData = layer->GetAnimationData();
+
+ // Process in order, since later animations override earlier ones.
+ for (size_t i = 0, iEnd = animations.Length(); i < iEnd; ++i) {
+ Animation& animation = animations[i];
+ AnimData& animData = animationData[i];
+
+ activeAnimations = true;
+
+ MOZ_ASSERT(!animation.startTime().IsNull(),
+ "Failed to resolve start time of pending animations");
+ TimeDuration elapsedDuration =
+ (aPoint - animation.startTime()).MultDouble(animation.playbackRate());
+ TimingParams timing;
+ timing.mDuration.emplace(animation.duration());
+ timing.mDelay = animation.delay();
+ timing.mIterations = animation.iterations();
+ timing.mIterationStart = animation.iterationStart();
+ timing.mDirection =
+ static_cast<dom::PlaybackDirection>(animation.direction());
+ timing.mFill = static_cast<dom::FillMode>(animation.fillMode());
+ timing.mFunction =
+ AnimationUtils::TimingFunctionToComputedTimingFunction(
+ animation.easingFunction());
+
+ ComputedTiming computedTiming =
+ dom::AnimationEffectReadOnly::GetComputedTimingAt(
+ Nullable<TimeDuration>(elapsedDuration), timing,
+ animation.playbackRate());
+
+ if (computedTiming.mProgress.IsNull()) {
+ continue;
+ }
+
+ uint32_t segmentIndex = 0;
+ size_t segmentSize = animation.segments().Length();
+ AnimationSegment* segment = animation.segments().Elements();
+ while (segment->endPortion() < computedTiming.mProgress.Value() &&
+ segmentIndex < segmentSize - 1) {
+ ++segment;
+ ++segmentIndex;
+ }
+
+ double positionInSegment =
+ (computedTiming.mProgress.Value() - segment->startPortion()) /
+ (segment->endPortion() - segment->startPortion());
+
+ double portion =
+ ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
+ positionInSegment,
+ computedTiming.mBeforeFlag);
+
+ // interpolate the property
+ Animatable interpolatedValue;
+ SampleValue(portion, animation,
+ animData.mStartValues[segmentIndex],
+ animData.mEndValues[segmentIndex],
+ animData.mEndValues.LastElement(),
+ computedTiming.mCurrentIteration,
+ &interpolatedValue, layer);
+ LayerComposite* layerComposite = layer->AsLayerComposite();
+ switch (animation.property()) {
+ case eCSSProperty_opacity:
+ {
+ layerComposite->SetShadowOpacity(interpolatedValue.get_float());
+ layerComposite->SetShadowOpacitySetByAnimation(true);
+ break;
+ }
+ case eCSSProperty_transform:
+ {
+ Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ matrix.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
+ }
+ layerComposite->SetShadowBaseTransform(matrix);
+ layerComposite->SetShadowTransformSetByAnimation(true);
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled animated property");
+ }
+ }
+ });
+ return activeAnimations;
+}
+
+static bool
+SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime)
+{
+ bool activeAnimations = false;
+
+ ForEachNodePostOrder<ForwardIterator>(aLayer,
+ [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics)
+ {
+ if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
+ apzc->ReportCheckerboard(aSampleTime);
+ activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
+ }
+ }
+ );
+
+ return activeAnimations;
+}
+
+void
+AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer)
+{
+ MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ ForEachNodePostOrder<ForwardIterator>(
+ aLayer,
+ [this] (Layer* layer)
+ {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
+ if (!apzc) {
+ continue;
+ }
+ gfx::Matrix4x4 shadowTransform = layer->AsLayerComposite()->GetShadowBaseTransform();
+ if (!shadowTransform.Is2D()) {
+ continue;
+ }
+
+ Matrix transform = shadowTransform.As2D();
+ if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
+ Point translation = transform.GetTranslation();
+ mLayerTransformRecorder.RecordTransform(layer, translation);
+ return;
+ }
+ }
+ });
+}
+
+static AsyncTransformComponentMatrix
+AdjustForClip(const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer)
+{
+ AsyncTransformComponentMatrix result = asyncTransform;
+
+ // Container layers start at the origin, but they are clipped to where they
+ // actually have content on the screen. The tree transform is meant to apply
+ // to the clipped area. If the tree transform includes a scale component,
+ // then applying it to container as-is will produce incorrect results. To
+ // avoid this, translate the layer so that the clip rect starts at the origin,
+ // apply the tree transform, and translate back.
+ if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
+ if (shadowClipRect->TopLeft() != ParentLayerIntPoint()) { // avoid a gratuitous change of basis
+ result.ChangeBasis(shadowClipRect->x, shadowClipRect->y, 0);
+ }
+ }
+ return result;
+}
+
+static void
+ExpandRootClipRect(Layer* aLayer, const ScreenMargin& aFixedLayerMargins)
+{
+ // For Fennec we want to expand the root scrollable layer clip rect based on
+ // the fixed position margins. In particular, we want this while the dynamic
+ // toolbar is in the process of sliding offscreen and the area of the
+ // LayerView visible to the user is larger than the viewport size that Gecko
+ // knows about (and therefore larger than the clip rect). We could also just
+ // clear the clip rect on aLayer entirely but this seems more precise.
+ Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+ if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
+#ifndef MOZ_WIDGET_ANDROID
+ // We should never enter here on anything other than Fennec, since
+ // aFixedLayerMargins should be empty everywhere else.
+ MOZ_ASSERT(false);
+#endif
+ ParentLayerRect rect(rootClipRect.value());
+ rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
+ PixelCastJustification::ScreenIsParentLayerForRoot));
+ aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
+ }
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+static void
+MoveScrollbarForLayerMargin(Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
+ const ScreenMargin& aFixedLayerMargins)
+{
+ // See bug 1223928 comment 9 - once we can detect the RCD with just the
+ // isRootContent flag on the metrics, we can probably move this code into
+ // ApplyAsyncTransformToScrollbar rather than having it as a separate
+ // adjustment on the layer tree.
+ Layer* scrollbar = BreadthFirstSearch<ReverseIterator>(aRoot,
+ [aRootScrollId](Layer* aNode) {
+ return (aNode->GetScrollbarDirection() == Layer::HORIZONTAL &&
+ aNode->GetScrollbarTargetContainerId() == aRootScrollId);
+ });
+ if (scrollbar) {
+ // Shift the horizontal scrollbar down into the new space exposed by the
+ // dynamic toolbar hiding. Technically we should also scale the vertical
+ // scrollbar a bit to expand into the new space but it's not as noticeable
+ // and it would add a lot more complexity, so we're going with the "it's not
+ // worth it" justification.
+ TranslateShadowLayer(scrollbar, ParentLayerPoint(0, -aFixedLayerMargins.bottom), true, nullptr);
+ if (scrollbar->GetParent()) {
+ // The layer that has the HORIZONTAL direction sits inside another
+ // ContainerLayer. This ContainerLayer also has a clip rect that causes
+ // the scrollbar to get clipped. We need to expand that clip rect to
+ // prevent that from happening. This is kind of ugly in that we're
+ // assuming a particular layer tree structure but short of adding more
+ // flags to the layer there doesn't appear to be a good way to do this.
+ ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
+ }
+ }
+}
+#endif
+
+bool
+AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
+ bool* aOutFoundRoot)
+{
+ bool appliedTransform = false;
+ std::stack<Maybe<ParentLayerIntRect>> stackDeferredClips;
+
+ // Maps layers to their ClipParts. The parts are not stored individually
+ // on the layer, but during AlignFixedAndStickyLayers we need access to
+ // the individual parts for descendant layers.
+ ClipPartsCache clipPartsCache;
+
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [&stackDeferredClips] (Layer* layer)
+ {
+ stackDeferredClips.push(Maybe<ParentLayerIntRect>());
+ },
+ [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform, &clipPartsCache] (Layer* layer)
+ {
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren = stackDeferredClips.top();
+ stackDeferredClips.pop();
+ LayerToParentLayerMatrix4x4 oldTransform = layer->GetTransformTyped() *
+ AsyncTransformMatrix();
+
+ AsyncTransformComponentMatrix combinedAsyncTransform;
+ bool hasAsyncTransform = false;
+ ScreenMargin fixedLayerMargins;
+
+ // Each layer has multiple clips:
+ // - Its local clip, which is fixed to the layer contents, i.e. it moves
+ // with those async transforms which the layer contents move with.
+ // - Its scrolled clip, which moves with all async transforms.
+ // - For each ScrollMetadata on the layer, a scroll clip. This includes
+ // the composition bounds and any other clips induced by layout. This
+ // moves with async transforms from ScrollMetadatas above it.
+ // In this function, these clips are combined into two shadow clip parts:
+ // - The fixed clip, which consists of the local clip only, initially
+ // transformed by all async transforms.
+ // - The scrolled clip, which consists of the other clips, transformed by
+ // the appropriate transforms.
+ // These two parts are kept separate for now, because for fixed layers, we
+ // need to adjust the fixed clip (to cancel out some async transforms).
+ // The parts are kept in a cache which is cleared at the beginning of every
+ // composite.
+ // The final shadow clip for the layer is the intersection of the (possibly
+ // adjusted) fixed clip and the scrolled clip.
+ ClipParts& clipParts = clipPartsCache[layer];
+ clipParts.mFixedClip = layer->GetClipRect();
+ clipParts.mScrolledClip = layer->GetScrolledClipRect();
+
+ // If we are a perspective transform ContainerLayer, apply the clip deferred
+ // from our child (if there is any) before we iterate over our frame metrics,
+ // because this clip is subject to all async transforms of this layer.
+ // Since this clip came from the a scroll clip on the child, it becomes part
+ // of our scrolled clip.
+ clipParts.mScrolledClip = IntersectMaybeRects(
+ clipDeferredFromChildren, clipParts.mScrolledClip);
+
+ // The transform of a mask layer is relative to the masked layer's parent
+ // layer. So whenever we apply an async transform to a layer, we need to
+ // apply that same transform to the layer's own mask layer.
+ // A layer can also have "ancestor" mask layers for any rounded clips from
+ // its ancestor scroll frames. A scroll frame mask layer only needs to be
+ // async transformed for async scrolls of this scroll frame's ancestor
+ // scroll frames, not for async scrolls of this scroll frame itself.
+ // In the loop below, we iterate over scroll frames from inside to outside.
+ // At each iteration, this array contains the layer's ancestor mask layers
+ // of all scroll frames inside the current one.
+ nsTArray<Layer*> ancestorMaskLayers;
+
+ // The layer's scrolled clip can have an ancestor mask layer as well,
+ // which is moved by all async scrolls on this layer.
+ if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
+ if (scrolledClip->GetMaskLayerIndex()) {
+ ancestorMaskLayers.AppendElement(
+ layer->GetAncestorMaskLayerAt(*scrolledClip->GetMaskLayerIndex()));
+ }
+ }
+
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ AsyncPanZoomController* controller = layer->GetAsyncPanZoomController(i);
+ if (!controller) {
+ continue;
+ }
+
+ hasAsyncTransform = true;
+
+ AsyncTransform asyncTransformWithoutOverscroll =
+ controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix overscrollTransform =
+ controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ AsyncTransformComponentMatrix asyncTransform =
+ AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
+ * overscrollTransform;
+
+ if (!layer->IsScrollInfoLayer()) {
+ controller->MarkAsyncTransformAppliedToContent();
+ }
+
+ const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
+ const FrameMetrics& metrics = scrollMetadata.GetMetrics();
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // If we find a metrics which is the root content doc, use that. If not, use
+ // the root layer. Since this function recurses on children first we should
+ // only end up using the root layer if the entire tree was devoid of a
+ // root content metrics. This is a temporary solution; in the long term we
+ // should not need the root content metrics at all. See bug 1201529 comment
+ // 6 for details.
+ if (!(*aOutFoundRoot)) {
+ *aOutFoundRoot = metrics.IsRootContent() || /* RCD */
+ (layer->GetParent() == nullptr && /* rootmost metrics */
+ i + 1 >= layer->GetScrollMetadataCount());
+ if (*aOutFoundRoot) {
+ mRootScrollableId = metrics.GetScrollId();
+ CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
+ if (mIsFirstPaint) {
+ LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
+ mContentRect = metrics.GetScrollableRect();
+ SetFirstPaintViewport(scrollOffsetLayerPixels,
+ geckoZoom,
+ mContentRect);
+ } else {
+ ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(
+ AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ // Compute the painted displayport in document-relative CSS pixels.
+ CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
+ metrics.GetDisplayPort() :
+ metrics.GetCriticalDisplayPort());
+ displayPort += metrics.GetScrollOffset();
+ SyncFrameMetrics(scrollOffset,
+ geckoZoom * asyncTransformWithoutOverscroll.mScale,
+ metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
+ mPaintSyncId, fixedLayerMargins);
+ mFixedLayerMargins = fixedLayerMargins;
+ mLayersUpdated = false;
+ mPaintSyncId = 0;
+ }
+ mIsFirstPaint = false;
+ }
+ }
+#else
+ // Non-Android platforms still care about this flag being cleared after
+ // the first call to TransformShadowTree().
+ mIsFirstPaint = false;
+#endif
+
+ // Transform the current local clips by this APZC's async transform. If we're
+ // using containerful scrolling, then the clip is not part of the scrolled
+ // frame and should not be transformed.
+ if (!scrollMetadata.UsesContainerScrolling()) {
+ MOZ_ASSERT(asyncTransform.Is2D());
+ if (clipParts.mFixedClip) {
+ *clipParts.mFixedClip = TransformBy(asyncTransform, *clipParts.mFixedClip);
+ }
+ if (clipParts.mScrolledClip) {
+ *clipParts.mScrolledClip = TransformBy(asyncTransform, *clipParts.mScrolledClip);
+ }
+ }
+ // Note: we don't set the layer's shadow clip rect property yet;
+ // AlignFixedAndStickyLayers will use the clip parts from the clip parts
+ // cache.
+
+ combinedAsyncTransform *= asyncTransform;
+
+ // For the purpose of aligning fixed and sticky layers, we disregard
+ // the overscroll transform as well as any OMTA transform when computing the
+ // 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
+ // and OMTA transforms are not unapplied, and therefore that the visual
+ // effects apply to fixed and sticky layers. We do this by using
+ // GetTransform() as the base transform rather than GetLocalTransform(),
+ // which would include those factors.
+ LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
+ layer->GetTransformTyped()
+ * CompleteAsyncTransform(
+ AdjustForClip(asyncTransformWithoutOverscroll, layer));
+
+ AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(), oldTransform,
+ transformWithoutOverscrollOrOmta, fixedLayerMargins,
+ &clipPartsCache);
+
+ // Combine the local clip with the ancestor scrollframe clip. This is not
+ // included in the async transform above, since the ancestor clip should not
+ // move with this APZC.
+ if (scrollMetadata.HasScrollClip()) {
+ ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
+ if (layer->GetParent() && layer->GetParent()->GetTransformIsPerspective()) {
+ // If our parent layer has a perspective transform, we want to apply
+ // our scroll clip to it instead of to this layer (see bug 1168263).
+ // A layer with a perspective transform shouldn't have multiple
+ // children with FrameMetrics, nor a child with multiple FrameMetrics.
+ // (A child with multiple FrameMetrics would mean that there's *another*
+ // scrollable element between the one with the CSS perspective and the
+ // transformed element. But you'd have to use preserve-3d on the inner
+ // scrollable element in order to have the perspective apply to the
+ // transformed child, and preserve-3d is not supported on scrollable
+ // elements, so this case can't occur.)
+ MOZ_ASSERT(!stackDeferredClips.top());
+ stackDeferredClips.top().emplace(clip);
+ } else {
+ clipParts.mScrolledClip = IntersectMaybeRects(Some(clip),
+ clipParts.mScrolledClip);
+ }
+ }
+
+ // Do the same for the ancestor mask layers: ancestorMaskLayers contains
+ // the ancestor mask layers for scroll frames *inside* the current scroll
+ // frame, so these are the ones we need to shift by our async transform.
+ for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
+ SetShadowTransform(ancestorMaskLayer,
+ ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
+ }
+
+ // Append the ancestor mask layer for this scroll frame to ancestorMaskLayers.
+ if (scrollMetadata.HasScrollClip()) {
+ const LayerClip& scrollClip = scrollMetadata.ScrollClip();
+ if (scrollClip.GetMaskLayerIndex()) {
+ size_t maskLayerIndex = scrollClip.GetMaskLayerIndex().value();
+ Layer* ancestorMaskLayer = layer->GetAncestorMaskLayerAt(maskLayerIndex);
+ ancestorMaskLayers.AppendElement(ancestorMaskLayer);
+ }
+ }
+ }
+
+ bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
+ layer->GetScrolledClipRect());
+ if (clipChanged) {
+ // Intersect the two clip parts and apply them to the layer.
+ // During ApplyAsyncContentTransformTree on an ancestor layer,
+ // AlignFixedAndStickyLayers may overwrite this with a new clip it
+ // computes from the clip parts, but if that doesn't happen, this
+ // is the layer's final clip rect.
+ layer->AsLayerComposite()->SetShadowClipRect(clipParts.Intersect());
+ }
+
+ if (hasAsyncTransform) {
+ // Apply the APZ transform on top of GetLocalTransform() here (rather than
+ // GetTransform()) in case the OMTA code in SampleAnimations already set a
+ // shadow transform; in that case we want to apply ours on top of that one
+ // rather than clobber it.
+ SetShadowTransform(layer,
+ layer->GetLocalTransformTyped()
+ * AdjustForClip(combinedAsyncTransform, layer));
+
+ // Do the same for the layer's own mask layer, if it has one.
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowTransform(maskLayer,
+ maskLayer->GetLocalTransformTyped() * combinedAsyncTransform);
+ }
+
+ appliedTransform = true;
+ }
+
+ ExpandRootClipRect(layer, fixedLayerMargins);
+
+ if (layer->GetScrollbarDirection() != Layer::NONE) {
+ ApplyAsyncTransformToScrollbar(layer);
+ }
+ });
+
+ return appliedTransform;
+}
+
+static bool
+LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
+{
+ AsyncPanZoomController* apzc = aTarget.GetApzc();
+ if (!apzc) {
+ return false;
+ }
+ const FrameMetrics& metrics = aTarget.Metrics();
+ if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
+ return false;
+ }
+ return !aTarget.IsScrollInfoLayer();
+}
+
+static void
+ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
+ const LayerMetricsWrapper& aContent,
+ bool aScrollbarIsDescendant)
+{
+ // We only apply the transform if the scroll-target layer has non-container
+ // children (i.e. when it has some possibly-visible content). This is to
+ // avoid moving scroll-bars in the situation that only a scroll information
+ // layer has been built for a scroll frame, as this would result in a
+ // disparity between scrollbars and visible content.
+ if (aContent.IsScrollInfoLayer()) {
+ return;
+ }
+
+ const FrameMetrics& metrics = aContent.Metrics();
+ AsyncPanZoomController* apzc = aContent.GetApzc();
+ MOZ_RELEASE_ASSERT(apzc);
+
+ AsyncTransformComponentMatrix asyncTransform =
+ apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // |asyncTransform| represents the amount by which we have scrolled and
+ // zoomed since the last paint. Because the scrollbar was sized and positioned based
+ // on the painted content, we need to adjust it based on asyncTransform so that
+ // it reflects what the user is actually seeing now.
+ AsyncTransformComponentMatrix scrollbarTransform;
+ if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
+ const ParentLayerCoord asyncScrollY = asyncTransform._42;
+ const float asyncZoomY = asyncTransform._22;
+
+ // The scroll thumb needs to be scaled in the direction of scrolling by the
+ // inverse of the async zoom. This is because zooming in decreases the
+ // fraction of the whole srollable rect that is in view.
+ const float yScale = 1.f / asyncZoomY;
+
+ // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().yScale * asyncZoomY);
+
+ // Here we convert the scrollbar thumb ratio into a true unitless ratio by
+ // dividing out the conversion factor from the scrollframe's parent's space
+ // to the scrollframe's space.
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomY);
+ // The scroll thumb needs to be translated in opposite direction of the
+ // async scroll. This is because scrolling down, which translates the layer
+ // content up, should result in moving the scroll thumb down.
+ ParentLayerCoord yTranslation = -asyncScrollY * ratio;
+
+ // The scroll thumb additionally needs to be translated to compensate for
+ // the scale applied above. The origin with respect to which the scale is
+ // applied is the origin of the entire scrollbar, rather than the origin of
+ // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
+ // the composition bounds). This means that empty space above the thumb
+ // is scaled too, effectively translating the thumb. We undo that
+ // translation here.
+ // (One can think of the adjustment being done to the translation here as
+ // a change of basis. We have a method to help with that,
+ // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
+ // cleaner in this case).
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().y * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ yTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ // Scrollbar for the root are painted at the same resolution as the
+ // content. Since the coordinate space we apply this transform in includes
+ // the resolution, we need to adjust for it as well here. Note that in
+ // another metrics.IsRootContent() hunk below we apply a
+ // resolution-cancelling transform which ensures the scroll thumb isn't
+ // actually rendered at a larger scale.
+ yTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(1.f, yScale, 1.f);
+ scrollbarTransform.PostTranslate(0, yTranslation, 0);
+ }
+ if (aScrollbar->GetScrollbarDirection() == Layer::HORIZONTAL) {
+ // See detailed comments under the VERTICAL case.
+
+ const ParentLayerCoord asyncScrollX = asyncTransform._41;
+ const float asyncZoomX = asyncTransform._11;
+
+ const float xScale = 1.f / asyncZoomX;
+
+ const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().xScale * asyncZoomX);
+
+ const float ratio = aScrollbar->GetScrollbarThumbRatio() /
+ (metrics.GetPresShellResolution() * asyncZoomX);
+ ParentLayerCoord xTranslation = -asyncScrollX * ratio;
+
+ const CSSCoord thumbOrigin = (metrics.GetScrollOffset().x * ratio);
+ const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
+ const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
+ const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
+ xTranslation -= thumbOriginDeltaPL;
+
+ if (metrics.IsRootContent()) {
+ xTranslation *= metrics.GetPresShellResolution();
+ }
+
+ scrollbarTransform.PostScale(xScale, 1.f, 1.f);
+ scrollbarTransform.PostTranslate(xTranslation, 0, 0);
+ }
+
+ LayerToParentLayerMatrix4x4 transform =
+ aScrollbar->GetLocalTransformTyped() * scrollbarTransform;
+
+ AsyncTransformComponentMatrix compensation;
+ // If the scrollbar layer is for the root then the content's resolution
+ // applies to the scrollbar as well. Since we don't actually want the scroll
+ // thumb's size to vary with the zoom (other than its length reflecting the
+ // fraction of the scrollable length that's in view, which is taken care of
+ // above), we apply a transform to cancel out this resolution.
+ if (metrics.IsRootContent()) {
+ compensation =
+ AsyncTransformComponentMatrix::Scaling(
+ metrics.GetPresShellResolution(),
+ metrics.GetPresShellResolution(),
+ 1.0f).Inverse();
+ }
+ // If the scrollbar layer is a child of the content it is a scrollbar for,
+ // then we need to adjust for any async transform (including an overscroll
+ // transform) on the content. This needs to be cancelled out because layout
+ // positions and sizes the scrollbar on the assumption that there is no async
+ // transform, and without this adjustment the scrollbar will end up in the
+ // wrong place.
+ //
+ // Note that since the async transform is applied on top of the content's
+ // regular transform, we need to make sure to unapply the async transform in
+ // the same coordinate space. This requires applying the content transform
+ // and then unapplying it after unapplying the async transform.
+ if (aScrollbarIsDescendant) {
+ AsyncTransformComponentMatrix overscroll =
+ apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
+ Matrix4x4 contentTransform = aContent.GetTransform();
+ Matrix4x4 contentUntransform = contentTransform.Inverse();
+
+ AsyncTransformComponentMatrix asyncCompensation =
+ ViewAs<AsyncTransformComponentMatrix>(
+ contentTransform
+ * asyncUntransform
+ * contentUntransform);
+
+ compensation = compensation * asyncCompensation;
+
+ // We also need to make a corresponding change on the clip rect of all the
+ // layers on the ancestor chain from the scrollbar layer up to but not
+ // including the layer with the async transform. Otherwise the scrollbar
+ // shifts but gets clipped and so appears to flicker.
+ for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
+ TransformClipRect(ancestor, asyncCompensation);
+ }
+ }
+ transform = transform * compensation;
+
+ SetShadowTransform(aScrollbar, transform);
+}
+
+static LayerMetricsWrapper
+FindScrolledLayerForScrollbar(Layer* aScrollbar, bool* aOutIsAncestor)
+{
+ // First check if the scrolled layer is an ancestor of the scrollbar layer.
+ LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
+ LayerMetricsWrapper prevAncestor(aScrollbar);
+ LayerMetricsWrapper scrolledLayer;
+
+ for (LayerMetricsWrapper ancestor(aScrollbar); ancestor; ancestor = ancestor.GetParent()) {
+ // Don't walk into remote layer trees; the scrollbar will always be in
+ // the same layer space.
+ if (ancestor.AsRefLayer()) {
+ root = prevAncestor;
+ break;
+ }
+ prevAncestor = ancestor;
+
+ if (LayerIsScrollbarTarget(ancestor, aScrollbar)) {
+ *aOutIsAncestor = true;
+ return ancestor;
+ }
+ }
+
+ // Search the entire layer space of the scrollbar.
+ ForEachNode<ForwardIterator>(
+ root,
+ [&root, &scrolledLayer, &aScrollbar](LayerMetricsWrapper aLayerMetrics)
+ {
+ // Do not recurse into RefLayers, since our initial aSubtreeRoot is the
+ // root (or RefLayer root) of a single layer space to search.
+ if (root != aLayerMetrics && aLayerMetrics.AsRefLayer()) {
+ return TraversalFlag::Skip;
+ }
+ if (LayerIsScrollbarTarget(aLayerMetrics, aScrollbar)) {
+ scrolledLayer = aLayerMetrics;
+ return TraversalFlag::Abort;
+ }
+ return TraversalFlag::Continue;
+ }
+ );
+ return scrolledLayer;
+}
+
+void
+AsyncCompositionManager::ApplyAsyncTransformToScrollbar(Layer* aLayer)
+{
+ // If this layer corresponds to a scrollbar, then there should be a layer that
+ // is a previous sibling or a parent that has a matching ViewID on its FrameMetrics.
+ // That is the content that this scrollbar is for. We pick up the transient
+ // async transform from that layer and use it to update the scrollbar position.
+ // Note that it is possible that the content layer is no longer there; in
+ // this case we don't need to do anything because there can't be an async
+ // transform on the content.
+ bool isAncestor = false;
+ const LayerMetricsWrapper& scrollTarget = FindScrolledLayerForScrollbar(aLayer, &isAncestor);
+ if (scrollTarget) {
+ ApplyAsyncTransformToScrollbarForContent(aLayer, scrollTarget, isAncestor);
+ }
+}
+
+void
+AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLayerTransformRecorder.EndTest(aOutData);
+}
+
+bool
+AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip)
+{
+ PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ Layer* root = mLayerManager->GetRoot();
+ if (!root) {
+ return false;
+ }
+
+ // First, compute and set the shadow transforms from OMT animations.
+ // NB: we must sample animations *before* sampling pan/zoom
+ // transforms.
+ // Use a previous vsync time to make main thread animations and compositor
+ // more in sync with each other.
+ // On the initial frame we use aVsyncTimestamp here so the timestamp on the
+ // second frame are the same as the initial frame, but it does not matter.
+ bool wantNextFrame = SampleAnimations(root,
+ !mPreviousFrameTimeStamp.IsNull() ?
+ mPreviousFrameTimeStamp : aCurrentFrame);
+
+ // Reset the previous time stamp if we don't already have any running
+ // animations to avoid using the time which is far behind for newly
+ // started animations.
+ mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame : TimeStamp();
+
+ if (!(aSkip & TransformsToSkip::APZ)) {
+ // FIXME/bug 775437: unify this interface with the ~native-fennec
+ // derived code
+ //
+ // Attempt to apply an async content transform to any layer that has
+ // an async pan zoom controller (which means that it is rendered
+ // async using Gecko). If this fails, fall back to transforming the
+ // primary scrollable layer. "Failing" here means that we don't
+ // find a frame that is async scrollable. Note that the fallback
+ // code also includes Fennec which is rendered async. Fennec uses
+ // its own platform-specific async rendering that is done partially
+ // in Gecko and partially in Java.
+ bool foundRoot = false;
+ if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+#if defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(foundRoot);
+ if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
+ MoveScrollbarForLayerMargin(root, mRootScrollableId, mFixedLayerMargins);
+ }
+#endif
+ }
+
+ // Advance APZ animations to the next expected vsync timestamp, if we can
+ // get it.
+ TimeStamp nextFrame = aCurrentFrame;
+
+ MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
+ if (aVsyncRate != TimeDuration::Forever()) {
+ nextFrame += aVsyncRate;
+ }
+
+ wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
+ }
+
+ LayerComposite* rootComposite = root->AsLayerComposite();
+
+ gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
+ trans *= gfx::Matrix4x4::From2D(mWorldTransform);
+ rootComposite->SetShadowBaseTransform(trans);
+
+ if (gfxPrefs::CollectScrollTransforms()) {
+ RecordShadowTransforms(root);
+ }
+
+ return wantNextFrame;
+}
+
+void
+AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ widget::AndroidCompositorWidget* widget =
+ mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
+ if (!widget) {
+ return;
+ }
+ widget->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
+#endif
+}
+
+void
+AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ widget::AndroidCompositorWidget* widget =
+ mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
+ if (!widget) {
+ return;
+ }
+ widget->SyncFrameMetrics(
+ aScrollOffset, aZoom, aCssPageRect, aDisplayPort, aPaintedResolution,
+ aLayersUpdated, aPaintSyncId, aFixedLayerMargins);
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h
new file mode 100644
index 000000000..4ec49b1a9
--- /dev/null
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ASYNCCOMPOSITIONMANAGER_H
+#define GFX_ASYNCCOMPOSITIONMANAGER_H
+
+#include "Units.h" // for ScreenPoint, etc
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite
+#include "mozilla/Attributes.h" // for final, etc
+#include "mozilla/RefPtr.h" // for RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/FrameUniformityData.h" // For FrameUniformityData
+#include "mozilla/layers/LayersMessages.h" // for TargetConfig
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for LayerManager::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class Layer;
+class LayerManagerComposite;
+class AutoResolveRefLayers;
+class CompositorBridgeParent;
+
+// Represents async transforms consisting of a scale and a translation.
+struct AsyncTransform {
+ explicit AsyncTransform(LayerToParentLayerScale aScale = LayerToParentLayerScale(),
+ ParentLayerPoint aTranslation = ParentLayerPoint())
+ : mScale(aScale)
+ , mTranslation(aTranslation)
+ {}
+
+ operator AsyncTransformComponentMatrix() const
+ {
+ return AsyncTransformComponentMatrix::Scaling(mScale.scale, mScale.scale, 1)
+ .PostTranslate(mTranslation.x, mTranslation.y, 0);
+ }
+
+ bool operator==(const AsyncTransform& rhs) const {
+ return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
+ }
+
+ bool operator!=(const AsyncTransform& rhs) const {
+ return !(*this == rhs);
+ }
+
+ LayerToParentLayerScale mScale;
+ ParentLayerPoint mTranslation;
+};
+
+/**
+ * Manage async composition effects. This class is only used with OMTC and only
+ * lives on the compositor thread. It is a layer on top of the layer manager
+ * (LayerManagerComposite) which deals with elements of composition which are
+ * usually dealt with by dom or layout when main thread rendering, but which can
+ * short circuit that stuff to directly affect layers as they are composited,
+ * for example, off-main thread animation, async video, async pan/zoom.
+ */
+class AsyncCompositionManager final
+{
+ friend class AutoResolveRefLayers;
+ ~AsyncCompositionManager();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager)
+
+ explicit AsyncCompositionManager(LayerManagerComposite* aManager);
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint() { mIsFirstPaint = true; }
+
+ // Sample transforms for layer trees. Return true to request
+ // another animation frame.
+ enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
+ bool TransformShadowTree(TimeStamp aCurrentFrame,
+ TimeDuration aVsyncRate,
+ TransformsToSkip aSkip = TransformsToSkip::NoneOfThem);
+
+ // Calculates the correct rotation and applies the transform to
+ // our layer manager
+ void ComputeRotation();
+
+ // Call after updating our layer tree.
+ void Updated(bool isFirstPaint, const TargetConfig& aTargetConfig,
+ int32_t aPaintSyncId)
+ {
+ mIsFirstPaint |= isFirstPaint;
+ mLayersUpdated = true;
+ mTargetConfig = aTargetConfig;
+ if (aPaintSyncId) {
+ mPaintSyncId = aPaintSyncId;
+ }
+ }
+
+ bool RequiresReorientation(mozilla::dom::ScreenOrientationInternal aOrientation) const
+ {
+ return mTargetConfig.orientation() != aOrientation;
+ }
+
+ // True if the underlying layer tree is ready to be composited.
+ bool ReadyForCompose() { return mReadyForCompose; }
+
+ // Returns true if the next composition will be the first for a
+ // particular document.
+ bool IsFirstPaint() { return mIsFirstPaint; }
+
+ // GetFrameUniformity will return the frame uniformity for each layer attached to an APZ
+ // from the recorded data in RecordShadowTransform
+ void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
+
+ // Stores the clip rect of a layer in two parts: a fixed part and a scrolled
+ // part. When a layer is fixed, the clip needs to be adjusted to account for
+ // async transforms. Only the fixed part needs to be adjusted, so we need
+ // to store the two parts separately.
+ struct ClipParts {
+ Maybe<ParentLayerIntRect> mFixedClip;
+ Maybe<ParentLayerIntRect> mScrolledClip;
+
+ Maybe<ParentLayerIntRect> Intersect() const {
+ return IntersectMaybeRects(mFixedClip, mScrolledClip);
+ }
+ };
+
+ typedef std::map<Layer*, ClipParts> ClipPartsCache;
+private:
+ // Return true if an AsyncPanZoomController content transform was
+ // applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
+ // one of the metrics on one of the layers was determined to be the "root"
+ // and its state was synced to the Java front-end. |aOutFoundRoot| must be
+ // non-null.
+ bool ApplyAsyncContentTransformToTree(Layer* aLayer,
+ bool* aOutFoundRoot);
+ /**
+ * Update the shadow transform for aLayer assuming that is a scrollbar,
+ * so that it stays in sync with the content that is being scrolled by APZ.
+ */
+ void ApplyAsyncTransformToScrollbar(Layer* aLayer);
+
+ void SetFirstPaintViewport(const LayerIntPoint& aOffset,
+ const CSSToLayerScale& aZoom,
+ const CSSRect& aCssPageRect);
+ void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
+ const CSSToParentLayerScale& aZoom,
+ const CSSRect& aCssPageRect,
+ const CSSRect& aDisplayPort,
+ const CSSToLayerScale& aPaintedResolution,
+ bool aLayersUpdated,
+ int32_t aPaintSyncId,
+ ScreenMargin& aFixedLayerMargins);
+
+ /**
+ * Adds a translation to the transform of any fixed position (whose parent
+ * layer is not fixed) or sticky position layer descendant of
+ * |aTransformedSubtreeRoot|. The translation is chosen so that the layer's
+ * anchor point relative to |aTransformedSubtreeRoot|'s parent layer is the same
+ * as it was when |aTransformedSubtreeRoot|'s GetLocalTransform() was
+ * |aPreviousTransformForRoot|. |aCurrentTransformForRoot| is
+ * |aTransformedSubtreeRoot|'s current GetLocalTransform() modulo any
+ * overscroll-related transform, which we don't want to adjust for.
+ * For sticky position layers, the translation is further intersected with
+ * the layer's sticky scroll ranges.
+ * This function will also adjust layers so that the given content document
+ * fixed position margins will be respected during asynchronous panning and
+ * zooming.
+ * |aTransformScrollId| is the scroll id of the scroll frame that scrolls
+ * |aTransformedSubtreeRoot|.
+ * |aClipPartsCache| optionally maps layers to separate fixed and scrolled
+ * clips, so we can only adjust the fixed portion.
+ * This function has a recursive implementation; aStartTraversalAt specifies
+ * where to start the current recursion of the traversal. For the initial
+ * call, it should be the same as aTrasnformedSubtreeRoot.
+ */
+ void AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+ Layer* aStartTraversalAt,
+ FrameMetrics::ViewID aTransformScrollId,
+ const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+ const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
+ const ScreenMargin& aFixedLayerMargins,
+ ClipPartsCache* aClipPartsCache);
+
+ /**
+ * DRAWING PHASE ONLY
+ *
+ * For reach RefLayer in our layer tree, look up its referent and connect it
+ * to the layer tree, if found.
+ * aHasRemoteContent - indicates if the layer tree contains a remote reflayer.
+ * May be null.
+ * aResolvePlugins - incoming value indicates if plugin windows should be
+ * updated through a call on aCompositor's UpdatePluginWindowState. Applies
+ * to linux and windows only, may be null. On return value indicates
+ * if any updates occured.
+ */
+ void ResolveRefLayers(CompositorBridgeParent* aCompositor, bool* aHasRemoteContent,
+ bool* aResolvePlugins);
+
+ /**
+ * Detaches all referents resolved by ResolveRefLayers.
+ * Assumes that mLayerManager->GetRoot() and mTargetConfig have not changed
+ * since ResolveRefLayers was called.
+ */
+ void DetachRefLayers();
+
+ // Records the shadow transforms for the tree of layers rooted at the given layer
+ void RecordShadowTransforms(Layer* aLayer);
+
+ TargetConfig mTargetConfig;
+ CSSRect mContentRect;
+
+ RefPtr<LayerManagerComposite> mLayerManager;
+ // When this flag is set, the next composition will be the first for a
+ // particular document (i.e. the document displayed on the screen will change).
+ // This happens when loading a new page or switching tabs. We notify the
+ // front-end (e.g. Java on Android) about this so that it take the new page
+ // size and zoom into account when providing us with the next view transform.
+ bool mIsFirstPaint;
+
+ // This flag is set during a layers update, so that the first composition
+ // after a layers update has it set. It is cleared after that first composition.
+ bool mLayersUpdated;
+
+ int32_t mPaintSyncId;
+
+ bool mReadyForCompose;
+
+ gfx::Matrix mWorldTransform;
+ LayerTransformRecorder mLayerTransformRecorder;
+
+ TimeStamp mPreviousFrameTimeStamp;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // The following two fields are only needed on Fennec with C++ APZ, because
+ // then we need to reposition the gecko scrollbar to deal with the
+ // dynamic toolbar shifting content around.
+ FrameMetrics::ViewID mRootScrollableId;
+ ScreenMargin mFixedLayerMargins;
+#endif
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AsyncCompositionManager::TransformsToSkip)
+
+class MOZ_STACK_CLASS AutoResolveRefLayers {
+public:
+ explicit AutoResolveRefLayers(AsyncCompositionManager* aManager,
+ CompositorBridgeParent* aCompositor = nullptr,
+ bool* aHasRemoteContent = nullptr,
+ bool* aResolvePlugins = nullptr) :
+ mManager(aManager)
+ {
+ if (mManager) {
+ mManager->ResolveRefLayers(aCompositor, aHasRemoteContent, aResolvePlugins);
+ }
+ }
+
+ ~AutoResolveRefLayers()
+ {
+ if (mManager) {
+ mManager->DetachRefLayers();
+ }
+ }
+
+private:
+ AsyncCompositionManager* mManager;
+
+ AutoResolveRefLayers(const AutoResolveRefLayers&) = delete;
+ AutoResolveRefLayers& operator=(const AutoResolveRefLayers&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp
new file mode 100644
index 000000000..3c8299e05
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasLayerComposite.h"
+#include "composite/CompositableHost.h" // for CompositableHost
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CanvasLayerComposite::CanvasLayerComposite(LayerManagerComposite* aManager)
+ : CanvasLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mCompositableHost(nullptr)
+{
+ MOZ_COUNT_CTOR(CanvasLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+CanvasLayerComposite::~CanvasLayerComposite()
+{
+ MOZ_COUNT_DTOR(CanvasLayerComposite);
+
+ CleanupResources();
+}
+
+bool
+CanvasLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE:
+ mCompositableHost = aHost;
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+Layer*
+CanvasLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mCompositableHost && mCompositor) {
+ mCompositableHost->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+CanvasLayerComposite::GetRenderState()
+{
+ if (mDestroyed || !mCompositableHost || !mCompositableHost->IsAttached()) {
+ return LayerRenderState();
+ }
+ return mCompositableHost->GetRenderState();
+}
+
+void
+CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ if (!mCompositableHost || !mCompositableHost->IsAttached()) {
+ return;
+ }
+
+ mCompositor->MakeCurrent();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mCompositableHost->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransform(),
+ GetSamplingFilter(),
+ clipRect);
+ });
+
+ mCompositableHost->BumpFlashCounter();
+}
+
+CompositableHost*
+CanvasLayerComposite::GetCompositableHost()
+{
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ return mCompositableHost.get();
+ }
+
+ return nullptr;
+}
+
+void
+CanvasLayerComposite::CleanupResources()
+{
+ if (mCompositableHost) {
+ mCompositableHost->Detach(this);
+ }
+ mCompositableHost = nullptr;
+}
+
+gfx::SamplingFilter
+CanvasLayerComposite::GetSamplingFilter()
+{
+ gfx::SamplingFilter filter = mSamplingFilter;
+#ifdef ANDROID
+ // Bug 691354
+ // Using the LINEAR filter we get unexplained artifacts.
+ // Use NEAREST when no scaling is required.
+ Matrix matrix;
+ bool is2D = GetEffectiveTransform().Is2D(&matrix);
+ if (is2D && !ThebesMatrix(matrix).HasNonTranslationOrFlip()) {
+ filter = SamplingFilter::POINT;
+ }
+#endif
+ return filter;
+}
+
+void
+CanvasLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mCompositableHost->GenEffect(GetSamplingFilter());
+}
+
+void
+CanvasLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ CanvasLayer::PrintInfo(aStream, aPrefix);
+ aStream << "\n";
+ if (mCompositableHost && mCompositableHost->IsAttached()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mCompositableHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/CanvasLayerComposite.h b/gfx/layers/composite/CanvasLayerComposite.h
new file mode 100644
index 000000000..0042d9027
--- /dev/null
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CanvasLayerComposite_H
+#define GFX_CanvasLayerComposite_H
+
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+// Canvas layers use ImageHosts (but CanvasClients) because compositing a
+// canvas is identical to compositing an image.
+class ImageHost;
+
+class CanvasLayerComposite : public CanvasLayer,
+ public LayerComposite
+{
+public:
+ explicit CanvasLayerComposite(LayerManagerComposite* aManager);
+
+protected:
+ virtual ~CanvasLayerComposite();
+
+public:
+ // CanvasLayer impl
+ virtual void Initialize(const Data& aData) override
+ {
+ NS_RUNTIMEABORT("Incompatibe surface type");
+ }
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual void Disconnect() override
+ {
+ Destroy();
+ }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual Layer* GetLayer() override;
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ void SetBounds(gfx::IntRect aBounds) { mBounds = aBounds; }
+
+ virtual const char* Name() const override { return "CanvasLayerComposite"; }
+
+protected:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+private:
+ RefPtr<CompositableHost> mCompositableHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CanvasLayerComposite_H */
diff --git a/gfx/layers/composite/ColorLayerComposite.cpp b/gfx/layers/composite/ColorLayerComposite.cpp
new file mode 100644
index 000000000..4277a8f70
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ColorLayerComposite.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::COLOR
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ Rect rect(GetBounds());
+ const Matrix4x4& transform = GetEffectiveTransform();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ GenEffectChain(effectChain);
+ mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
+ transform);
+ });
+
+ mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
+ transform);
+}
+
+void
+ColorLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = new EffectSolidColor(GetColor());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ColorLayerComposite.h b/gfx/layers/composite/ColorLayerComposite.h
new file mode 100644
index 000000000..fb019f74f
--- /dev/null
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ColorLayerComposite_H
+#define GFX_ColorLayerComposite_H
+
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+class ColorLayerComposite : public ColorLayer,
+ public LayerComposite
+{
+public:
+ explicit ColorLayerComposite(LayerManagerComposite *aManager)
+ : ColorLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ {
+ MOZ_COUNT_CTOR(ColorLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+ }
+
+protected:
+ ~ColorLayerComposite()
+ {
+ MOZ_COUNT_DTOR(ColorLayerComposite);
+ Destroy();
+ }
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ }
+
+ virtual void Destroy() override { mDestroyed = true; }
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void CleanupResources() override {};
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual const char* Name() const override { return "ColorLayerComposite"; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ColorLayerComposite_H */
diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp
new file mode 100644
index 000000000..5ed3d3fe9
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableHost.h"
+#include <map> // for _Rb_tree_iterator, map, etc
+#include <utility> // for pair
+#include "ContentHost.h" // for ContentHostDoubleBuffered, etc
+#include "Effects.h" // for EffectMask, Effect, etc
+#include "gfxUtils.h"
+#include "ImageHost.h" // for ImageHostBuffered, etc
+#include "TiledContentHost.h" // for TiledContentHost
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/layers/PCompositableParent.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class Compositor;
+
+/**
+ * IPDL actor used by CompositableHost to match with its corresponding
+ * CompositableClient on the content side.
+ *
+ * CompositableParent is owned by the IPDL system. It's deletion is triggered
+ * by either the CompositableChild's deletion, or by the IPDL communication
+ * going down.
+ */
+class CompositableParent : public ParentActor<PCompositableParent>
+{
+public:
+ CompositableParent(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID = 0,
+ PImageContainerParent* aImageContainer = nullptr)
+ {
+ MOZ_COUNT_CTOR(CompositableParent);
+ mHost = CompositableHost::Create(aTextureInfo);
+ mHost->SetAsyncID(aID);
+ if (aID) {
+ CompositableMap::Set(aID, this);
+ }
+ if (aImageContainer) {
+ mHost->SetImageContainer(
+ static_cast<ImageContainerParent*>(aImageContainer));
+ }
+ }
+
+ ~CompositableParent()
+ {
+ MOZ_COUNT_DTOR(CompositableParent);
+ CompositableMap::Erase(mHost->GetAsyncID());
+ }
+
+ virtual void Destroy() override
+ {
+ if (mHost) {
+ mHost->Detach(nullptr, CompositableHost::FORCE_DETACH);
+ }
+ }
+
+ RefPtr<CompositableHost> mHost;
+};
+
+CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
+ : mTextureInfo(aTextureInfo)
+ , mAsyncID(0)
+ , mCompositorID(0)
+ , mCompositor(nullptr)
+ , mLayer(nullptr)
+ , mFlashCounter(0)
+ , mAttached(false)
+ , mKeepAttached(false)
+{
+ MOZ_COUNT_CTOR(CompositableHost);
+}
+
+CompositableHost::~CompositableHost()
+{
+ MOZ_COUNT_DTOR(CompositableHost);
+}
+
+PCompositableParent*
+CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
+ const TextureInfo& aTextureInfo,
+ uint64_t aID,
+ PImageContainerParent* aImageContainer)
+{
+ return new CompositableParent(aMgr, aTextureInfo, aID, aImageContainer);
+}
+
+bool
+CompositableHost::DestroyIPDLActor(PCompositableParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+CompositableHost*
+CompositableHost::FromIPDLActor(PCompositableParent* aActor)
+{
+ MOZ_ASSERT(aActor);
+ return static_cast<CompositableParent*>(aActor)->mHost;
+}
+
+void
+CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ if (GetCompositor()) {
+ for (auto& texture : aTextures) {
+ texture.mTexture->SetCompositor(GetCompositor());
+ }
+ }
+}
+
+void
+CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite)
+{
+ MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
+ if (GetCompositor()) {
+ aTextureOnBlack->SetCompositor(GetCompositor());
+ aTextureOnWhite->SetCompositor(GetCompositor());
+ }
+}
+
+void
+CompositableHost::RemoveTextureHost(TextureHost* aTexture)
+{}
+
+void
+CompositableHost::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ mCompositor = aCompositor;
+}
+
+bool
+CompositableHost::AddMaskEffect(EffectChain& aEffects,
+ const gfx::Matrix4x4& aTransform)
+{
+ CompositableTextureSourceRef source;
+ RefPtr<TextureHost> host = GetAsTextureHost();
+
+ if (!host) {
+ NS_WARNING("Using compositable with no valid TextureHost as mask");
+ return false;
+ }
+
+ if (!host->Lock()) {
+ NS_WARNING("Failed to lock the mask texture");
+ return false;
+ }
+
+ if (!host->BindTextureSource(source)) {
+ NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
+ host->Unlock();
+ return false;
+ }
+ MOZ_ASSERT(source);
+
+ RefPtr<EffectMask> effect = new EffectMask(source,
+ source->GetSize(),
+ aTransform);
+ aEffects.mSecondaryEffects[EffectTypes::MASK] = effect;
+ return true;
+}
+
+void
+CompositableHost::RemoveMaskEffect()
+{
+ RefPtr<TextureHost> host = GetAsTextureHost();
+ if (host) {
+ host->Unlock();
+ }
+}
+
+/* static */ already_AddRefed<CompositableHost>
+CompositableHost::Create(const TextureInfo& aTextureInfo)
+{
+ RefPtr<CompositableHost> result;
+ switch (aTextureInfo.mCompositableType) {
+ case CompositableType::IMAGE_BRIDGE:
+ NS_ERROR("Cannot create an image bridge compositable this way");
+ break;
+ case CompositableType::CONTENT_TILED:
+ result = new TiledContentHost(aTextureInfo);
+ break;
+ case CompositableType::IMAGE:
+ result = new ImageHost(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_SINGLE:
+ result = new ContentHostSingleBuffered(aTextureInfo);
+ break;
+ case CompositableType::CONTENT_DOUBLE:
+ result = new ContentHostDoubleBuffered(aTextureInfo);
+ break;
+ default:
+ NS_ERROR("Unknown CompositableType");
+ }
+ return result.forget();
+}
+
+void
+CompositableHost::DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture)
+{
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+}
+
+void
+CompositableHost::ReceivedDestroy(PCompositableParent* aActor)
+{
+ static_cast<CompositableParent*>(aActor)->RecvDestroy();
+}
+
+namespace CompositableMap {
+
+typedef std::map<uint64_t, PCompositableParent*> CompositableMap_t;
+static CompositableMap_t* sCompositableMap = nullptr;
+bool IsCreated() {
+ return sCompositableMap != nullptr;
+}
+PCompositableParent* Get(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return nullptr;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it == sCompositableMap->end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+void Set(uint64_t aID, PCompositableParent* aParent)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ (*sCompositableMap)[aID] = aParent;
+}
+void Erase(uint64_t aID)
+{
+ if (!IsCreated() || aID == 0) {
+ return;
+ }
+ CompositableMap_t::iterator it = sCompositableMap->find(aID);
+ if (it != sCompositableMap->end()) {
+ sCompositableMap->erase(it);
+ }
+}
+void Clear()
+{
+ if (!IsCreated()) {
+ return;
+ }
+ sCompositableMap->clear();
+}
+void Create()
+{
+ if (sCompositableMap == nullptr) {
+ sCompositableMap = new CompositableMap_t;
+ }
+}
+void Destroy()
+{
+ Clear();
+ delete sCompositableMap;
+ sCompositableMap = nullptr;
+}
+
+} // namespace CompositableMap
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h
new file mode 100644
index 000000000..d8a967732
--- /dev/null
+++ b/gfx/layers/composite/CompositableHost.h
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_BUFFERHOST_H
+#define MOZILLA_GFX_BUFFERHOST_H
+
+#include <stdint.h> // for uint64_t
+#include <stdio.h> // for FILE
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted, etc
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/Effects.h" // for Texture Effect
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+#include "Units.h" // for CSSToScreenScale
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Layer;
+class LayerComposite;
+class Compositor;
+class ImageContainerParent;
+class ThebesBufferData;
+class TiledContentHost;
+class CompositableParentManager;
+class PCompositableParent;
+struct EffectChain;
+
+/**
+ * The compositor-side counterpart to CompositableClient. Responsible for
+ * updating textures and data about textures from IPC and how textures are
+ * composited (tiling, double buffering, etc.).
+ *
+ * Update (for images/canvases) and UpdateThebes (for Thebes) are called during
+ * the layers transaction to update the Compositbale's textures from the
+ * content side. The actual update (and any syncronous upload) is done by the
+ * TextureHost, but it is coordinated by the CompositableHost.
+ *
+ * Composite is called by the owning layer when it is composited. CompositableHost
+ * will use its TextureHost(s) and call Compositor::DrawQuad to do the actual
+ * rendering.
+ */
+class CompositableHost
+{
+protected:
+ virtual ~CompositableHost();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(CompositableHost)
+ explicit CompositableHost(const TextureInfo& aTextureInfo);
+
+ static already_AddRefed<CompositableHost> Create(const TextureInfo& aTextureInfo);
+
+ virtual CompositableType GetType() = 0;
+
+ // If base class overrides, it should still call the parent implementation
+ virtual void SetCompositor(Compositor* aCompositor);
+
+ // composite the contents of this buffer host to the compositor's surface
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) = 0;
+
+ /**
+ * Update the content host.
+ * aUpdated is the region which should be updated
+ * aUpdatedRegionBack is the region in aNewBackResult which has been updated
+ */
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+ {
+ NS_ERROR("should be implemented or not used");
+ return false;
+ }
+
+ /**
+ * Returns the front buffer.
+ * *aPictureRect (if non-null, and the returned TextureHost is non-null)
+ * is set to the picture rect.
+ */
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) {
+ return nullptr;
+ }
+
+ virtual LayerRenderState GetRenderState() = 0;
+
+ virtual gfx::IntSize GetImageSize() const
+ {
+ MOZ_ASSERT(false, "Should have been overridden");
+ return gfx::IntSize();
+ }
+
+ /**
+ * Adds a mask effect using this texture as the mask, if possible.
+ * @return true if the effect was added, false otherwise.
+ */
+ bool AddMaskEffect(EffectChain& aEffects,
+ const gfx::Matrix4x4& aTransform);
+
+ void RemoveMaskEffect();
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ Layer* GetLayer() const { return mLayer; }
+ void SetLayer(Layer* aLayer) { mLayer = aLayer; }
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) {}
+
+ virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
+
+ typedef uint32_t AttachFlags;
+ static const AttachFlags NO_FLAGS = 0;
+ static const AttachFlags ALLOW_REATTACH = 1;
+ static const AttachFlags KEEP_ATTACHED = 2;
+ static const AttachFlags FORCE_DETACH = 2;
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS)
+ {
+ MOZ_ASSERT(aCompositor, "Compositor is required");
+ NS_ASSERTION(aFlags & ALLOW_REATTACH || !mAttached,
+ "Re-attaching compositables must be explicitly authorised");
+ SetCompositor(aCompositor);
+ SetLayer(aLayer);
+ mAttached = true;
+ mKeepAttached = aFlags & KEEP_ATTACHED;
+ }
+ // Detach this compositable host from its layer.
+ // If we are used for async video, then it is not safe to blindly detach since
+ // we might be re-attached to a different layer. aLayer is the layer which the
+ // caller expects us to be attached to, we will only detach if we are in fact
+ // attached to that layer. If we are part of a normal layer, then we will be
+ // detached in any case. if aLayer is null, then we will only detach if we are
+ // not async.
+ // Only force detach if the IPDL tree is being shutdown.
+ virtual void Detach(Layer* aLayer = nullptr, AttachFlags aFlags = NO_FLAGS)
+ {
+ if (!mKeepAttached ||
+ aLayer == mLayer ||
+ aFlags & FORCE_DETACH) {
+ SetLayer(nullptr);
+ mAttached = false;
+ mKeepAttached = false;
+ }
+ }
+ bool IsAttached() { return mAttached; }
+
+ static void
+ ReceivedDestroy(PCompositableParent* aActor);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) { }
+ static void DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture);
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() { return nullptr; }
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
+
+ struct TimedTexture {
+ CompositableTextureHostRef mTexture;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures);
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite);
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) { }
+
+ virtual void RemoveTextureHost(TextureHost* aTexture);
+
+ // Called every time this is composited
+ void BumpFlashCounter() {
+ mFlashCounter = mFlashCounter >= DIAGNOSTIC_FLASH_COUNTER_MAX
+ ? DIAGNOSTIC_FLASH_COUNTER_MAX : mFlashCounter + 1;
+ }
+
+ static PCompositableParent*
+ CreateIPDLActor(CompositableParentManager* mgr,
+ const TextureInfo& textureInfo,
+ uint64_t asyncID,
+ PImageContainerParent* aImageContainer = nullptr);
+
+ static bool DestroyIPDLActor(PCompositableParent* actor);
+
+ static CompositableHost* FromIPDLActor(PCompositableParent* actor);
+
+ uint64_t GetCompositorID() const { return mCompositorID; }
+
+ uint64_t GetAsyncID() const { return mAsyncID; }
+
+ void SetCompositorID(uint64_t aID) { mCompositorID = aID; }
+
+ void SetAsyncID(uint64_t aID) { mAsyncID = aID; }
+
+ virtual bool Lock() { return false; }
+
+ virtual void Unlock() { }
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) {
+ return nullptr;
+ }
+
+ /// Called when shutting down the layer tree.
+ /// This is a good place to clear all potential gpu resources before the widget
+ /// is is destroyed.
+ virtual void CleanupResources() {}
+
+protected:
+ TextureInfo mTextureInfo;
+ uint64_t mAsyncID;
+ uint64_t mCompositorID;
+ RefPtr<Compositor> mCompositor;
+ Layer* mLayer;
+ uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
+ bool mAttached;
+ bool mKeepAttached;
+};
+
+class AutoLockCompositableHost final
+{
+public:
+ explicit AutoLockCompositableHost(CompositableHost* aHost)
+ : mHost(aHost)
+ {
+ mSucceeded = (mHost && mHost->Lock());
+ }
+
+ ~AutoLockCompositableHost()
+ {
+ if (mSucceeded && mHost) {
+ mHost->Unlock();
+ }
+ }
+
+ bool Failed() const { return !mSucceeded; }
+
+private:
+ RefPtr<CompositableHost> mHost;
+ bool mSucceeded;
+};
+
+/**
+ * Global CompositableMap, to use in the compositor thread only.
+ *
+ * PCompositable and PLayer can, in the case of async textures, be managed by
+ * different top level protocols. In this case they don't share the same
+ * communication channel and we can't send an OpAttachCompositable (PCompositable,
+ * PLayer) message.
+ *
+ * In order to attach a layer and the right compositable if the the compositable
+ * is async, we store references to the async compositables in a CompositableMap
+ * that is accessed only on the compositor thread. During a layer transaction we
+ * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
+ * side we lookup the ID in the map and attach the corresponding compositable to
+ * the layer.
+ *
+ * CompositableMap must be global because the image bridge doesn't have any
+ * reference to whatever we have created with PLayerTransaction. So, the only way to
+ * actually connect these two worlds is to have something global that they can
+ * both query (in the same thread). The map is not allocated the map on the
+ * stack to avoid the badness of static initialization.
+ *
+ * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
+ * ImageBridge is used by all the existing compositors that have a video, so
+ * there isn't an instance or "something" that lives outside the boudaries of a
+ * given layer manager on the compositor thread except the image bridge and the
+ * thread itself.
+ */
+namespace CompositableMap {
+ void Create();
+ void Destroy();
+ PCompositableParent* Get(uint64_t aID);
+ void Set(uint64_t aID, PCompositableParent* aParent);
+ void Erase(uint64_t aID);
+ void Clear();
+} // namespace CompositableMap
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
new file mode 100755
index 000000000..35070cad6
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -0,0 +1,698 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContainerLayerComposite.h"
+#include <algorithm> // for min
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for LayerRect, LayerPixel, etc
+#include "CompositableHost.h" // for CompositableHost
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point, IntPoint
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/Compositor.h" // for Compositor, etc
+#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include <stack>
+#include "TextRenderer.h" // for TextRenderer
+#include <vector>
+#include "GeckoProfiler.h" // for GeckoProfiler
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h" // for ProfilerMarkers
+#endif
+
+#define CULLING_LOG(...)
+// #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
+
+#define DUMP(...) do { if (gfxEnv::DumpDebug()) { printf_stderr(__VA_ARGS__); } } while(0)
+#define XYWH(k) (k).x, (k).y, (k).width, (k).height
+#define XY(k) (k).x, (k).y
+#define WH(k) (k).width, (k).height
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
+ LayerManagerComposite* aManager,
+ Layer* aLayer)
+{
+
+ if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
+ // XXX - should figure out a way to render this, but for now this
+ // is hard to do, since it will often get superimposed over the first
+ // child of the layer, which is bad.
+ return;
+ }
+
+ std::stringstream ss;
+ aLayer->PrintInfo(ss, "");
+
+ LayerIntRegion visibleRegion = aLayer->GetVisibleRegion();
+
+ uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
+
+ IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
+ aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
+ aLayer->GetEffectiveTransform(), 16,
+ maxWidth);
+}
+
+template<class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
+{
+ gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+ return surfaceRect;
+}
+
+static void PrintUniformityInfo(Layer* aLayer)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (!profiler_is_active()) {
+ return;
+ }
+
+ // Don't want to print a log for smaller layers
+ if (aLayer->GetLocalVisibleRegion().GetBounds().width < 300 ||
+ aLayer->GetLocalVisibleRegion().GetBounds().height < 300) {
+ return;
+ }
+
+ Matrix4x4 transform = aLayer->AsLayerComposite()->GetShadowBaseTransform();
+ if (!transform.Is2D()) {
+ return;
+ }
+
+ Point translation = transform.As2D().GetTranslation();
+ LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
+ PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
+#endif
+}
+
+/* all of the per-layer prepared data we need to maintain */
+struct PreparedLayer
+{
+ PreparedLayer(Layer *aLayer, RenderTargetIntRect aClipRect) :
+ mLayer(aLayer), mClipRect(aClipRect) {}
+ RefPtr<Layer> mLayer;
+ RenderTargetIntRect mClipRect;
+};
+
+/* all of the prepared data that we need in RenderLayer() */
+struct PreparedData
+{
+ RefPtr<CompositingRenderTarget> mTmpTarget;
+ AutoTArray<PreparedLayer, 12> mLayers;
+ bool mNeedsSurfaceCopy;
+};
+
+// ContainerPrepare is shared between RefLayer and ContainerLayer
+template<class ContainerT> void
+ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect)
+{
+ aContainer->mPrepared = MakeUnique<PreparedData>();
+ aContainer->mPrepared->mNeedsSurfaceCopy = false;
+
+ /**
+ * Determine which layers to draw.
+ */
+ AutoTArray<Layer*, 12> children;
+ aContainer->SortChildrenBy3DZOrder(children);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
+
+ RenderTargetIntRect clipRect = layerToRender->GetLayer()->
+ CalculateScissorRect(aClipRect);
+
+ if (layerToRender->GetLayer()->IsBackfaceHidden()) {
+ continue;
+ }
+
+ // We don't want to skip container layers because otherwise their mPrepared
+ // may be null which is not allowed.
+ if (!layerToRender->GetLayer()->AsContainerLayer()) {
+ if (!layerToRender->GetLayer()->IsVisible() &&
+ !layerToRender->NeedToDrawCheckerboarding(nullptr)) {
+ CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
+ continue;
+ }
+
+ if (clipRect.IsEmpty()) {
+ CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
+ continue;
+ }
+ }
+
+ CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
+
+ layerToRender->Prepare(clipRect);
+ aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender->GetLayer(),
+ clipRect));
+ }
+
+ CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
+
+ /**
+ * Setup our temporary surface for rendering the contents of this container.
+ */
+
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ bool surfaceCopyNeeded;
+ // DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call it unconditionally
+ aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
+ if (aContainer->UseIntermediateSurface()) {
+ if (!surfaceCopyNeeded) {
+ RefPtr<CompositingRenderTarget> surface = nullptr;
+
+ RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
+ if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ surface = lastSurf;
+ }
+
+ if (!surface) {
+ // If we don't need a copy we can render to the intermediate now to avoid
+ // unecessary render target switching. This brings a big perf boost on mobile gpus.
+ surface = CreateOrRecycleTarget(aContainer, aManager);
+
+ MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface rendering\n", aContainer);
+ RenderIntermediate(aContainer, aManager, aClipRect.ToUnknownRect(), surface);
+ aContainer->SetChildrenChanged(false);
+ }
+
+ aContainer->mPrepared->mTmpTarget = surface;
+ } else {
+ MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface copy\n", aContainer);
+ aContainer->mPrepared->mNeedsSurfaceCopy = true;
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+ } else {
+ aContainer->mLastIntermediateSurface = nullptr;
+ }
+}
+
+template<class ContainerT> void
+RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ if (aLayer->GetScrollMetadataCount() < 1) {
+ return;
+ }
+
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ if (!controller) {
+ return;
+ }
+
+ ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+
+ // Options
+ const int verticalPadding = 10;
+ const int horizontalPadding = 5;
+ gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
+ gfx::Color tileActiveColor(1, 1, 1, 0.4f);
+ gfx::Color tileBorderColor(0, 0, 0, 0.1f);
+ gfx::Color pageBorderColor(0, 0, 0);
+ gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
+ gfx::Color displayPortColor(0, 1.f, 0);
+ gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
+ gfx::Color visibilityColor(1.f, 0, 0);
+
+ // Rects
+ const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
+ ParentLayerRect compositionBounds = fm.GetCompositionBounds();
+ LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
+ LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
+ LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
+ Maybe<LayerRect> cdp;
+ if (!fm.GetCriticalDisplayPort().IsEmpty()) {
+ cdp = Some((fm.GetCriticalDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel());
+ }
+
+ // Don't render trivial minimap. They can show up from textboxes and other tiny frames.
+ if (viewRect.width < 64 && viewRect.height < 64) {
+ return;
+ }
+
+ // Compute a scale with an appropriate aspect ratio
+ // We allocate up to 100px of width and the height of this layer.
+ float scaleFactor;
+ float scaleFactorX;
+ float scaleFactorY;
+ Rect dest = Rect(aClipRect.ToUnknownRect());
+ if (aLayer->GetLocalClipRect()) {
+ dest = Rect(aLayer->GetLocalClipRect().value().ToUnknownRect());
+ } else {
+ dest = aContainer->GetEffectiveTransform().Inverse().TransformBounds(dest);
+ }
+ dest = dest.Intersect(compositionBounds.ToUnknownRect());
+ scaleFactorX = std::min(100.f, dest.width - (2 * horizontalPadding)) / scrollRect.width;
+ scaleFactorY = (dest.height - (2 * verticalPadding)) / scrollRect.height;
+ scaleFactor = std::min(scaleFactorX, scaleFactorY);
+ if (scaleFactor <= 0) {
+ return;
+ }
+
+ Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1);
+ transform.PostTranslate(horizontalPadding + dest.x, verticalPadding + dest.y, 0);
+
+ Rect transformedScrollRect = transform.TransformBounds(scrollRect.ToUnknownRect());
+
+ IntRect clipRect = RoundedOut(aContainer->GetEffectiveTransform().TransformBounds(transformedScrollRect));
+
+ // Render the scrollable area.
+ compositor->FillRect(transformedScrollRect, backgroundColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(transformedScrollRect, pageBorderColor, clipRect, aContainer->GetEffectiveTransform());
+
+ // If enabled, render information about visibility.
+ if (gfxPrefs::APZMinimapVisibilityEnabled()) {
+ // Retrieve the APZC scrollable layer guid, which we'll use to get the
+ // appropriate visibility information from the layer manager.
+ AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
+ MOZ_ASSERT(controller);
+
+ ScrollableLayerGuid guid = controller->GetGuid();
+
+ // Get the approximately visible region.
+ static CSSIntRegion emptyRegion;
+ CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
+ if (!visibleRegion) {
+ visibleRegion = &emptyRegion;
+ }
+
+ // Iterate through and draw the rects in the region.
+ for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
+ !iterator.Done();
+ iterator.Next())
+ {
+ CSSIntRect rect = iterator.Get();
+ LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
+ Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
+ compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
+ }
+ }
+
+ // Render the displayport.
+ Rect r = transform.TransformBounds(dp.ToUnknownRect());
+ compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
+
+ // Render the critical displayport if there is one
+ if (cdp) {
+ r = transform.TransformBounds(cdp->ToUnknownRect());
+ compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
+ }
+
+ // Render the viewport.
+ r = transform.TransformBounds(viewRect.ToUnknownRect());
+ compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
+}
+
+
+template<class ContainerT> void
+RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect)
+{
+ Compositor* compositor = aManager->GetCompositor();
+
+ for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
+ PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+ LayerComposite* layerToRender = static_cast<LayerComposite*>(preparedData.mLayer->ImplData());
+ const RenderTargetIntRect& clipRect = preparedData.mClipRect;
+ Layer* layer = layerToRender->GetLayer();
+
+ if (layerToRender->HasStaleCompositor()) {
+ continue;
+ }
+
+ if (gfxPrefs::LayersDrawFPS()) {
+ for (const auto& metadata : layer->GetAllScrollMetadata()) {
+ if (metadata.IsApzForceDisabled()) {
+ aManager->DisabledApzWarning();
+ break;
+ }
+ }
+ }
+
+ Color color;
+ if (layerToRender->NeedToDrawCheckerboarding(&color)) {
+ if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
+ color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
+ }
+ // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
+ // and only fill in that area. However the layer bounds takes into account the base translation
+ // for the painted layer whereas the checkerboard region does not. One does not simply
+ // intersect areas in different coordinate spaces. So we do this a little more permissively
+ // and only fill in the background when we know there is checkerboard, which in theory
+ // should only occur transiently.
+ gfx::IntRect layerBounds = layer->GetLayerBounds();
+ EffectChain effectChain(layer);
+ effectChain.mPrimaryEffect = new EffectSolidColor(color);
+ aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
+ clipRect.ToUnknownRect(),
+ effectChain, layer->GetEffectiveOpacity(),
+ layer->GetEffectiveTransform());
+ }
+
+ if (layerToRender->HasLayerBeenComposited()) {
+ // Composer2D will compose this layer so skip GPU composition
+ // this time. The flag will be reset for the next composition phase
+ // at the beginning of LayerManagerComposite::Rener().
+ gfx::IntRect clearRect = layerToRender->GetClearRect();
+ if (!clearRect.IsEmpty()) {
+ // Clear layer's visible rect on FrameBuffer with transparent pixels
+ gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
+ compositor->ClearRect(fbRect);
+ layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
+ }
+ } else {
+ layerToRender->RenderLayer(clipRect.ToUnknownRect());
+ }
+
+ if (gfxPrefs::UniformityInfo()) {
+ PrintUniformityInfo(layer);
+ }
+
+ if (gfxPrefs::DrawLayerInfo()) {
+ DrawLayerInfo(clipRect, aManager, layer);
+ }
+
+ // Draw a border around scrollable layers.
+ // A layer can be scrolled by multiple scroll frames. Draw a border
+ // for each.
+ // Within the list of scroll frames for a layer, the layer border for a
+ // scroll frame lower down is affected by the async transforms on scroll
+ // frames higher up, so loop from the top down, and accumulate an async
+ // transform as we go along.
+ Matrix4x4 asyncTransform;
+ for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) {
+ if (layer->GetFrameMetrics(i - 1).IsScrollable()) {
+ // Since the composition bounds are in the parent layer's coordinates,
+ // use the parent's effective transform rather than the layer's own.
+ ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
+ aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
+ compositionBounds.ToUnknownRect(),
+ aClipRect.ToUnknownRect(),
+ asyncTransform * aContainer->GetEffectiveTransform());
+ if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
+ asyncTransform =
+ apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix()
+ * asyncTransform;
+ }
+ }
+ }
+
+ if (gfxPrefs::APZMinimap()) {
+ RenderMinimap(aContainer, aManager, aClipRect, layer);
+ }
+
+ // invariant: our GL context should be current here, I don't think we can
+ // assert it though
+ }
+}
+
+template<class ContainerT> RefPtr<CompositingRenderTarget>
+CreateOrRecycleTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ SurfaceInitMode mode = INIT_MODE_CLEAR;
+ gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+ if (aContainer->GetLocalVisibleRegion().GetNumRects() == 1 &&
+ (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE))
+ {
+ mode = INIT_MODE_NONE;
+ }
+
+ RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
+ if (lastSurf && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
+ if (mode == INIT_MODE_CLEAR) {
+ lastSurf->ClearOnBind();
+ }
+
+ return lastSurf;
+ } else {
+ lastSurf = compositor->CreateRenderTarget(surfaceRect, mode);
+
+ return lastSurf;
+ }
+}
+
+template<class ContainerT> RefPtr<CompositingRenderTarget>
+CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ gfx::IntRect visibleRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+ RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
+ gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height);
+
+ gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
+
+ gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
+ DebugOnly<gfx::Matrix> transform2d;
+ MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
+ sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
+
+ sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin();
+
+ return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint);
+}
+
+template<class ContainerT> void
+RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface)
+{
+ Compositor* compositor = aManager->GetCompositor();
+ RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
+
+ if (!surface) {
+ return;
+ }
+
+ compositor->SetRenderTarget(surface);
+ // pre-render all of the layers into our temporary
+ RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+ // Unbind the current surface and rebind the previous one.
+ compositor->SetRenderTarget(previousTarget);
+}
+
+template<class ContainerT> void
+ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect)
+{
+ MOZ_ASSERT(aContainer->mPrepared);
+
+ if (aContainer->UseIntermediateSurface()) {
+ RefPtr<CompositingRenderTarget> surface;
+
+ if (aContainer->mPrepared->mNeedsSurfaceCopy) {
+ // we needed to copy the background so we waited until now to render the intermediate
+ surface = CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager);
+ RenderIntermediate(aContainer, aManager,
+ aClipRect, surface);
+ } else {
+ surface = aContainer->mPrepared->mTmpTarget;
+ }
+
+ if (!surface) {
+ aContainer->mPrepared = nullptr;
+ return;
+ }
+
+ gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ RefPtr<Compositor> compositor = aManager->GetCompositor();
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
+ if (surf) {
+ WriteSnapshotToDumpFile(aContainer, surf);
+ }
+ }
+#endif
+
+ RefPtr<ContainerT> container = aContainer;
+ RenderWithAllMasks(aContainer, compositor, aClipRect,
+ [&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
+ effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
+ compositor->DrawQuad(visibleRect, clipRect, effectChain,
+ container->GetEffectiveOpacity(),
+ container->GetEffectiveTransform());
+ });
+ } else {
+ RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+ }
+ aContainer->mPrepared = nullptr;
+
+ // If it is a scrollable container layer with no child layers, and one of the APZCs
+ // attached to it has a nonempty async transform, then that transform is not applied
+ // to any visible content. Display a warning box (conditioned on the FPS display being
+ // enabled).
+ if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
+ // Since aContainer doesn't have any children we can just iterate from the top metrics
+ // on it down to the bottom using GetFirstChild and not worry about walking onto another
+ // underlying layer.
+ for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
+ if (AsyncPanZoomController* apzc = i.GetApzc()) {
+ if (!apzc->GetAsyncTransformAppliedToContent()
+ && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) {
+ aManager->UnusedApzTransformWarning();
+ break;
+ }
+ }
+ }
+ }
+}
+
+ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager)
+ : ContainerLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+{
+ MOZ_COUNT_CTOR(ContainerLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ContainerLayerComposite::~ContainerLayerComposite()
+{
+ MOZ_COUNT_DTOR(ContainerLayerComposite);
+
+ // We don't Destroy() on destruction here because this destructor
+ // can be called after remote content has crashed, and it may not be
+ // safe to free the IPC resources of our children. Those resources
+ // are automatically cleaned up by IPDL-generated code.
+ //
+ // In the common case of normal shutdown, either
+ // LayerManagerComposite::Destroy(), a parent
+ // *ContainerLayerComposite::Destroy(), or Disconnect() will trigger
+ // cleanup of our resources.
+ while (mFirstChild) {
+ RemoveChild(mFirstChild);
+ }
+}
+
+void
+ContainerLayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ while (mFirstChild) {
+ static_cast<LayerComposite*>(GetFirstChild()->ImplData())->Destroy();
+ RemoveChild(mFirstChild);
+ }
+ mDestroyed = true;
+ }
+}
+
+LayerComposite*
+ContainerLayerComposite::GetFirstChildComposite()
+{
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->ImplData());
+}
+
+void
+ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+void
+ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
+{
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void
+ContainerLayerComposite::CleanupResources()
+{
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+
+ for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+ LayerComposite* layerToCleanup = static_cast<LayerComposite*>(l->ImplData());
+ layerToCleanup->CleanupResources();
+ }
+}
+
+RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
+ : RefLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+{
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+RefLayerComposite::~RefLayerComposite()
+{
+ Destroy();
+}
+
+void
+RefLayerComposite::Destroy()
+{
+ MOZ_ASSERT(!mFirstChild);
+ mDestroyed = true;
+}
+
+LayerComposite*
+RefLayerComposite::GetFirstChildComposite()
+{
+ if (!mFirstChild) {
+ return nullptr;
+ }
+ return static_cast<LayerComposite*>(mFirstChild->ImplData());
+}
+
+void
+RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ ContainerRender(this, mCompositeManager, aClipRect);
+}
+
+void
+RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
+{
+ ContainerPrepare(this, mCompositeManager, aClipRect);
+}
+
+void
+RefLayerComposite::CleanupResources()
+{
+ mLastIntermediateSurface = nullptr;
+ mPrepared = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ContainerLayerComposite.h b/gfx/layers/composite/ContainerLayerComposite.h
new file mode 100644
index 000000000..5128b9d80
--- /dev/null
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ContainerLayerComposite_H
+#define GFX_ContainerLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+class CompositingRenderTarget;
+struct PreparedData;
+
+class ContainerLayerComposite : public ContainerLayer,
+ public LayerComposite
+{
+ template<class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateOrRecycleTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+
+ template<class ContainerT>
+ void RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect, Layer* aLayer);
+public:
+ explicit ContainerLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ ~ContainerLayerComposite();
+
+public:
+ // LayerComposite Implementation
+ virtual Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ virtual void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // container layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ // If the layer is marked as scale-to-resolution, add a post-scale
+ // to the layer's transform equal to the pres shell resolution we're
+ // scaling to. This cancels out the post scale of '1 / resolution'
+ // added by Layout. TODO: It would be nice to get rid of both of these
+ // post-scales.
+ virtual float GetPostXScale() const override {
+ if (mScaleToResolution) {
+ return mPostXScale * mPresShellResolution;
+ }
+ return mPostXScale;
+ }
+ virtual float GetPostYScale() const override {
+ if (mScaleToResolution) {
+ return mPostYScale * mPresShellResolution;
+ }
+ return mPostYScale;
+ }
+
+ virtual const char* Name() const override { return "ContainerLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+class RefLayerComposite : public RefLayer,
+ public LayerComposite
+{
+ template<class ContainerT>
+ friend void ContainerPrepare(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const RenderTargetIntRect& aClipRect);
+ template<class ContainerT>
+ friend void ContainerRender(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderLayers(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend void RenderIntermediate(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect,
+ RefPtr<CompositingRenderTarget> surface);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+ template<class ContainerT>
+ friend RefPtr<CompositingRenderTarget>
+ CreateTemporaryTarget(ContainerT* aContainer,
+ LayerManagerComposite* aManager,
+ const gfx::IntRect& aClipRect);
+
+public:
+ explicit RefLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ ~RefLayerComposite();
+
+public:
+ /** LayerOGL implementation */
+ Layer* GetLayer() override { return this; }
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override
+ {
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ mLastIntermediateSurface = nullptr;
+ }
+
+ void Destroy() override;
+
+ LayerComposite* GetFirstChildComposite() override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ virtual void CleanupResources() override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ // ref layers don't use a compositable
+ CompositableHost* GetCompositableHost() override { return nullptr; }
+
+ virtual const char* Name() const override { return "RefLayerComposite"; }
+ UniquePtr<PreparedData> mPrepared;
+ RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ContainerLayerComposite_H */
diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp
new file mode 100644
index 000000000..b1d92a6c9
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentHost.h"
+#include "LayersLogging.h" // for AppendToString
+#include "gfx2DGlue.h" // for ContentForFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "nsAString.h"
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+
+namespace mozilla {
+using namespace gfx;
+
+namespace layers {
+
+ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo)
+ , mInitialised(false)
+{}
+
+ContentHostBase::~ContentHostBase()
+{
+}
+
+void
+ContentHostTexture::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const SamplingFilter aSamplingFilter,
+ const IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ NS_ASSERTION(aVisibleRegion, "Requires a visible region");
+
+ AutoLockCompositableHost lock(this);
+ if (lock.Failed()) {
+ return;
+ }
+
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return;
+ }
+ MOZ_ASSERT(mTextureSource.get());
+
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect = CreateTexturedEffect(mTextureSource.get(),
+ mTextureSourceOnWhite.get(),
+ aSamplingFilter, true,
+ GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ aEffectChain.mPrimaryEffect = effect;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ } else {
+ renderRegion = aVisibleRegion;
+ }
+#else
+ renderRegion = aVisibleRegion;
+#endif
+
+ nsIntRegion region(*renderRegion);
+ nsIntPoint origin = GetOriginOffset();
+ // translate into TexImage space, buffer origin might not be at texture (0,0)
+ region.MoveBy(-origin);
+
+ // Figure out the intersecting draw region
+ gfx::IntSize texSize = mTextureSource->GetSize();
+ IntRect textureRect = IntRect(0, 0, texSize.width, texSize.height);
+ textureRect.MoveBy(region.GetBounds().TopLeft());
+ nsIntRegion subregion;
+ subregion.And(region, textureRect);
+ if (subregion.IsEmpty()) {
+ // Region is empty, nothing to draw
+ return;
+ }
+
+ nsIntRegion screenRects;
+ nsIntRegion regionRects;
+
+ // Collect texture/screen coordinates for drawing
+ for (auto iter = subregion.RectIter(); !iter.Done(); iter.Next()) {
+ IntRect regionRect = iter.Get();
+ IntRect screenRect = iter.Get();
+ screenRect.MoveBy(origin);
+
+ screenRects.Or(screenRects, screenRect);
+ regionRects.Or(regionRects, regionRect);
+ }
+
+ BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator();
+ BigImageIterator* iterOnWhite = nullptr;
+ if (bigImgIter) {
+ bigImgIter->BeginBigImageIteration();
+ }
+
+ if (mTextureSourceOnWhite) {
+ iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator();
+ MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
+ "Tile count mismatch on component alpha texture");
+ if (iterOnWhite) {
+ iterOnWhite->BeginBigImageIteration();
+ }
+ }
+
+ bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
+ do {
+ if (iterOnWhite && bigImgIter) {
+ MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
+ "component alpha textures should be the same size.");
+ }
+
+ IntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
+ : IntRect(0, 0,
+ texSize.width,
+ texSize.height);
+
+ // Draw texture. If we're using tiles, we do repeating manually, as texture
+ // repeat would cause each individual tile to repeat instead of the
+ // compound texture as a whole. This involves drawing at most 4 sections,
+ // 2 for each axis that has texture repeat.
+ for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
+ for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
+ IntRect currentTileRect(texRect);
+ currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
+
+ for (auto screenIter = screenRects.RectIter(),
+ regionIter = regionRects.RectIter();
+ !screenIter.Done() && !regionIter.Done();
+ screenIter.Next(), regionIter.Next()) {
+ const IntRect& screenRect = screenIter.Get();
+ const IntRect& regionRect = regionIter.Get();
+ IntRect tileScreenRect(screenRect);
+ IntRect tileRegionRect(regionRect);
+
+ // When we're using tiles, find the intersection between the tile
+ // rect and this region rect. Tiling is then handled by the
+ // outer for-loops and modifying the tile rect.
+ if (usingTiles) {
+ tileScreenRect.MoveBy(-origin);
+ tileScreenRect = tileScreenRect.Intersect(currentTileRect);
+ tileScreenRect.MoveBy(origin);
+
+ if (tileScreenRect.IsEmpty())
+ continue;
+
+ tileRegionRect = regionRect.Intersect(currentTileRect);
+ tileRegionRect.MoveBy(-currentTileRect.TopLeft());
+ }
+ gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
+ tileScreenRect.width, tileScreenRect.height);
+
+ effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
+ Float(tileRegionRect.y) / texRect.height,
+ Float(tileRegionRect.width) / texRect.width,
+ Float(tileRegionRect.height) / texRect.height);
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ if (usingTiles) {
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+ }
+ }
+
+ if (iterOnWhite) {
+ iterOnWhite->NextTile();
+ }
+ } while (usingTiles && bigImgIter->NextTile());
+
+ if (bigImgIter) {
+ bigImgIter->EndBigImageIteration();
+ }
+ if (iterOnWhite) {
+ iterOnWhite->EndBigImageIteration();
+ }
+
+ DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
+ if (iterOnWhite) {
+ diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
+ aTransform, mFlashCounter);
+}
+
+void
+ContentHostTexture::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ ContentHostBase::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() == 1);
+ const TimedTexture& t = aTextures[0];
+ MOZ_ASSERT(t.mPictureRect.IsEqualInterior(
+ nsIntRect(nsIntPoint(0, 0), nsIntSize(t.mTexture->GetSize()))),
+ "Only default picture rect supported");
+
+ if (t.mTexture != mTextureHost) {
+ mReceivedNewHost = true;
+ }
+
+ mTextureHost = t.mTexture;
+ mTextureHostOnWhite = nullptr;
+ mTextureSourceOnWhite = nullptr;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+}
+
+void
+ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite)
+{
+ ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite);
+ mTextureHost = aTextureOnBlack;
+ mTextureHostOnWhite = aTextureOnWhite;
+ if (mTextureHost) {
+ mTextureHost->PrepareTextureSource(mTextureSource);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->PrepareTextureSource(mTextureSourceOnWhite);
+ }
+}
+
+void
+ContentHostTexture::SetCompositor(Compositor* aCompositor)
+{
+ ContentHostBase::SetCompositor(aCompositor);
+ if (mTextureHost) {
+ mTextureHost->SetCompositor(aCompositor);
+ }
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+}
+
+void
+ContentHostTexture::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+#ifdef MOZ_DUMP_PAINTING
+ if (aDumpHtml) {
+ aStream << "<ul>";
+ }
+ if (mTextureHost) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer: ";
+ }
+ DumpTextureHost(aStream, mTextureHost);
+ if (aDumpHtml) {
+ aStream << "> Front buffer </a></li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (mTextureHostOnWhite) {
+ aStream << aPrefix;
+ if (aDumpHtml) {
+ aStream << "<li> <a href=";
+ } else {
+ aStream << "Front buffer on white: ";
+ }
+ DumpTextureHost(aStream, mTextureHostOnWhite);
+ if (aDumpHtml) {
+ aStream << "> Front buffer on white </a> </li> ";
+ } else {
+ aStream << "\n";
+ }
+ }
+ if (aDumpHtml) {
+ aStream << "</ul>";
+ }
+#endif
+}
+
+static inline void
+AddWrappedRegion(const nsIntRegion& aInput, nsIntRegion& aOutput,
+ const IntSize& aSize, const nsIntPoint& aShift)
+{
+ nsIntRegion tempRegion;
+ tempRegion.And(IntRect(aShift, aSize), aInput);
+ tempRegion.MoveBy(-aShift);
+ aOutput.Or(aOutput, tempRegion);
+}
+
+bool
+ContentHostSingleBuffered::UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ aUpdatedRegionBack->SetEmpty();
+
+ if (!mTextureHost) {
+ mInitialised = false;
+ return true; // FIXME should we return false? Returning true for now
+ } // to preserve existing behavior of NOT causing IPC errors.
+
+ // updated is in screen coordinates. Convert it to buffer coordinates.
+ nsIntRegion destRegion(aUpdated);
+
+ if (mReceivedNewHost) {
+ destRegion.Or(destRegion, aOldValidRegionBack);
+ mReceivedNewHost = false;
+ }
+ destRegion.MoveBy(-aData.rect().TopLeft());
+
+ if (!aData.rect().Contains(aUpdated.GetBounds()) ||
+ aData.rotation().x > aData.rect().width ||
+ aData.rotation().y > aData.rect().height) {
+ NS_ERROR("Invalid update data");
+ return false;
+ }
+
+ // destRegion is now in logical coordinates relative to the buffer, but we
+ // need to account for rotation. We do that by moving the region to the
+ // rotation offset and then wrapping any pixels that extend off the
+ // bottom/right edges.
+
+ // Shift to the rotation point
+ destRegion.MoveBy(aData.rotation());
+
+ IntSize bufferSize = aData.rect().Size();
+
+ // Select only the pixels that are still within the buffer.
+ nsIntRegion finalRegion;
+ finalRegion.And(IntRect(IntPoint(), bufferSize), destRegion);
+
+ // For each of the overlap areas (right, bottom-right, bottom), select those
+ // pixels and wrap them around to the opposite edge of the buffer rect.
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, 0));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, aData.rect().height));
+ AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(0, aData.rect().height));
+
+ MOZ_ASSERT(IntRect(0, 0, aData.rect().width, aData.rect().height).Contains(finalRegion.GetBounds()));
+
+ mTextureHost->Updated(&finalRegion);
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated(&finalRegion);
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ return true;
+}
+
+bool
+ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack)
+{
+ if (!mTextureHost) {
+ mInitialised = false;
+
+ *aUpdatedRegionBack = aUpdated;
+ return true;
+ }
+
+ // We don't need to calculate an update region because we assume that if we
+ // are using double buffering then we have render-to-texture and thus no
+ // upload to do.
+ mTextureHost->Updated();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Updated();
+ }
+ mInitialised = true;
+
+ mBufferRect = aData.rect();
+ mBufferRotation = aData.rotation();
+
+ *aUpdatedRegionBack = aUpdated;
+
+ // Save the current valid region of our front buffer, because if
+ // we're double buffering, it's going to be the valid region for the
+ // next back buffer sent back to the renderer.
+ //
+ // NB: we rely here on the fact that mValidRegion is initialized to
+ // empty, and that the first time Swap() is called we don't have a
+ // valid front buffer that we're going to return to content.
+ mValidRegionForNextBackBuffer = aOldValidRegionBack;
+
+ return true;
+}
+
+void
+ContentHostTexture::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentHost (0x%p)", this).get();
+
+ AppendToString(aStream, mBufferRect, " [buffer-rect=", "]");
+ AppendToString(aStream, mBufferRotation, " [buffer-rotation=", "]");
+ if (PaintWillResample()) {
+ aStream << " [paint-will-resample]";
+ }
+
+ if (mTextureHost) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n";
+ mTextureHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+
+LayerRenderState
+ContentHostTexture::GetRenderState()
+{
+ if (!mTextureHost) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState result = mTextureHost->GetRenderState();
+
+ if (mBufferRotation != nsIntPoint()) {
+ result.mFlags |= LayerRenderStateFlags::BUFFER_ROTATION;
+ }
+ result.SetOffset(GetOriginOffset());
+ return result;
+}
+
+already_AddRefed<TexturedEffect>
+ContentHostTexture::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ if (!mTextureHost->BindTextureSource(mTextureSource)) {
+ return nullptr;
+ }
+ if (!mTextureHostOnWhite) {
+ mTextureSourceOnWhite = nullptr;
+ }
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+ return nullptr;
+ }
+ return CreateTexturedEffect(mTextureSource.get(),
+ mTextureSourceOnWhite.get(),
+ aSamplingFilter, true,
+ GetRenderState());
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+ContentHostTexture::GetAsSurface()
+{
+ if (!mTextureHost) {
+ return nullptr;
+ }
+
+ return mTextureHost->GetAsSurface();
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ContentHost.h b/gfx/layers/composite/ContentHost.h
new file mode 100644
index 000000000..9b7498415
--- /dev/null
+++ b/gfx/layers/composite/ContentHost.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CONTENTHOST_H
+#define GFX_CONTENTHOST_H
+
+#include <stdint.h> // for uint32_t
+#include <stdio.h> // for FILE
+#include "mozilla-config.h" // for MOZ_DUMP_PAINTING
+#include "CompositableHost.h" // for CompositableHost, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+class Compositor;
+class ThebesBufferData;
+struct EffectChain;
+
+struct TexturedEffect;
+
+/**
+ * ContentHosts are used for compositing Painted layers, always matched by a
+ * ContentClient of the same type.
+ *
+ * ContentHosts support only UpdateThebes(), not Update().
+ */
+class ContentHost : public CompositableHost
+{
+public:
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack) = 0;
+
+ virtual void SetPaintWillResample(bool aResample) { mPaintWillResample = aResample; }
+ bool PaintWillResample() { return mPaintWillResample; }
+
+ // We use this to allow TiledContentHost to invalidate regions where
+ // tiles are fading in.
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) { }
+
+protected:
+ explicit ContentHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo)
+ , mPaintWillResample(false)
+ {}
+
+ bool mPaintWillResample;
+};
+
+/**
+ * Base class for non-tiled ContentHosts.
+ *
+ * Ownership of the SurfaceDescriptor and the resources it represents is passed
+ * from the ContentClient to the ContentHost when the TextureClient/Hosts are
+ * created, that is recevied here by SetTextureHosts which assigns one or two
+ * texture hosts (for single and double buffering) to the ContentHost.
+ *
+ * It is the responsibility of the ContentHost to destroy its resources when
+ * they are recreated or the ContentHost dies.
+ */
+class ContentHostBase : public ContentHost
+{
+public:
+ typedef RotatedContentBuffer::ContentType ContentType;
+ typedef RotatedContentBuffer::PaintState PaintState;
+
+ explicit ContentHostBase(const TextureInfo& aTextureInfo);
+ virtual ~ContentHostBase();
+
+protected:
+ virtual nsIntPoint GetOriginOffset()
+ {
+ return mBufferRect.TopLeft() - mBufferRotation;
+ }
+
+
+ gfx::IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+ bool mInitialised;
+};
+
+/**
+ * Shared ContentHostBase implementation for content hosts that
+ * use up to two TextureHosts.
+ */
+class ContentHostTexture : public ContentHostBase
+{
+public:
+ explicit ContentHostTexture(const TextureInfo& aTextureInfo)
+ : ContentHostBase(aTextureInfo)
+ , mLocked(false)
+ , mReceivedNewHost(false)
+ { }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+ virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
+ TextureHost* aTextureOnWhite) override;
+
+ virtual bool Lock() override {
+ MOZ_ASSERT(!mLocked);
+ if (!mTextureHost) {
+ return false;
+ }
+ if (!mTextureHost->Lock()) {
+ return false;
+ }
+
+ if (mTextureHostOnWhite && !mTextureHostOnWhite->Lock()) {
+ return false;
+ }
+
+ mLocked = true;
+ return true;
+ }
+ virtual void Unlock() override {
+ MOZ_ASSERT(mLocked);
+ mTextureHost->Unlock();
+ if (mTextureHostOnWhite) {
+ mTextureHostOnWhite->Unlock();
+ }
+ mLocked = false;
+ }
+
+ LayerRenderState GetRenderState() override;
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+protected:
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ CompositableTextureSourceRef mTextureSource;
+ CompositableTextureSourceRef mTextureSourceOnWhite;
+ bool mLocked;
+ bool mReceivedNewHost;
+};
+
+/**
+ * Double buffering is implemented by swapping the front and back TextureHosts.
+ * We assume that whenever we use double buffering, then we have
+ * render-to-texture and thus no texture upload to do.
+ */
+class ContentHostDoubleBuffered : public ContentHostTexture
+{
+public:
+ explicit ContentHostDoubleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo)
+ {}
+
+ virtual ~ContentHostDoubleBuffered() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_DOUBLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+
+protected:
+ nsIntRegion mValidRegionForNextBackBuffer;
+};
+
+/**
+ * Single buffered, therefore we must synchronously upload the image from the
+ * TextureHost in the layers transaction (i.e., in UpdateThebes).
+ */
+class ContentHostSingleBuffered : public ContentHostTexture
+{
+public:
+ explicit ContentHostSingleBuffered(const TextureInfo& aTextureInfo)
+ : ContentHostTexture(aTextureInfo)
+ {}
+ virtual ~ContentHostSingleBuffered() {}
+
+ virtual CompositableType GetType() { return CompositableType::CONTENT_SINGLE; }
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/FPSCounter.cpp b/gfx/layers/composite/FPSCounter.cpp
new file mode 100644
index 000000000..02ffc4b2c
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -0,0 +1,450 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stddef.h> // for size_t
+#include "Units.h" // for ScreenIntRect
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsIFile.h" // for nsIFile
+#include "nsDirectoryServiceDefs.h" // for NS_OS_TMP_DIR
+#include "mozilla/Sprintf.h"
+#include "FPSCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+FPSCounter::FPSCounter(const char* aName)
+ : mWriteIndex(0)
+ , mIteratorIndex(-1)
+ , mFPSName(aName)
+{
+ Init();
+}
+
+FPSCounter::~FPSCounter() { }
+
+void
+FPSCounter::Init()
+{
+ for (int i = 0; i < kMaxFrames; i++) {
+ mFrameTimestamps.AppendElement(TimeStamp());
+ }
+ mLastInterval = TimeStamp::Now();
+}
+
+// Returns true if we captured a full interval of data
+bool
+FPSCounter::CapturedFullInterval(TimeStamp aTimestamp) {
+ TimeDuration duration = aTimestamp - mLastInterval;
+ return duration.ToSeconds() >= kFpsDumpInterval;
+}
+
+void
+FPSCounter::AddFrame(TimeStamp aTimestamp) {
+ NS_ASSERTION(mWriteIndex < kMaxFrames, "We probably have a bug with the circular buffer");
+ NS_ASSERTION(mWriteIndex >= 0, "Circular Buffer index should never be negative");
+
+ int index = mWriteIndex++;
+ if (mWriteIndex == kMaxFrames) {
+ mWriteIndex = 0;
+ }
+
+ mFrameTimestamps[index] = aTimestamp;
+
+ if (CapturedFullInterval(aTimestamp)) {
+ PrintFPS();
+ WriteFrameTimeStamps();
+ mLastInterval = aTimestamp;
+ }
+}
+
+double
+FPSCounter::AddFrameAndGetFps(TimeStamp aTimestamp) {
+ AddFrame(aTimestamp);
+ return GetFPS(aTimestamp);
+}
+
+int
+FPSCounter::GetLatestReadIndex()
+{
+ if (mWriteIndex == 0) {
+ return kMaxFrames - 1;
+ }
+
+ return mWriteIndex - 1;
+}
+
+TimeStamp
+FPSCounter::GetLatestTimeStamp()
+{
+ TimeStamp timestamp = mFrameTimestamps[GetLatestReadIndex()];
+ MOZ_ASSERT(!timestamp.IsNull(), "Cannot use null timestamps");
+ return timestamp;
+}
+
+// Returns true if we iterated over a full interval of data
+bool
+FPSCounter::IteratedFullInterval(TimeStamp aTimestamp, double aDuration) {
+ MOZ_ASSERT(mIteratorIndex >= 0, "Cannot be negative");
+ MOZ_ASSERT(mIteratorIndex < kMaxFrames, "Iterator index cannot be greater than kMaxFrames");
+
+ TimeStamp currentStamp = mFrameTimestamps[mIteratorIndex];
+ TimeDuration duration = aTimestamp - currentStamp;
+ return duration.ToSeconds() >= aDuration;
+}
+
+void
+FPSCounter::ResetReverseIterator()
+{
+ mIteratorIndex = GetLatestReadIndex();
+}
+
+/***
+ * Returns true if we have another timestamp that is valid and
+ * is within the given duration that we're interested in.
+ * Duration is in seconds
+ */
+bool FPSCounter::HasNext(TimeStamp aTimestamp, double aDuration)
+{
+ // Order of evaluation here has to stay the same
+ // otherwise IteratedFullInterval reads from mFrameTimestamps which cannot
+ // be null
+ return (mIteratorIndex != mWriteIndex) // Didn't loop around the buffer
+ && !mFrameTimestamps[mIteratorIndex].IsNull() // valid data
+ && !IteratedFullInterval(aTimestamp, aDuration);
+}
+
+TimeStamp
+FPSCounter::GetNextTimeStamp()
+{
+ TimeStamp timestamp = mFrameTimestamps[mIteratorIndex--];
+ MOZ_ASSERT(!timestamp.IsNull(), "Reading Invalid Timestamp Data");
+
+ if (mIteratorIndex == -1) {
+ mIteratorIndex = kMaxFrames - 1;
+ }
+ return timestamp;
+}
+
+/**
+ * GetFPS calculates how many frames we've already composited from the current
+ * frame timestamp and we iterate from the latest timestamp we recorded,
+ * going back in time. When we hit a frame that is longer than the 1 second
+ * from the current composited frame, we return how many frames we've counted.
+ * Just a visualization:
+ *
+ * aTimestamp
+ * Frames: 1 2 3 4 5 6 7 8 9 10 11 12
+ * Time -------------------------->
+ *
+ * GetFPS iterates from aTimestamp, which is the current frame.
+ * Then starting at frame 12, going back to frame 11, 10, etc, we calculate
+ * the duration of the recorded frame timestamp from aTimestamp.
+ * Once duration is greater than 1 second, we return how many frames
+ * we composited.
+ */
+double
+FPSCounter::GetFPS(TimeStamp aTimestamp)
+{
+ int frameCount = 0;
+ int duration = 1.0; // Only care about the last 1s of data
+
+ ResetReverseIterator();
+ while (HasNext(aTimestamp, duration)) {
+ GetNextTimeStamp();
+ frameCount++;
+ }
+
+ return frameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+int
+FPSCounter::BuildHistogram(std::map<int, int>& aFpsData)
+{
+ TimeStamp currentIntervalStart = GetLatestTimeStamp();
+ TimeStamp currentTimeStamp = GetLatestTimeStamp();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ int frameCount = 0;
+ int totalFrameCount = 0;
+
+ ResetReverseIterator();
+ while (HasNext(startTimeStamp)) {
+ currentTimeStamp = GetNextTimeStamp();
+ TimeDuration interval = currentIntervalStart - currentTimeStamp;
+
+ if (interval.ToSeconds() >= 1.0 ) {
+ currentIntervalStart = currentTimeStamp;
+ aFpsData[frameCount]++;
+ frameCount = 0;
+ }
+
+ frameCount++;
+ totalFrameCount++;
+ }
+
+ TimeDuration totalTime = currentIntervalStart - currentTimeStamp;
+ printf_stderr("Discarded %d frames over %f ms in histogram for %s\n",
+ frameCount, totalTime.ToMilliseconds(), mFPSName);
+ return totalFrameCount;
+}
+
+// Iterate the same way we do in GetFPS()
+void
+FPSCounter::WriteFrameTimeStamps(PRFileDesc* fd)
+{
+ const int bufferSize = 256;
+ char buffer[bufferSize];
+ int writtenCount = SprintfLiteral(buffer, "FPS Data for: %s\n", mFPSName);
+ MOZ_ASSERT(writtenCount >= 0);
+ PR_Write(fd, buffer, writtenCount);
+
+ ResetReverseIterator();
+ TimeStamp startTimeStamp = GetLatestTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp previousSample = GetNextTimeStamp();
+
+ MOZ_ASSERT(HasNext(startTimeStamp));
+ TimeStamp nextTimeStamp = GetNextTimeStamp();
+
+ while (HasNext(startTimeStamp)) {
+ TimeDuration duration = previousSample - nextTimeStamp;
+ writtenCount = SprintfLiteral(buffer, "%f,\n", duration.ToMilliseconds());
+
+ MOZ_ASSERT(writtenCount >= 0);
+ PR_Write(fd, buffer, writtenCount);
+
+ previousSample = nextTimeStamp;
+ nextTimeStamp = GetNextTimeStamp();
+ }
+}
+
+double
+FPSCounter::GetMean(std::map<int, int> aHistogram)
+{
+ double average = 0.0;
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ average += fps * count;
+ samples += count;
+ }
+
+ return average / samples;
+}
+
+double
+FPSCounter::GetStdDev(std::map<int, int> aHistogram)
+{
+ double sumOfDifferences = 0;
+ double average = GetMean(aHistogram);
+ double samples = 0.0;
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); ++iter)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ double diff = ((double) fps) - average;
+ diff *= diff;
+
+ for (int i = 0; i < count; i++) {
+ sumOfDifferences += diff;
+ }
+ samples += count;
+ }
+
+ double stdDev = sumOfDifferences / samples;
+ return sqrt(stdDev);
+}
+
+void
+FPSCounter::PrintFPS()
+{
+ if (!gfxPrefs::FPSPrintHistogram()) {
+ return;
+ }
+
+ std::map<int, int> histogram;
+ int totalFrames = BuildHistogram(histogram);
+
+ TimeDuration measurementInterval = mFrameTimestamps[GetLatestReadIndex()] - mLastInterval;
+ printf_stderr("FPS for %s. Total Frames: %d Time Interval: %f seconds\n",
+ mFPSName, totalFrames, measurementInterval.ToSecondsSigDigits());
+
+ PrintHistogram(histogram);
+}
+
+void
+FPSCounter::PrintHistogram(std::map<int, int>& aHistogram)
+{
+ int length = 0;
+ const int kBufferLength = 512;
+ char buffer[kBufferLength];
+
+ for (std::map<int, int>::iterator iter = aHistogram.begin();
+ iter != aHistogram.end(); iter++)
+ {
+ int fps = iter->first;
+ int count = iter->second;
+
+ length += snprintf(buffer + length, kBufferLength - length,
+ "FPS: %d = %d. ", fps, count);
+ NS_ASSERTION(length >= kBufferLength, "Buffer overrun while printing FPS histogram.");
+ }
+
+ printf_stderr("%s\n", buffer);
+ printf_stderr("Mean: %f , std dev %f\n", GetMean(aHistogram), GetStdDev(aHistogram));
+}
+
+// Write FPS timestamp data to a file only if
+// draw-fps.write-to-file is true
+nsresult
+FPSCounter::WriteFrameTimeStamps()
+{
+ if (!gfxPrefs::WriteFPSToFile()) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mWriteIndex == 0);
+
+ nsCOMPtr<nsIFile> resultFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(resultFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!strncmp(mFPSName, "Compositor", strlen(mFPSName))) {
+ resultFile->Append(NS_LITERAL_STRING("fps.txt"));
+ } else {
+ resultFile->Append(NS_LITERAL_STRING("txn.txt"));
+ }
+
+ PRFileDesc* fd = nullptr;
+ int mode = 644;
+ int openFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
+ rv = resultFile->OpenNSPRFileDesc(openFlags, mode, &fd);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ WriteFrameTimeStamps(fd);
+ PR_Close(fd);
+
+ nsAutoCString path;
+ rv = resultFile->GetNativePath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf_stderr("Wrote FPS data to file: %s\n", path.get());
+ return NS_OK;
+}
+
+FPSState::FPSState()
+ : mCompositionFps("Compositor")
+ , mTransactionFps("LayerTransactions")
+{
+}
+
+// Size of the builtin font.
+static const float FontHeight = 7.f;
+static const float FontWidth = 4.f;
+
+// Scale the font when drawing it to the viewport for better readability.
+static const float FontScaleX = 2.f;
+static const float FontScaleY = 3.f;
+
+static void DrawDigits(unsigned int aValue,
+ int aOffsetX, int aOffsetY,
+ Compositor* aCompositor,
+ EffectChain& aEffectChain)
+{
+ if (aValue > 999) {
+ aValue = 999;
+ }
+
+ unsigned int divisor = 100;
+ float textureWidth = FontWidth * 10;
+ gfx::Float opacity = 1;
+ gfx::Matrix4x4 transform;
+ transform.PreScale(FontScaleX, FontScaleY, 1);
+
+ for (size_t n = 0; n < 3; ++n) {
+ unsigned int digit = aValue % (divisor * 10) / divisor;
+ divisor /= 10;
+
+ RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
+
+ Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
+ IntRect clipRect = IntRect(0, 0, 300, 100);
+ aCompositor->DrawQuad(drawRect, clipRect, aEffectChain, opacity, transform);
+ }
+}
+
+void FPSState::DrawFPS(TimeStamp aNow,
+ int aOffsetX, int aOffsetY,
+ unsigned int aFillRatio,
+ Compositor* aCompositor)
+{
+ if (!mFPSTextureSource) {
+ const char *text =
+ " "
+ " XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
+ " X X X X X X X X X X X X X X"
+ " X X X XXX XXX XXX XXX XXX X XXX XXX"
+ " X X X X X X X X X X X X X"
+ " XXX XXX XXX XXX X XXX XXX X XXX X"
+ " ";
+
+ // Convert the text encoding above to RGBA.
+ int w = FontWidth * 10;
+ int h = FontHeight;
+ uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ uint32_t purple = 0xfff000ff;
+ uint32_t white = 0xffffffff;
+ buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
+ }
+ }
+
+ int bytesPerPixel = 4;
+ RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mFPSTextureSource = aCompositor->CreateDataTextureSource();
+ mFPSTextureSource->Update(fpsSurface);
+ }
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8,
+ mFPSTextureSource,
+ SamplingFilter::POINT,
+ true);
+
+ unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
+ unsigned int txnFps = unsigned(mTransactionFps.GetFPS(aNow));
+
+ DrawDigits(fps, aOffsetX + 0, aOffsetY, aCompositor, effectChain);
+ DrawDigits(txnFps, aOffsetX + FontWidth * 4, aOffsetY, aCompositor, effectChain);
+ DrawDigits(aFillRatio, aOffsetX + FontWidth * 8, aOffsetY, aCompositor, effectChain);
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/gfx/layers/composite/FPSCounter.h b/gfx/layers/composite/FPSCounter.h
new file mode 100644
index 000000000..7a59267fc
--- /dev/null
+++ b/gfx/layers/composite/FPSCounter.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_opengl_FPSCounter_h_
+#define mozilla_layers_opengl_FPSCounter_h_
+
+#include <algorithm> // for min
+#include <stddef.h> // for size_t
+#include <map> // for std::map
+#include "GLDefs.h" // for GLuint
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "nsTArray.h" // for AutoTArray, nsTArray_Impl, etc
+#include "prio.h" // for NSPR file i/o
+
+namespace mozilla {
+namespace layers {
+
+class DataTextureSource;
+class Compositor;
+
+// Dump the FPS histogram every 10 seconds or kMaxFrameFPS
+const int kFpsDumpInterval = 10;
+
+// On desktop, we can have 240 hz monitors, so 10 seconds
+// times 240 frames = 2400
+const int kMaxFrames = 2400;
+
+/**
+ * The FPSCounter tracks how often we composite or have a layer transaction.
+ * At each composite / layer transaction, we record the timestamp.
+ * After kFpsDumpInterval number of composites / transactions, we calculate
+ * the average and standard deviation of frames composited. We dump a histogram,
+ * which allows for more statistically significant measurements. We also dump
+ * absolute frame composite times to a file on the device.
+ * The FPS counters displayed on screen are based on how many frames we
+ * composited within the last ~1 second. The more accurate measurement is to
+ * grab the histogram from stderr or grab the FPS timestamp dumps written to file.
+ *
+ * To enable dumping to file, enable
+ * layers.acceleration.draw-fps.write-to-file pref.
+
+ double AddFrameAndGetFps(TimeStamp aCurrentFrame) {
+ AddFrame(aCurrentFrame);
+ return EstimateFps(aCurrentFrame);
+ }
+ * To enable printing histogram data to logcat,
+ * enable layers.acceleration.draw-fps.print-histogram
+ *
+ * Use the HasNext(), GetNextTimeStamp() like an iterator to read the data,
+ * backwards in time. This abstracts away the mechanics of reading the data.
+ */
+class FPSCounter {
+public:
+ explicit FPSCounter(const char* aName);
+ ~FPSCounter();
+
+ void AddFrame(TimeStamp aTimestamp);
+ double AddFrameAndGetFps(TimeStamp aTimestamp);
+ double GetFPS(TimeStamp aTimestamp);
+
+private:
+ void Init();
+ bool CapturedFullInterval(TimeStamp aTimestamp);
+
+ // Used while iterating backwards over the data
+ void ResetReverseIterator();
+ bool HasNext(TimeStamp aTimestamp, double aDuration = kFpsDumpInterval);
+ TimeStamp GetNextTimeStamp();
+ int GetLatestReadIndex();
+ TimeStamp GetLatestTimeStamp();
+ void WriteFrameTimeStamps(PRFileDesc* fd);
+ bool IteratedFullInterval(TimeStamp aTimestamp, double aDuration);
+
+ void PrintFPS();
+ int BuildHistogram(std::map<int, int>& aHistogram);
+ void PrintHistogram(std::map<int, int>& aHistogram);
+ double GetMean(std::map<int,int> aHistogram);
+ double GetStdDev(std::map<int, int> aHistogram);
+ nsresult WriteFrameTimeStamps();
+
+ /***
+ * mFrameTimestamps is a psuedo circular buffer
+ * Since we have a constant write time and don't
+ * read at an offset except our latest write
+ * we don't need an explicit read pointer.
+ */
+ AutoTArray<TimeStamp, kMaxFrames> mFrameTimestamps;
+ int mWriteIndex; // points to next open write slot
+ int mIteratorIndex; // used only when iterating
+ const char* mFPSName;
+ TimeStamp mLastInterval;
+};
+
+struct FPSState {
+ FPSState();
+ void DrawFPS(TimeStamp, int offsetX, int offsetY, unsigned, Compositor* aCompositor);
+ void NotifyShadowTreeTransaction() {
+ mTransactionFps.AddFrame(TimeStamp::Now());
+ }
+
+ FPSCounter mCompositionFps;
+ FPSCounter mTransactionFps;
+
+private:
+ RefPtr<DataTextureSource> mFPSTextureSource;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_FPSCounter_h_
diff --git a/gfx/layers/composite/FontData.h b/gfx/layers/composite/FontData.h
new file mode 100644
index 000000000..f8c73cc99
--- /dev/null
+++ b/gfx/layers/composite/FontData.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is explicitly not guarded as we want only 1 file to include this and
+// it's good if things break if someone else does.
+const unsigned char sFontPNG[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0xb, 0xfe, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0xd1, 0xb6, 0xe3, 0x20, 0x8, 0x74, 0x72, 0xf2, 0xff, 0xbf, 0x3c, 0xfb, 0xd0, 0xbb, 0xbd, 0x51, 0x8, 0x48, 0xd4, 0xc4, 0xde, 0xda, 0xb3, 0x67, 0x77, 0x6d, 0x13, 0x63, 0x10, 0x81, 0x41, 0x40, 0x30, 0x7d, 0xf7, 0x67, 0x4b, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x2f, 0xdc, 0x3, 0xc6, 0xda, 0x76, 0x67, 0x29, 0xa5, 0x94, 0xb8, 0x38, 0x20, 0x42, 0x33, 0xc0, 0x68, 0xaa, 0x37, 0x64, 0x14, 0x47, 0xc2, 0x67, 0x13, 0x80, 0x5, 0xbf, 0x91, 0xb0, 0x5f, 0x9f, 0x73, 0x71, 0x40, 0x39, 0x61, 0x1c, 0xcd, 0xff, 0x39, 0x7d, 0x8, 0x1e, 0x29, 0x72, 0x3f, 0x1, 0x90, 0x12, 0x8a, 0xf9, 0xc, 0x52, 0x20, 0x9f, 0x51, 0x2, 0x8e, 0xd0, 0x99, 0x8d, 0x3, 0x7a, 0x2f, 0x2, 0x7a, 0x4, 0xa4, 0x90, 0xba, 0x8, 0x68, 0x1, 0x28, 0x32, 0x1a, 0x87, 0x6f, 0x50, 0x71, 0x7f, 0x5f, 0x29, 0xf, 0x85, 0xa3, 0x18, 0x5b, 0x10, 0xc, 0x10, 0x80, 0xf2, 0x1d, 0x79, 0x4e, 0x5e, 0x8f, 0xfc, 0xaf, 0x1, 0xb0, 0x60, 0x50, 0x4, 0x17, 0x40, 0xc6, 0xd3, 0xf4, 0xd5, 0x2e, 0x3a, 0xdb, 0x1, 0x5d, 0x85, 0x78, 0x8a, 0xbd, 0x7e, 0x33, 0xc3, 0x24, 0xe4, 0x52, 0x70, 0xff, 0xbc, 0xf5, 0x8f, 0xf0, 0x82, 0xe2, 0x5c, 0x1c, 0xd0, 0x9b, 0x83, 0x1c, 0x7a, 0x50, 0xb4, 0x33, 0xa9, 0xf9, 0xf5, 0x58, 0x0, 0xb, 0xe, 0x2f, 0x2, 0x2c, 0x2, 0x7c, 0x3d, 0x1, 0xea, 0x8c, 0xb9, 0xc, 0x4f, 0x16, 0x70, 0x14, 0x21, 0xc5, 0x8e, 0x60, 0xfb, 0x1, 0xe, 0x80, 0xf5, 0xc2, 0x52, 0x8c, 0xb6, 0x9, 0x52, 0xdc, 0xfb, 0xc6, 0x35, 0x4, 0x40, 0xa, 0x81, 0x8d, 0x66, 0xbd, 0xce, 0xc9, 0x8, 0x80, 0x6f, 0x93, 0x1, 0x6d, 0xef, 0xcf, 0xf1, 0xf4, 0xc2, 0xc0, 0xf9, 0xd9, 0xdb, 0xe7, 0x7f, 0x30, 0x5, 0x50, 0x98, 0x6b, 0x48, 0xe8, 0x9, 0xaf, 0x77, 0x14, 0xf8, 0xbe, 0xf5, 0x7d, 0xa0, 0xfa, 0xb, 0x18, 0xa2, 0x6f, 0x3e, 0x9e, 0xc2, 0x5c, 0x65, 0x5f, 0xe3, 0x75, 0xa7, 0x30, 0x87, 0x3d, 0xa, 0xd8, 0x42, 0x8b, 0x8a, 0x3f, 0x81, 0xb6, 0x7f, 0xa1, 0xa6, 0x8f, 0x80, 0x7, 0xa2, 0x5d, 0x8, 0xd2, 0xd1, 0xfb, 0x7f, 0x4a, 0x4a, 0x6e, 0xfe, 0xc, 0x3c, 0xab, 0xa6, 0x9e, 0x31, 0x84, 0x4c, 0x9e, 0xa3, 0xfb, 0x45, 0x47, 0x7f, 0xc5, 0xc2, 0x2, 0xcb, 0x1f, 0xb0, 0x38, 0x60, 0x11, 0x60, 0x7a, 0x2, 0xe0, 0x6f, 0x11, 0x0, 0x1e, 0x5a, 0x85, 0x3, 0x97, 0x5f, 0xde, 0x1, 0x94, 0x5f, 0x9c, 0x93, 0x2b, 0xf2, 0x73, 0x77, 0x68, 0xd1, 0xce, 0x1, 0x60, 0x6e, 0x3b, 0x82, 0x44, 0x26, 0x5c, 0xe1, 0x6d, 0x5f, 0xd3, 0xb2, 0x43, 0x91, 0xb4, 0xcd, 0xb0, 0x27, 0x97, 0x0, 0x12, 0x4, 0x53, 0xc, 0x54, 0x25, 0x44, 0x6f, 0x4d, 0x95, 0x77, 0xe7, 0x13, 0x80, 0xd2, 0x30, 0xf4, 0x2d, 0x1f, 0xf0, 0xb4, 0x45, 0x47, 0x11, 0x13, 0xb7, 0xda, 0x49, 0x7b, 0xf7, 0x29, 0x22, 0x12, 0xc1, 0x23, 0xa2, 0x3, 0x1b, 0xec, 0xd, 0x30, 0xc7, 0x7f, 0x3f, 0x4b, 0x82, 0x9d, 0x18, 0xe0, 0x2, 0x1, 0xca, 0xdd, 0xd9, 0x32, 0xe4, 0xe2, 0x35, 0xb6, 0xdf, 0xab, 0xd0, 0x86, 0xaf, 0x59, 0xf0, 0x80, 0x4b, 0xcc, 0xe0, 0xd6, 0xe1, 0x7e, 0x41, 0x4, 0xb8, 0xf, 0x11, 0xf8, 0x9a, 0x37, 0x1a, 0x9c, 0xc1, 0x27, 0x6d, 0xd1, 0xe, 0x5e, 0x22, 0x80, 0x21, 0x99, 0xcc, 0x89, 0xc, 0xee, 0x72, 0x28, 0xbe, 0x1d, 0x10, 0x17, 0xd3, 0x60, 0x4a, 0x7c, 0x7, 0xee, 0x10, 0x0, 0x81, 0x71, 0x73, 0x1c, 0xdd, 0x86, 0xe8, 0xf, 0x86, 0x1a, 0xfb, 0x50, 0xc8, 0x77, 0xe3, 0xe3, 0xb7, 0xfb, 0x57, 0xdd, 0x5c, 0x78, 0x76, 0x86, 0xd5, 0x99, 0x8d, 0xe1, 0xee, 0x1, 0x2d, 0x7f, 0xc0, 0x82, 0xc3, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x15, 0xcb, 0x1a, 0x22, 0xb6, 0x94, 0x56, 0xbb, 0x94, 0x62, 0xa5, 0x62, 0x80, 0x68, 0x9a, 0xbf, 0xf7, 0xdd, 0xf9, 0xd2, 0xed, 0x82, 0x2c, 0x52, 0x54, 0x40, 0x15, 0xc8, 0xb7, 0xcb, 0xd0, 0x88, 0xb0, 0xc6, 0x6d, 0x6c, 0x5c, 0x5e, 0xcf, 0xe2, 0xe, 0xbf, 0xff, 0x36, 0x59, 0xe, 0x7b, 0x3c, 0x9b, 0x7b, 0x3, 0x3b, 0x68, 0xfd, 0x96, 0x94, 0x92, 0x8a, 0x0, 0x12, 0x33, 0x5c, 0xbc, 0xb4, 0x42, 0xe9, 0x81, 0xa1, 0xf0, 0xb, 0x96, 0x70, 0x4f, 0x2e, 0xa9, 0xd4, 0x64, 0xeb, 0x79, 0x33, 0x80, 0xa4, 0xf0, 0x63, 0xc6, 0x72, 0x16, 0x76, 0xdb, 0x2f, 0x30, 0x98, 0x83, 0x5, 0x29, 0x4d, 0x1b, 0x7b, 0x86, 0xc2, 0xd6, 0x7d, 0x88, 0x63, 0x93, 0x8c, 0xa7, 0x6, 0x63, 0x4, 0x90, 0x8f, 0x28, 0xd7, 0xac, 0x39, 0x0, 0xfa, 0xbd, 0x83, 0x85, 0x43, 0x25, 0x6, 0xc6, 0xfc, 0x68, 0xf1, 0x73, 0x7, 0xd5, 0xde, 0x24, 0x60, 0xfe, 0xcf, 0x57, 0xc8, 0x9, 0x1, 0xc6, 0x58, 0xde, 0x5f, 0x22, 0xd9, 0xe3, 0xc5, 0x12, 0x40, 0x63, 0xb0, 0xb4, 0x97, 0x14, 0xc7, 0xce, 0xc6, 0xfc, 0x85, 0xfb, 0x33, 0x96, 0xa4, 0xdb, 0x9f, 0x92, 0xae, 0x60, 0xe9, 0x69, 0x57, 0x6f, 0x2b, 0x5a, 0xd1, 0x6e, 0x33, 0xa5, 0xa6, 0xeb, 0x5b, 0xad, 0x84, 0x47, 0xc1, 0xd7, 0x74, 0xf8, 0x6d, 0xfb, 0xf2, 0xf7, 0x5f, 0x70, 0x78, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0xaf, 0xf7, 0x7, 0x28, 0xd1, 0x9c, 0xb4, 0xc0, 0x45, 0x91, 0x4c, 0xaa, 0xda, 0xd, 0x87, 0x6f, 0xdc, 0xfb, 0x99, 0x3f, 0x12, 0x34, 0xf1, 0xbb, 0x7b, 0xfd, 0xff, 0x76, 0xfe, 0x6f, 0xb9, 0xbd, 0xc8, 0x23, 0x1, 0xde, 0x9b, 0x78, 0xbf, 0xf0, 0x4e, 0xdb, 0xeb, 0xcb, 0x9e, 0x50, 0x98, 0x5e, 0x25, 0xe0, 0x37, 0xd1, 0x1b, 0x63, 0xda, 0x47, 0xcb, 0x2c, 0xb5, 0x6e, 0xff, 0x79, 0x63, 0xdb, 0x90, 0x7a, 0x7f, 0xb9, 0xfd, 0xfe, 0xbf, 0x12, 0x82, 0xbc, 0x2e, 0xf7, 0xf6, 0xb4, 0x83, 0xf7, 0x8f, 0x54, 0xde, 0x25, 0x5a, 0x67, 0xf6, 0xe5, 0xf6, 0x42, 0x63, 0xc7, 0x98, 0x87, 0x32, 0xe4, 0xe3, 0xd3, 0x6c, 0x21, 0x22, 0x42, 0xa2, 0xfd, 0x8d, 0x48, 0x6d, 0x8f, 0x4c, 0x4, 0xed, 0x29, 0x42, 0x41, 0x1, 0xfc, 0xbc, 0x84, 0xfe, 0xaf, 0xdc, 0x60, 0xee, 0xcd, 0xee, 0xa, 0x53, 0x6a, 0x3e, 0xbe, 0xd0, 0x9a, 0xad, 0xf0, 0x50, 0x1c, 0x1e, 0xc2, 0xe0, 0x4b, 0x81, 0x2e, 0xb, 0x50, 0x44, 0x30, 0xb8, 0x70, 0x18, 0x85, 0x2c, 0x9c, 0xd8, 0x44, 0x66, 0xd7, 0xfd, 0xf6, 0x2d, 0xfd, 0x6c, 0x60, 0x57, 0xf7, 0x49, 0x28, 0x33, 0x16, 0x61, 0x10, 0x84, 0x6e, 0x40, 0x8d, 0x60, 0xf3, 0x78, 0xfe, 0x78, 0x79, 0xf1, 0xfc, 0xfd, 0xcd, 0x84, 0xb9, 0xfa, 0x3c, 0xf4, 0x51, 0xae, 0x69, 0xc2, 0x5e, 0xe3, 0x5e, 0xcd, 0x8, 0x22, 0x22, 0x53, 0xa8, 0x6f, 0x13, 0x14, 0xe3, 0x63, 0x15, 0xd5, 0xb5, 0xe7, 0x1f, 0x63, 0x79, 0xbe, 0xd2, 0x12, 0xfc, 0x55, 0x0, 0x5f, 0x8a, 0x8b, 0x97, 0x3f, 0x60, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0x2f, 0xf7, 0x7, 0x54, 0xe1, 0xfd, 0xf3, 0x8d, 0x82, 0x12, 0xfe, 0x97, 0x96, 0xa4, 0x5, 0xcf, 0x7f, 0x77, 0xc5, 0x60, 0xf8, 0x7, 0xe4, 0xe3, 0x2d, 0xbf, 0x3e, 0x82, 0x7b, 0xd1, 0x7b, 0x1d, 0xde, 0x17, 0xe, 0x88, 0x77, 0xdb, 0xdb, 0xcd, 0x67, 0x79, 0x7d, 0xd1, 0x13, 0xe5, 0xde, 0x20, 0xa5, 0xbb, 0xc1, 0xc1, 0x16, 0x96, 0xf7, 0xc0, 0x89, 0xd3, 0x3d, 0x8b, 0xf, 0xf0, 0xf3, 0x67, 0xdb, 0x94, 0x2f, 0x8e, 0x86, 0x99, 0x48, 0x99, 0x8, 0xc4, 0x4e, 0xb6, 0x22, 0xf7, 0xf1, 0x32, 0x20, 0x6, 0xcf, 0x47, 0x18, 0x3a, 0x74, 0x6a, 0x89, 0x79, 0x78, 0xbf, 0xf8, 0x9d, 0x68, 0xe3, 0x80, 0x17, 0x58, 0x3d, 0x40, 0x56, 0x5, 0xba, 0x34, 0x6, 0xcf, 0xc6, 0x84, 0x60, 0x45, 0x3d, 0x38, 0xb0, 0xc5, 0x3f, 0x50, 0xb1, 0x1e, 0x22, 0xc1, 0xf7, 0x21, 0x77, 0x87, 0xcc, 0x37, 0xb8, 0xb4, 0x4, 0x1a, 0x53, 0x1e, 0x30, 0xb3, 0xc1, 0xbd, 0x55, 0xe1, 0xf5, 0x23, 0x5, 0xd0, 0xba, 0xe6, 0xf9, 0x2b, 0xfd, 0x6b, 0x16, 0x29, 0x38, 0x74, 0x7a, 0xf6, 0x3a, 0xbc, 0x7e, 0xe0, 0x33, 0x57, 0x6, 0x8, 0x4, 0x4f, 0xd7, 0x89, 0x6d, 0x5f, 0x31, 0x14, 0xaa, 0xea, 0x4f, 0x9e, 0x19, 0x23, 0x82, 0xad, 0x17, 0x54, 0xc8, 0x80, 0xbe, 0x19, 0x1e, 0x93, 0x9b, 0xc2, 0x3, 0xec, 0x9c, 0xc1, 0x2c, 0x60, 0x8e, 0xae, 0x32, 0x81, 0x86, 0xcb, 0x21, 0xb2, 0xd0, 0xe0, 0x22, 0xc0, 0xf, 0x1, 0xf0, 0xed, 0x4, 0xf0, 0xec, 0x88, 0xba, 0xd2, 0x4f, 0xd7, 0x3b, 0x40, 0xe7, 0x76, 0x7c, 0x9, 0xc, 0x16, 0x83, 0x51, 0x31, 0xdb, 0x23, 0x3c, 0xbf, 0x4d, 0x6, 0x88, 0x3, 0xb, 0x0, 0xf3, 0x77, 0x79, 0x79, 0x79, 0x9e, 0x81, 0xe8, 0xce, 0xea, 0xa0, 0x66, 0x86, 0x4d, 0xd3, 0x39, 0x1, 0xc7, 0x3e, 0x61, 0x8f, 0x7f, 0x57, 0x67, 0xcc, 0xa, 0x77, 0x57, 0x32, 0x3c, 0x60, 0xdc, 0x4f, 0x25, 0x40, 0x43, 0x44, 0x2c, 0x4, 0xb9, 0xc4, 0x45, 0x8f, 0x38, 0x42, 0x58, 0x7b, 0xfc, 0x15, 0xc9, 0xd3, 0x74, 0xd9, 0xb4, 0xad, 0xd4, 0x35, 0x3b, 0x2f, 0x2a, 0x25, 0xe1, 0x3e, 0x66, 0x9, 0xb6, 0xaa, 0x85, 0xd1, 0x6a, 0x65, 0x78, 0x41, 0x45, 0x36, 0x3e, 0x63, 0xf8, 0x89, 0x19, 0x5d, 0xcd, 0xd7, 0x8a, 0xa2, 0xaa, 0x70, 0x27, 0x1, 0x8d, 0x9a, 0xef, 0x82, 0x8b, 0x65, 0x20, 0x7, 0x10, 0x5, 0x8d, 0x8b, 0x9c, 0x94, 0x12, 0xee, 0x97, 0x29, 0x2e, 0x74, 0xf2, 0xfe, 0xa2, 0x29, 0x31, 0xe7, 0x3e, 0x95, 0xeb, 0xd4, 0xe3, 0xd3, 0xc8, 0x1f, 0x1c, 0x73, 0xed, 0x47, 0x60, 0x81, 0xc9, 0x4c, 0xef, 0x7, 0x38, 0x20, 0x96, 0x1a, 0xb, 0xfe, 0x39, 0x2, 0x2c, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x8, 0x70, 0xbb, 0x54, 0xb7, 0x4f, 0x33, 0x88, 0xfa, 0xb3, 0x3f, 0xef, 0x98, 0x1d, 0x17, 0xa, 0x79, 0xa5, 0xc6, 0x52, 0x51, 0x4e, 0x4f, 0xc1, 0xcb, 0x79, 0x81, 0x3, 0x17, 0xef, 0xcb, 0x19, 0xc1, 0x69, 0xf3, 0xdd, 0x86, 0x71, 0x7d, 0x51, 0x8f, 0x12, 0x23, 0xeb, 0x50, 0xed, 0x1a, 0x5e, 0x36, 0x2b, 0xe, 0x78, 0x78, 0xff, 0x82, 0x59, 0xb, 0xaf, 0x58, 0x39, 0xda, 0xd6, 0xa8, 0xc5, 0x15, 0xaa, 0x4b, 0x8c, 0x41, 0x2a, 0x6, 0x4d, 0x71, 0x58, 0xfb, 0xc5, 0xda, 0xde, 0x6a, 0x13, 0x8d, 0x41, 0x5a, 0x15, 0x26, 0xb6, 0xb, 0x6f, 0x0, 0x44, 0x5f, 0xb8, 0xd, 0xd9, 0x4, 0x93, 0x9d, 0x29, 0x18, 0xd8, 0x3a, 0xd3, 0xf2, 0x82, 0x16, 0xf0, 0xce, 0xf8, 0x8c, 0x4f, 0x11, 0x4c, 0x8a, 0xd9, 0x45, 0x57, 0x5b, 0x8d, 0xdb, 0x2d, 0x5c, 0xc8, 0x16, 0x35, 0xab, 0xce, 0x99, 0xf0, 0x88, 0xe0, 0xc0, 0xf0, 0x9a, 0xa2, 0x12, 0xef, 0x9b, 0x54, 0xd6, 0xfc, 0x1, 0xc9, 0xf4, 0xf, 0xb8, 0x27, 0x82, 0xd2, 0x3c, 0x42, 0x62, 0x6c, 0x7c, 0xc0, 0x9e, 0x3c, 0x17, 0xa7, 0xeb, 0x24, 0x55, 0xda, 0xf4, 0xae, 0xe7, 0x69, 0xb3, 0xa7, 0xc0, 0xad, 0x61, 0xcf, 0x9, 0xd, 0xa1, 0xb1, 0x3c, 0xcf, 0xd9, 0x2d, 0x41, 0xb7, 0x1e, 0xb, 0xbd, 0xf8, 0x0, 0x4e, 0x44, 0xee, 0x85, 0x6, 0x3b, 0x31, 0x5, 0xc6, 0xdd, 0xff, 0x11, 0x1c, 0x30, 0xb2, 0x32, 0xcf, 0xf3, 0x4, 0x78, 0x78, 0x4, 0xfb, 0x15, 0x3d, 0x1b, 0x14, 0x34, 0x66, 0x21, 0x20, 0x50, 0xad, 0x54, 0x86, 0xf3, 0x2, 0x5, 0xe7, 0x8a, 0x8d, 0xa7, 0xbf, 0x5a, 0xa9, 0xb3, 0xd1, 0x74, 0xfe, 0xce, 0x73, 0x46, 0x59, 0x6a, 0x2e, 0x87, 0xa7, 0x6e, 0x2a, 0xaf, 0x53, 0x80, 0xd0, 0x41, 0x93, 0xfb, 0x18, 0x6b, 0x23, 0x9f, 0x81, 0xb2, 0xd6, 0x17, 0x12, 0xa3, 0x24, 0x66, 0xcb, 0x74, 0x98, 0xd9, 0xe3, 0xf1, 0xc3, 0xdb, 0x82, 0xb1, 0xcf, 0x32, 0xd8, 0x7c, 0xaa, 0x52, 0xc8, 0x7b, 0xdc, 0xa1, 0x31, 0xb6, 0x54, 0xfa, 0xeb, 0x78, 0x2, 0xbc, 0x32, 0xec, 0x99, 0x64, 0x6c, 0x32, 0x4e, 0x57, 0x3a, 0xaf, 0x50, 0xec, 0xa, 0x7, 0x8c, 0x3e, 0x81, 0x13, 0xc4, 0xfb, 0xaf, 0x54, 0xe6, 0x27, 0x9c, 0x3c, 0xfd, 0xb2, 0xa2, 0xdb, 0x98, 0xc8, 0x4f, 0xb2, 0x6, 0x7b, 0x57, 0xf7, 0xd8, 0xaa, 0x41, 0x7e, 0x37, 0xa1, 0x8, 0xc7, 0x75, 0xad, 0xa0, 0x61, 0x8b, 0x2, 0x6e, 0x98, 0x9f, 0x8b, 0xc6, 0x11, 0xe4, 0x20, 0xe1, 0xb0, 0x53, 0x3c, 0x78, 0x99, 0x6a, 0x8a, 0x15, 0x4, 0x7c, 0xa9, 0xe5, 0x5f, 0x19, 0x30, 0xdc, 0xe, 0xe8, 0xae, 0x2, 0x59, 0x1, 0xf2, 0x3d, 0xbc, 0x7a, 0x2c, 0xe9, 0xd0, 0xe8, 0xa2, 0x65, 0x13, 0x83, 0x5c, 0x60, 0x80, 0xf, 0xfb, 0x80, 0xe3, 0xef, 0xf8, 0xd3, 0x40, 0x6b, 0xf9, 0x3, 0x16, 0x1, 0x3e, 0x90, 0xeb, 0x8f, 0xba, 0x75, 0x9b, 0x60, 0x38, 0x68, 0xbc, 0x20, 0xfa, 0x80, 0xcc, 0x6d, 0xed, 0x9f, 0x3d, 0xee, 0x9d, 0x3f, 0xd8, 0x38, 0x3e, 0x69, 0x46, 0xbc, 0x77, 0xa7, 0xd1, 0x7, 0x7b, 0x50, 0x3b, 0xc, 0x0, 0xe7, 0x4, 0x70, 0x2d, 0xa7, 0xbe, 0x72, 0x53, 0x3f, 0xc4, 0x17, 0xae, 0x7d, 0x13, 0xb5, 0x2c, 0x72, 0xcb, 0x11, 0x2c, 0xfc, 0x1, 0xd7, 0xd5, 0xc4, 0x18, 0x3b, 0x80, 0xe0, 0x7b, 0xcb, 0xd0, 0x3, 0x6b, 0x48, 0x66, 0x78, 0xaa, 0xb0, 0x4, 0xf3, 0xfd, 0x78, 0xf7, 0x7c, 0x1, 0x3a, 0x33, 0x46, 0xaf, 0xde, 0x7f, 0xf, 0x45, 0x6d, 0x55, 0xb3, 0x7f, 0x7b, 0xb4, 0xc, 0x9c, 0x88, 0x3b, 0xb5, 0x0, 0xba, 0x40, 0x2b, 0x9e, 0x1e, 0xd2, 0x78, 0x1, 0xe, 0xca, 0xd3, 0xef, 0x52, 0xaa, 0x5e, 0x2, 0x1d, 0xe8, 0x91, 0xbd, 0x82, 0x5f, 0xe, 0x84, 0x87, 0x3f, 0xa9, 0x36, 0x17, 0xfb, 0x72, 0xfe, 0xf9, 0x3e, 0x60, 0xf9, 0x9a, 0xf9, 0x1a, 0x57, 0x8a, 0xe7, 0x57, 0x84, 0x48, 0x5c, 0x77, 0x88, 0xc, 0x10, 0x60, 0x1c, 0xc1, 0x42, 0xfe, 0xfb, 0xe0, 0xaa, 0x3f, 0xe0, 0x3, 0xc, 0xb7, 0xae, 0x5a, 0xc0, 0x96, 0xa9, 0xb, 0xb, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x7c, 0xd3, 0x67, 0xff, 0xb6, 0x17, 0xbe, 0x52, 0x43, 0xc4, 0x2f, 0xd9, 0x27, 0x2e, 0x86, 0xb1, 0x97, 0x57, 0xe6, 0xed, 0x81, 0x96, 0xe2, 0x6e, 0x87, 0x56, 0xf6, 0xc6, 0x40, 0xcd, 0xe6, 0x72, 0x92, 0x25, 0xc, 0x3, 0xdb, 0xf5, 0xce, 0x79, 0x7f, 0x17, 0x28, 0x78, 0x5, 0x9, 0x9d, 0x76, 0xe2, 0x9f, 0x3c, 0xd, 0x27, 0x7d, 0x1c, 0x45, 0x82, 0x41, 0x4d, 0xe9, 0x30, 0xfb, 0xfd, 0x81, 0x63, 0xb0, 0x69, 0x23, 0xb4, 0x14, 0xfd, 0xb9, 0x4, 0xa0, 0x8e, 0xce, 0x2c, 0xe0, 0x1a, 0xf2, 0x89, 0xb8, 0xd1, 0xe6, 0xc8, 0xa2, 0xb1, 0x65, 0xd7, 0x71, 0xbc, 0x6d, 0x45, 0x77, 0xef, 0xe1, 0x45, 0x81, 0x82, 0x27, 0x50, 0x54, 0x7b, 0x87, 0x13, 0x41, 0x51, 0x75, 0xa4, 0x26, 0x4f, 0xfc, 0x37, 0x49, 0xd6, 0x5b, 0xa8, 0xa8, 0x8b, 0xdd, 0x78, 0xe0, 0xa2, 0xe5, 0x4e, 0x50, 0xc7, 0xe, 0xb6, 0xe1, 0xdf, 0x54, 0xd4, 0x14, 0x35, 0xdf, 0x5f, 0x1c, 0x4f, 0xa0, 0xbc, 0x2d, 0xfd, 0x18, 0x21, 0x1b, 0xdf, 0x9b, 0x24, 0x64, 0x99, 0x72, 0xe3, 0x48, 0x6e, 0x2f, 0xbb, 0xdc, 0xb9, 0xc0, 0x5d, 0x71, 0x3c, 0x19, 0x63, 0xb5, 0x16, 0x50, 0x17, 0x9d, 0xa1, 0x5, 0xf0, 0xa, 0x6a, 0x39, 0xed, 0x41, 0xf5, 0xdf, 0x44, 0xb4, 0x80, 0x26, 0xd6, 0x58, 0xdf, 0xa1, 0xd3, 0x5f, 0xd, 0x1, 0x9c, 0x73, 0x85, 0x9d, 0x3e, 0xa3, 0x6a, 0x30, 0x35, 0xd6, 0x2c, 0x6d, 0xb6, 0x3, 0x7a, 0x7b, 0x8, 0xbc, 0x83, 0x93, 0xbe, 0xcf, 0x14, 0x9e, 0xcb, 0x5, 0xb3, 0x4d, 0x3e, 0xbe, 0xf1, 0xd8, 0x60, 0xb9, 0xc4, 0x16, 0x1, 0x16, 0x1, 0xfa, 0x20, 0xec, 0xcf, 0x25, 0x80, 0x13, 0xb6, 0xe8, 0xd4, 0x92, 0xab, 0x88, 0x33, 0xb, 0xa6, 0x7f, 0x47, 0xb, 0x2, 0x94, 0xf9, 0xe6, 0x79, 0xfb, 0xff, 0xb3, 0x71, 0x2a, 0x4, 0xa5, 0x61, 0xe5, 0x5, 0x36, 0x22, 0x18, 0x5c, 0x1e, 0xb, 0x71, 0x80, 0x63, 0xfb, 0xeb, 0xd7, 0x83, 0x67, 0x6d, 0xc7, 0xf4, 0xaa, 0x58, 0x2, 0x22, 0x82, 0x83, 0xee, 0x84, 0xb4, 0xac, 0xa, 0x36, 0x5e, 0x2f, 0xec, 0x2e, 0xd5, 0xb6, 0x3f, 0x10, 0x80, 0x88, 0x15, 0x77, 0xe7, 0xc3, 0x87, 0x11, 0xaa, 0xee, 0x0, 0x83, 0x49, 0x49, 0x24, 0x3, 0xa0, 0x8a, 0x50, 0x59, 0x14, 0xc6, 0xaa, 0x80, 0xff, 0xd1, 0x39, 0x8a, 0xd2, 0xab, 0xf0, 0x26, 0x88, 0x3c, 0xc9, 0x12, 0xe, 0xe2, 0x10, 0x5d, 0xfc, 0xdb, 0x3e, 0x34, 0xff, 0x3, 0x66, 0xdd, 0xc9, 0xb6, 0x2b, 0x1c, 0x85, 0xce, 0xdb, 0xdb, 0x8, 0x49, 0x10, 0xf7, 0x81, 0x5, 0x5, 0xf8, 0x23, 0xc9, 0x90, 0xb5, 0x7f, 0x4f, 0x8d, 0x4b, 0xc5, 0xef, 0x8f, 0xdb, 0x1, 0xcd, 0xa6, 0x67, 0x9d, 0xd4, 0x36, 0xfc, 0xb2, 0x59, 0x73, 0x1b, 0xb4, 0x4c, 0x87, 0x1a, 0xef, 0x9a, 0x58, 0xb3, 0x95, 0x33, 0x4e, 0x4f, 0xa1, 0xdd, 0x4f, 0x84, 0x38, 0x5b, 0x5e, 0x9f, 0x23, 0x21, 0x6, 0xb5, 0xb2, 0x2e, 0x2c, 0xce, 0xd9, 0xcd, 0x24, 0x57, 0xf9, 0xfb, 0x60, 0x30, 0xa4, 0x79, 0x18, 0xa6, 0xca, 0x5d, 0xcd, 0x17, 0xc8, 0xf3, 0x5b, 0x63, 0xb7, 0xe7, 0x1b, 0xe4, 0x3e, 0xc7, 0x3b, 0x38, 0x60, 0x6a, 0x40, 0xbe, 0xfc, 0x1, 0xaa, 0xd4, 0xe, 0xc6, 0x4b, 0x77, 0x57, 0x1a, 0x5e, 0xd5, 0xda, 0x5e, 0x37, 0xff, 0x27, 0x0, 0x1d, 0x1d, 0x22, 0x4c, 0x19, 0x11, 0x7f, 0x8d, 0xc8, 0x33, 0x5d, 0x5b, 0xd1, 0x7b, 0x7e, 0x76, 0x6c, 0x73, 0xcb, 0xcd, 0x83, 0x96, 0xc0, 0x47, 0xe5, 0x51, 0xa9, 0xc9, 0xf3, 0xdd, 0xdb, 0xb9, 0x11, 0xa8, 0x9d, 0x67, 0x78, 0x7e, 0x80, 0xa1, 0xdb, 0x6e, 0x1b, 0xdc, 0xae, 0xb1, 0x74, 0xe7, 0x76, 0xa1, 0xea, 0xd4, 0xf3, 0xc, 0x3, 0x60, 0xa0, 0x2f, 0x77, 0x6d, 0x77, 0x71, 0x59, 0xd1, 0x9a, 0x66, 0x8d, 0x6c, 0xf2, 0xb0, 0xaf, 0xde, 0xed, 0xb9, 0x3f, 0xfb, 0x5d, 0x62, 0x26, 0x55, 0x7, 0xf3, 0x46, 0xdb, 0xed, 0x4, 0xe8, 0x7d, 0xcc, 0x89, 0xd6, 0xe6, 0x75, 0x7, 0xc0, 0x58, 0x19, 0xb0, 0xcb, 0x53, 0x5e, 0x47, 0xb4, 0xe7, 0x5d, 0x12, 0xf, 0x38, 0x44, 0x90, 0x66, 0xa2, 0xc8, 0xaf, 0xef, 0x6c, 0xe8, 0xbf, 0x98, 0xd6, 0xe, 0x78, 0xc4, 0x66, 0x9b, 0xc9, 0x50, 0x7c, 0x64, 0x6f, 0x70, 0x26, 0x2d, 0xf9, 0x8, 0x1, 0x66, 0xe2, 0x80, 0xe5, 0xf, 0xd0, 0x25, 0xa3, 0x3, 0x6f, 0xe1, 0x41, 0xf0, 0xd6, 0x7c, 0xef, 0x26, 0x88, 0x1f, 0xd2, 0x31, 0x5b, 0xd, 0x9c, 0x75, 0xfc, 0x5, 0x25, 0xe6, 0x76, 0xfd, 0x5, 0x51, 0x19, 0x11, 0x84, 0xf8, 0x33, 0x9d, 0xe1, 0xf2, 0x9, 0x58, 0x40, 0x98, 0xda, 0x22, 0xb0, 0x50, 0xc4, 0xcb, 0x37, 0xb6, 0x35, 0x86, 0x65, 0x51, 0x75, 0xa8, 0xb7, 0x65, 0x7a, 0x3a, 0x9c, 0x3d, 0xa9, 0xdb, 0xe9, 0x96, 0xcf, 0xa4, 0xb5, 0x5d, 0xe5, 0x93, 0x69, 0xd1, 0xb1, 0x8c, 0x3c, 0x6e, 0x93, 0xab, 0x4e, 0xdd, 0xcc, 0x3c, 0xc4, 0xef, 0xb7, 0xb6, 0xb, 0x21, 0xf5, 0x33, 0xa0, 0x7e, 0xa6, 0x41, 0x99, 0xb0, 0x60, 0x47, 0xa8, 0xdc, 0xbf, 0x31, 0xa2, 0x9d, 0x27, 0x30, 0xb6, 0x40, 0xdf, 0x99, 0x53, 0xe6, 0xc5, 0x1, 0x4a, 0x69, 0xb3, 0xc1, 0x86, 0xda, 0xf0, 0xf7, 0xf, 0xd5, 0xeb, 0xdf, 0xa8, 0x64, 0x7b, 0x73, 0xf0, 0xf6, 0xee, 0xd3, 0x96, 0xf0, 0xf1, 0x64, 0xf1, 0xfb, 0x97, 0x80, 0xac, 0xae, 0x3f, 0x58, 0x15, 0x2b, 0x1e, 0xa4, 0xc3, 0xf6, 0xf5, 0xa6, 0x8e, 0x41, 0xca, 0xc4, 0xdf, 0xb, 0x9a, 0xdb, 0x49, 0xd4, 0x34, 0x91, 0xd7, 0x67, 0x87, 0x98, 0xc4, 0xda, 0x72, 0xbe, 0x69, 0x95, 0x8c, 0x4, 0x9f, 0xb6, 0x3, 0x5e, 0x23, 0xb0, 0xc2, 0x90, 0xa2, 0x6d, 0xbb, 0xe0, 0xc4, 0xab, 0x82, 0xe2, 0x3a, 0x7c, 0xfd, 0x51, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x9, 0x80, 0x4f, 0x23, 0x40, 0xe7, 0x0, 0x11, 0xc4, 0x83, 0x63, 0x1b, 0x1c, 0x40, 0x3d, 0x22, 0x44, 0x9a, 0x3, 0x44, 0x10, 0x33, 0xc5, 0x9b, 0x1d, 0x40, 0xb0, 0xcc, 0xf6, 0xd0, 0xdd, 0xcb, 0xe, 0xf8, 0x39, 0x61, 0xa2, 0xc9, 0xf0, 0xaa, 0x68, 0x67, 0xb1, 0x79, 0x9d, 0xfb, 0x6f, 0x8e, 0x10, 0xd1, 0x22, 0x8b, 0x7b, 0xb7, 0x8f, 0x5f, 0xf6, 0xee, 0xbf, 0x59, 0x8, 0xb6, 0xee, 0x4e, 0x5f, 0xd9, 0xcd, 0xe6, 0x6c, 0x4b, 0xc0, 0x11, 0xfa, 0x1d, 0xda, 0xa8, 0x97, 0xe3, 0xd1, 0x76, 0x2b, 0x1, 0x40, 0xe9, 0xa3, 0xeb, 0xdd, 0xb6, 0xe1, 0x6a, 0x7, 0x96, 0x6a, 0x34, 0x84, 0x46, 0xf3, 0x80, 0x60, 0x2, 0xcc, 0xc3, 0x4, 0xa0, 0x88, 0xef, 0xef, 0xdf, 0x4e, 0x19, 0x4, 0x9f, 0x4b, 0xb, 0x3c, 0x61, 0x7, 0x4c, 0x65, 0x7b, 0x3c, 0x1, 0x86, 0xa6, 0xa, 0xa2, 0x7b, 0x82, 0x0, 0x53, 0x71, 0xc0, 0x72, 0x89, 0x2d, 0x7f, 0xc0, 0x22, 0x80, 0x62, 0x9a, 0xc5, 0x2, 0x44, 0x7a, 0x27, 0x94, 0xdc, 0x19, 0x20, 0xf2, 0x3a, 0x78, 0xd9, 0xc6, 0xfb, 0xbd, 0xfd, 0x5, 0x63, 0xfd, 0x3, 0xe2, 0x7a, 0x67, 0x7a, 0x3a, 0x63, 0xab, 0x9, 0x5, 0xaf, 0xf3, 0xb4, 0x4d, 0x46, 0xf8, 0x24, 0xa7, 0x66, 0x44, 0x6b, 0x5b, 0x24, 0x7e, 0xe6, 0x17, 0x88, 0xf8, 0x81, 0x58, 0x5b, 0xa9, 0x17, 0x21, 0xba, 0x3f, 0xfe, 0xfe, 0x8a, 0x16, 0x2f, 0xf7, 0x92, 0x46, 0xfa, 0x7, 0xb4, 0x19, 0xba, 0x3f, 0xd9, 0xd8, 0x40, 0x83, 0xe9, 0x86, 0xbd, 0x5a, 0xc5, 0x67, 0xd4, 0xcf, 0xcc, 0xb4, 0xb, 0x0, 0x94, 0xbf, 0xdf, 0xbe, 0x3d, 0xae, 0xd6, 0x96, 0x7b, 0xd0, 0x36, 0x9e, 0xc0, 0xe, 0xe8, 0x1e, 0x1f, 0x62, 0xf7, 0x58, 0xfc, 0xfe, 0x8, 0x16, 0xf0, 0x78, 0xa2, 0xaf, 0x7b, 0x0, 0xae, 0x84, 0x70, 0xaa, 0xe6, 0xf6, 0xf6, 0x12, 0x2b, 0xe3, 0x2b, 0xae, 0x2f, 0xea, 0x33, 0x6, 0xdb, 0x22, 0x20, 0xa, 0x91, 0xf0, 0x81, 0xbf, 0xf0, 0xf1, 0x2, 0x70, 0xc6, 0x97, 0xd0, 0x98, 0x5b, 0xa6, 0x38, 0x25, 0x34, 0x3e, 0xfe, 0xf5, 0x13, 0x43, 0xbf, 0x7f, 0xbd, 0x3f, 0xe0, 0x1f, 0x5f, 0x4e, 0x4b, 0x19, 0x56, 0xcb, 0xb5, 0x20, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 };
+const unsigned short sGlyphWidths[256] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 3, 5, 7, 7, 12, 9, 2, 4, 4, 5, 8, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 8, 8, 8, 7, 13, 9, 9, 9, 9, 9, 8, 10, 9, 3, 6, 9, 7, 11, 9, 10, 9, 10, 9, 9, 7, 9, 9, 13, 7, 9, 7, 4, 4, 4, 5, 7, 4, 7, 7, 7, 7, 7, 3, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7, 4, 7, 4, 7, 5, 9, 7, 7, 7, 4, 3, 4, 8, 10, 7, 10, 3, 7, 4, 13, 7, 7, 4, 14, 9, 4, 13, 10, 7, 10, 10, 3, 3, 4, 4, 5, 7, 13, 4, 13, 7, 4, 12, 10, 7, 9, 4, 3, 7, 7, 7, 7, 3, 7, 4, 10, 4, 7, 8, 4, 10, 7, 5, 7, 4, 4, 4, 7, 7, 4, 4, 4, 5, 7, 11, 11, 11, 8, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 3, 3, 3, 3, 9, 9, 10, 10, 10, 10, 10, 8, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 12, 7, 7, 7, 7, 7, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 };
+const unsigned int sTextureWidth = 256;
+const unsigned int sTextureHeight = 256;
+const unsigned int sCellWidth = 16;
+const unsigned int sCellHeight = 16;
diff --git a/gfx/layers/composite/FrameUniformityData.cpp b/gfx/layers/composite/FrameUniformityData.cpp
new file mode 100644
index 000000000..e8bab6adb
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameUniformityData.h"
+
+#include <map>
+
+#include "Units.h"
+#include "gfxPoint.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/APZTestDataBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+Point
+LayerTransforms::GetAverage()
+{
+ MOZ_ASSERT(!mTransforms.IsEmpty());
+
+ Point current = mTransforms[0];
+ Point average;
+ size_t length = mTransforms.Length();
+
+ for (size_t i = 1; i < length; i++) {
+ Point nextTransform = mTransforms[i];
+ Point movement = nextTransform - current;
+ average += Point(std::fabs(movement.x), std::fabs(movement.y));
+ current = nextTransform;
+ }
+
+ average = average / (float) length;
+ return average;
+}
+
+Point
+LayerTransforms::GetStdDev()
+{
+ Point average = GetAverage();
+ Point stdDev;
+ Point current = mTransforms[0];
+
+ for (size_t i = 1; i < mTransforms.Length(); i++) {
+ Point next = mTransforms[i];
+ Point move = next - current;
+ move.x = fabs(move.x);
+ move.y = fabs(move.y);
+
+ Point diff = move - average;
+ diff.x = diff.x * diff.x;
+ diff.y = diff.y * diff.y;
+ stdDev += diff;
+
+ current = next;
+ }
+
+ stdDev = stdDev / mTransforms.Length();
+ stdDev.x = sqrt(stdDev.x);
+ stdDev.y = sqrt(stdDev.y);
+ return stdDev;
+}
+
+LayerTransformRecorder::~LayerTransformRecorder()
+{
+ Reset();
+}
+
+void
+LayerTransformRecorder::RecordTransform(Layer* aLayer, const Point& aTransform)
+{
+ LayerTransforms* layerTransforms = GetLayerTransforms((uintptr_t) aLayer);
+ layerTransforms->mTransforms.AppendElement(aTransform);
+}
+
+void
+LayerTransformRecorder::EndTest(FrameUniformityData* aOutData)
+{
+ for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ uintptr_t layer = iter->first;
+ float uniformity = CalculateFrameUniformity(layer);
+
+ std::pair<uintptr_t,float> result(layer, uniformity);
+ aOutData->mUniformities.insert(result);
+ }
+
+ Reset();
+}
+
+LayerTransforms*
+LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer)
+{
+ if (!mFrameTransforms.count(aLayer)) {
+ LayerTransforms* newTransform = new LayerTransforms();
+ std::pair<uintptr_t, LayerTransforms*> newLayer(aLayer, newTransform);
+ mFrameTransforms.insert(newLayer);
+ }
+
+ return mFrameTransforms.find(aLayer)->second;
+}
+
+void
+LayerTransformRecorder::Reset()
+{
+ for (auto iter = mFrameTransforms.begin(); iter != mFrameTransforms.end(); ++iter) {
+ LayerTransforms* layerTransforms = iter->second;
+ delete layerTransforms;
+ }
+
+ mFrameTransforms.clear();
+}
+
+float
+LayerTransformRecorder::CalculateFrameUniformity(uintptr_t aLayer)
+{
+ LayerTransforms* layerTransform = GetLayerTransforms(aLayer);
+ float yUniformity = -1;
+ if (!layerTransform->mTransforms.IsEmpty()) {
+ Point stdDev = layerTransform->GetStdDev();
+ yUniformity = stdDev.y;
+ }
+ return yUniformity;
+}
+
+bool
+FrameUniformityData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext)
+{
+ dom::FrameUniformityResults results;
+ dom::Sequence<dom::FrameUniformity>& layers = results.mLayerUniformities.Construct();
+
+ for (auto iter = mUniformities.begin(); iter != mUniformities.end(); ++iter) {
+ uintptr_t layerAddr = iter->first;
+ float uniformity = iter->second;
+
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(layers.AppendElement(fallible));
+ dom::FrameUniformity& entry = layers.LastElement();
+
+ entry.mLayerAddress.Construct() = layerAddr;
+ entry.mFrameUniformity.Construct() = uniformity;
+ }
+
+ return dom::ToJSValue(aContext, results, aOutValue);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/FrameUniformityData.h b/gfx/layers/composite/FrameUniformityData.h
new file mode 100644
index 000000000..3ff1bdc4a
--- /dev/null
+++ b/gfx/layers/composite/FrameUniformityData.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_FrameUniformityData_h_
+#define mozilla_layers_FrameUniformityData_h_
+
+#include "ipc/IPCMessageUtils.h"
+#include "js/TypeDecls.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+
+class FrameUniformityData {
+ friend struct IPC::ParamTraits<FrameUniformityData>;
+
+public:
+ bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext);
+ // Contains the calculated frame uniformities
+ std::map<uintptr_t,float> mUniformities;
+};
+
+struct LayerTransforms {
+ LayerTransforms() {}
+
+ gfx::Point GetAverage();
+ gfx::Point GetStdDev();
+
+ // 60 fps * 5 seconds worth of data
+ AutoTArray<gfx::Point, 300> mTransforms;
+};
+
+class LayerTransformRecorder {
+public:
+ LayerTransformRecorder() {}
+ ~LayerTransformRecorder();
+
+ void RecordTransform(Layer* aLayer, const gfx::Point& aTransform);
+ void Reset();
+ void EndTest(FrameUniformityData* aOutData);
+
+private:
+ float CalculateFrameUniformity(uintptr_t aLayer);
+ LayerTransforms* GetLayerTransforms(uintptr_t aLayer);
+ std::map<uintptr_t,LayerTransforms*> mFrameTransforms;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::layers::FrameUniformityData>
+{
+ typedef mozilla::layers::FrameUniformityData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mUniformities);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ParamTraitsStd<std::map<uintptr_t,float>>::Read(aMsg, aIter, &aResult->mUniformities);
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_FrameUniformityData_h_
diff --git a/gfx/layers/composite/GPUVideoTextureHost.cpp b/gfx/layers/composite/GPUVideoTextureHost.cpp
new file mode 100644
index 000000000..1e539d8ac
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GPUVideoTextureHost.h"
+#include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+GPUVideoTextureHost::GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor)
+ : TextureHost(aFlags)
+{
+ MOZ_COUNT_CTOR(GPUVideoTextureHost);
+ mWrappedTextureHost = VideoBridgeParent::GetSingleton()->LookupTexture(aDescriptor.handle());
+}
+
+GPUVideoTextureHost::~GPUVideoTextureHost()
+{
+ MOZ_COUNT_DTOR(GPUVideoTextureHost);
+}
+
+bool
+GPUVideoTextureHost::Lock()
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->Lock();
+}
+
+bool
+GPUVideoTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mWrappedTextureHost) {
+ return false;
+ }
+ return mWrappedTextureHost->BindTextureSource(aTexture);
+}
+
+Compositor*
+GPUVideoTextureHost::GetCompositor()
+{
+ if (!mWrappedTextureHost) {
+ return nullptr;
+ }
+ return mWrappedTextureHost->GetCompositor();
+}
+
+void
+GPUVideoTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mWrappedTextureHost) {
+ mWrappedTextureHost->SetCompositor(aCompositor);
+ }
+}
+
+YUVColorSpace
+GPUVideoTextureHost::GetYUVColorSpace() const
+{
+ if (mWrappedTextureHost) {
+ return mWrappedTextureHost->GetYUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+gfx::IntSize
+GPUVideoTextureHost::GetSize() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::IntSize();
+ }
+ return mWrappedTextureHost->GetSize();
+}
+
+gfx::SurfaceFormat
+GPUVideoTextureHost::GetFormat() const
+{
+ if (!mWrappedTextureHost) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mWrappedTextureHost->GetFormat();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/GPUVideoTextureHost.h b/gfx/layers/composite/GPUVideoTextureHost.h
new file mode 100644
index 000000000..fd6bdc3fb
--- /dev/null
+++ b/gfx/layers/composite/GPUVideoTextureHost.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+#define MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+class GPUVideoTextureHost : public TextureHost
+{
+public:
+ GPUVideoTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorGPUVideo& aDescriptor);
+ virtual ~GPUVideoTextureHost();
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "GPUVideoTextureHost"; }
+#endif
+
+protected:
+ RefPtr<TextureHost> mWrappedTextureHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp
new file mode 100644
index 000000000..b1d77924b
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -0,0 +1,739 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageHost.h"
+
+#include "LayersLogging.h" // for AppendToString
+#include "composite/CompositableHost.h" // for CompositableHost, etc
+#include "ipc/IPCMessageUtils.h" // for null_t
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayerManagerComposite.h" // for TexturedEffect, Effect, etc
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsString.h" // for nsAutoCString
+
+#define BIAS_TIME_MS 1.0
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class ISurfaceAllocator;
+
+ImageHost::ImageHost(const TextureInfo& aTextureInfo)
+ : CompositableHost(aTextureInfo)
+ , mImageContainer(nullptr)
+ , mLastFrameID(-1)
+ , mLastProducerID(-1)
+ , mBias(BIAS_NONE)
+ , mLocked(false)
+{}
+
+ImageHost::~ImageHost()
+{
+ SetImageContainer(nullptr);
+}
+
+void
+ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::UseTextureHost(aTextures);
+ MOZ_ASSERT(aTextures.Length() >= 1);
+
+ nsTArray<TimedImage> newImages;
+
+ for (uint32_t i = 0; i < aTextures.Length(); ++i) {
+ const TimedTexture& t = aTextures[i];
+ MOZ_ASSERT(t.mTexture);
+ if (i + 1 < aTextures.Length() &&
+ t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
+ // Ignore frames before a frame that we already composited. We don't
+ // ever want to display these frames. This could be important if
+ // the frame producer adjusts timestamps (e.g. to track the audio clock)
+ // and the new frame times are earlier.
+ continue;
+ }
+ TimedImage& img = *newImages.AppendElement();
+ img.mTextureHost = t.mTexture;
+ img.mTimeStamp = t.mTimeStamp;
+ img.mPictureRect = t.mPictureRect;
+ img.mFrameID = t.mFrameID;
+ img.mProducerID = t.mProducerID;
+ img.mTextureHost->SetCropRect(img.mPictureRect);
+ img.mTextureHost->Updated();
+ }
+
+ mImages.SwapElements(newImages);
+ newImages.Clear();
+
+ // If we only have one image we can upload it right away, otherwise we'll upload
+ // on-demand during composition after we have picked the proper timestamp.
+ if (mImages.Length() == 1) {
+ SetCurrentTextureHost(mImages[0].mTextureHost);
+ }
+
+ // Video producers generally send replacement images with the same frameID but
+ // slightly different timestamps in order to sync with the audio clock. This
+ // means that any CompositeUntil() call we made in Composite() may no longer
+ // guarantee that we'll composite until the next frame is ready. Fix that here.
+ if (GetCompositor() && mLastFrameID >= 0) {
+ for (size_t i = 0; i < mImages.Length(); ++i) {
+ bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
+ mImages[i].mProducerID != mLastProducerID;
+ if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
+ GetCompositor()->CompositeUntil(mImages[i].mTimeStamp +
+ TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ break;
+ }
+ }
+ }
+}
+
+void
+ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
+{
+ if (aTexture == mCurrentTextureHost.get()) {
+ return;
+ }
+
+ bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
+ && mCurrentTextureHost->HasIntermediateBuffer();
+
+ if (swapTextureSources) {
+ auto dataSource = mCurrentTextureSource->AsDataTextureSource();
+ if (dataSource) {
+ // The current textureHost has an internal buffer in the form of the
+ // DataTextureSource. Removing the ownership of the texture source
+ // will enable the next texture host we bind to the texture source to
+ // acquire it instead of creating a new one. This is desirable in
+ // ImageHost because the current texture won't be used again with the
+ // same content. It wouldn't be desirable with ContentHost for instance,
+ // because the latter reuses the texture's valid regions.
+ dataSource->SetOwner(nullptr);
+ }
+
+ RefPtr<TextureSource> tmp = mExtraTextureSource;
+ mExtraTextureSource = mCurrentTextureSource.get();
+ mCurrentTextureSource = tmp;
+ } else {
+ mExtraTextureSource = nullptr;
+ }
+
+ mCurrentTextureHost = aTexture;
+ mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
+}
+
+void
+ImageHost::CleanupResources()
+{
+ mExtraTextureSource = nullptr;
+ mCurrentTextureSource = nullptr;
+ mCurrentTextureHost = nullptr;
+}
+
+void
+ImageHost::RemoveTextureHost(TextureHost* aTexture)
+{
+ MOZ_ASSERT(!mLocked);
+
+ CompositableHost::RemoveTextureHost(aTexture);
+
+ for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+ if (mImages[i].mTextureHost == aTexture) {
+ aTexture->UnbindTextureSource();
+ mImages.RemoveElementAt(i);
+ }
+ }
+}
+
+void
+ImageHost::UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect)
+{
+ if (ImageHostOverlay::IsValid(aOverlay)) {
+ if (!mImageHostOverlay) {
+ mImageHostOverlay = new ImageHostOverlay();
+ }
+ mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect);
+ } else {
+ mImageHostOverlay = nullptr;
+ }
+}
+
+static TimeStamp
+GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
+{
+ switch (aBias) {
+ case ImageHost::BIAS_NEGATIVE:
+ return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ case ImageHost::BIAS_POSITIVE:
+ return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
+ default:
+ return aInput;
+ }
+}
+
+static ImageHost::Bias
+UpdateBias(const TimeStamp& aCompositionTime,
+ const TimeStamp& aCompositedImageTime,
+ const TimeStamp& aNextImageTime, // may be null
+ ImageHost::Bias aBias)
+{
+ if (aCompositedImageTime.IsNull()) {
+ return ImageHost::BIAS_NONE;
+ }
+ TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
+ if (aCompositionTime - aCompositedImageTime < threshold &&
+ aCompositionTime - aCompositedImageTime > -threshold) {
+ // The chosen frame's time is very close to the composition time (probably
+ // just before the current composition time, but due to previously set
+ // negative bias, it could be just after the current composition time too).
+ // If the inter-frame time is almost exactly equal to (a multiple of)
+ // the inter-composition time, then we're in a dangerous situation because
+ // jitter might cause frames to fall one side or the other of the
+ // composition times, causing many frames to be skipped or duplicated.
+ // Try to prevent that by adding a negative bias to the frame times during
+ // the next composite; that should ensure the next frame's time is treated
+ // as falling just before a composite time.
+ return ImageHost::BIAS_NEGATIVE;
+ }
+ if (!aNextImageTime.IsNull() &&
+ aNextImageTime - aCompositionTime < threshold &&
+ aNextImageTime - aCompositionTime > -threshold) {
+ // The next frame's time is very close to our composition time (probably
+ // just after the current composition time, but due to previously set
+ // positive bias, it could be just before the current composition time too).
+ // We're in a dangerous situation because jitter might cause frames to
+ // fall one side or the other of the composition times, causing many frames
+ // to be skipped or duplicated.
+ // Try to prevent that by adding a negative bias to the frame times during
+ // the next composite; that should ensure the next frame's time is treated
+ // as falling just before a composite time.
+ return ImageHost::BIAS_POSITIVE;
+ }
+ return ImageHost::BIAS_NONE;
+}
+
+int ImageHost::ChooseImageIndex() const
+{
+ if (!GetCompositor() || mImages.IsEmpty()) {
+ return -1;
+ }
+ TimeStamp now = GetCompositor()->GetCompositionTime();
+
+ if (now.IsNull()) {
+ // Not in a composition, so just return the last image we composited
+ // (if it's one of the current images).
+ for (uint32_t i = 0; i < mImages.Length(); ++i) {
+ if (mImages[i].mFrameID == mLastFrameID &&
+ mImages[i].mProducerID == mLastProducerID) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ uint32_t result = 0;
+ while (result + 1 < mImages.Length() &&
+ GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
+ ++result;
+ }
+ return result;
+}
+
+const ImageHost::TimedImage* ImageHost::ChooseImage() const
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+ImageHost::TimedImage* ImageHost::ChooseImage()
+{
+ int index = ChooseImageIndex();
+ return index >= 0 ? &mImages[index] : nullptr;
+}
+
+TextureHost*
+ImageHost::GetAsTextureHost(IntRect* aPictureRect)
+{
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ }
+ if (aPictureRect && img) {
+ *aPictureRect = img->mPictureRect;
+ }
+ return img ? img->mTextureHost.get() : nullptr;
+}
+
+void ImageHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags)
+{
+ CompositableHost::Attach(aLayer, aCompositor, aFlags);
+ for (auto& img : mImages) {
+ if (GetCompositor()) {
+ img.mTextureHost->SetCompositor(GetCompositor());
+ }
+ img.mTextureHost->Updated();
+ }
+}
+
+void
+ImageHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ if (!GetCompositor()) {
+ // should only happen when a tab is dragged to another window and
+ // async-video is still sending frames but we haven't attached the
+ // set the new compositor yet.
+ return;
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->Composite(GetCompositor(),
+ mFlashCounter,
+ aLayer,
+ aEffectChain,
+ aOpacity,
+ aTransform,
+ aSamplingFilter,
+ aClipRect,
+ aVisibleRegion);
+ mBias = BIAS_NONE;
+ return;
+ }
+
+ int imageIndex = ChooseImageIndex();
+ if (imageIndex < 0) {
+ return;
+ }
+
+ if (uint32_t(imageIndex) + 1 < mImages.Length()) {
+ GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+ }
+
+ TimedImage* img = &mImages[imageIndex];
+ img->mTextureHost->SetCompositor(GetCompositor());
+ SetCurrentTextureHost(img->mTextureHost);
+
+ {
+ AutoLockCompositableHost autoLock(this);
+ if (autoLock.Failed()) {
+ NS_WARNING("failed to lock front buffer");
+ return;
+ }
+
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return;
+ }
+
+ if (!mCurrentTextureSource) {
+ // BindTextureSource above should have returned false!
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ bool isAlphaPremultiplied =
+ !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+ RefPtr<TexturedEffect> effect =
+ CreateTexturedEffect(mCurrentTextureHost,
+ mCurrentTextureSource.get(), aSamplingFilter, isAlphaPremultiplied,
+ GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ if (!GetCompositor()->SupportsEffect(effect->mType)) {
+ return;
+ }
+
+ DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE;
+ if (effect->mType == EffectTypes::NV12) {
+ diagnosticFlags |= DiagnosticFlags::NV12;
+ } else if (effect->mType == EffectTypes::YCBCR) {
+ diagnosticFlags |= DiagnosticFlags::YCBCR;
+ }
+
+ if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
+ if (mImageContainer) {
+ aLayer->GetLayerManager()->
+ AppendImageCompositeNotification(ImageCompositeNotification(
+ mImageContainer, nullptr,
+ img->mTimeStamp, GetCompositor()->GetCompositionTime(),
+ img->mFrameID, img->mProducerID));
+ }
+ mLastFrameID = img->mFrameID;
+ mLastProducerID = img->mProducerID;
+ }
+ aEffectChain.mPrimaryEffect = effect;
+ gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
+ BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
+ if (it) {
+
+ // This iteration does not work if we have multiple texture sources here
+ // (e.g. 3 YCbCr textures). There's nothing preventing the different
+ // planes from having different resolutions or tile sizes. For example, a
+ // YCbCr frame could have Cb and Cr planes that are half the resolution of
+ // the Y plane, in such a way that the Y plane overflows the maximum
+ // texture size and the Cb and Cr planes do not. Then the Y plane would be
+ // split into multiple tiles and the Cb and Cr planes would just be one
+ // tile each.
+ // To handle the general case correctly, we'd have to create a grid of
+ // intersected tiles over all planes, and then draw each grid tile using
+ // the corresponding source tiles from all planes, with appropriate
+ // per-plane per-tile texture coords.
+ // DrawQuad currently assumes that all planes use the same texture coords.
+ MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
+ "Can't handle multi-plane BigImages");
+
+ it->BeginBigImageIteration();
+ do {
+ IntRect tileRect = it->GetTileRect();
+ gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
+ rect = rect.Intersect(pictureRect);
+ effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
+ Float(rect.y - tileRect.y) / tileRect.height,
+ Float(rect.width) / tileRect.width,
+ Float(rect.height) / tileRect.height);
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+ GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, mFlashCounter);
+ } while (it->NextTile());
+ it->EndBigImageIteration();
+ // layer border
+ GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
+ aClipRect, aTransform, mFlashCounter);
+ } else {
+ IntSize textureSize = mCurrentTextureSource->GetSize();
+ effect->mTextureCoords = Rect(Float(img->mPictureRect.x) / textureSize.width,
+ Float(img->mPictureRect.y) / textureSize.height,
+ Float(img->mPictureRect.width) / textureSize.width,
+ Float(img->mPictureRect.height) / textureSize.height);
+
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ effect->mTextureCoords.y = effect->mTextureCoords.YMost();
+ effect->mTextureCoords.height = -effect->mTextureCoords.height;
+ }
+
+ GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
+ aOpacity, aTransform);
+ GetCompositor()->DrawDiagnostics(diagnosticFlags,
+ pictureRect, aClipRect,
+ aTransform, mFlashCounter);
+ }
+ }
+
+ // Update mBias last. This can change which frame ChooseImage(Index) would
+ // return, and we don't want to do that until we've finished compositing
+ // since callers of ChooseImage(Index) assume the same image will be chosen
+ // during a given composition. This must happen after autoLock's
+ // destructor!
+ mBias = UpdateBias(
+ GetCompositor()->GetCompositionTime(), mImages[imageIndex].mTimeStamp,
+ uint32_t(imageIndex + 1) < mImages.Length() ?
+ mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
+ mBias);
+}
+
+void
+ImageHost::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor != aCompositor) {
+ for (auto& img : mImages) {
+ img.mTextureHost->SetCompositor(aCompositor);
+ }
+ }
+ if (mImageHostOverlay) {
+ mImageHostOverlay->SetCompositor(aCompositor);
+ }
+ CompositableHost::SetCompositor(aCompositor);
+}
+
+void
+ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
+
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ for (auto& img : mImages) {
+ aStream << "\n";
+ img.mTextureHost->PrintInfo(aStream, pfx.get());
+ AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
+ }
+
+ if (mImageHostOverlay) {
+ mImageHostOverlay->PrintInfo(aStream, aPrefix);
+ }
+}
+
+void
+ImageHost::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+ for (auto& img : mImages) {
+ aStream << aPrefix;
+ aStream << (aDumpHtml ? "<ul><li>TextureHost: "
+ : "TextureHost: ");
+ DumpTextureHost(aStream, img.mTextureHost);
+ aStream << (aDumpHtml ? " </li></ul> " : " ");
+ }
+}
+
+LayerRenderState
+ImageHost::GetRenderState()
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->GetRenderState();
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ return img->mTextureHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+ImageHost::GetAsSurface()
+{
+ if (mImageHostOverlay) {
+ return nullptr;
+ }
+
+ TimedImage* img = ChooseImage();
+ if (img) {
+ return img->mTextureHost->GetAsSurface();
+ }
+ return nullptr;
+}
+
+bool
+ImageHost::Lock()
+{
+ MOZ_ASSERT(!mLocked);
+ TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ SetCurrentTextureHost(img->mTextureHost);
+
+ if (!mCurrentTextureHost->Lock()) {
+ return false;
+ }
+ mLocked = true;
+ return true;
+}
+
+void
+ImageHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+
+ if (mCurrentTextureHost) {
+ mCurrentTextureHost->Unlock();
+ }
+ mLocked = false;
+}
+
+IntSize
+ImageHost::GetImageSize() const
+{
+ if (mImageHostOverlay) {
+ return mImageHostOverlay->GetImageSize();
+ }
+
+ const TimedImage* img = ChooseImage();
+ if (img) {
+ return IntSize(img->mPictureRect.width, img->mPictureRect.height);
+ }
+ return IntSize();
+}
+
+bool
+ImageHost::IsOpaque()
+{
+ const TimedImage* img = ChooseImage();
+ if (!img) {
+ return false;
+ }
+
+ if (img->mPictureRect.width == 0 ||
+ img->mPictureRect.height == 0 ||
+ !img->mTextureHost) {
+ return false;
+ }
+
+ gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
+ if (gfx::IsOpaque(format)) {
+ return true;
+ }
+ return false;
+}
+
+already_AddRefed<TexturedEffect>
+ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ TimedImage* img = ChooseImage();
+ if (!img) {
+ return nullptr;
+ }
+ SetCurrentTextureHost(img->mTextureHost);
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
+ return nullptr;
+ }
+ bool isAlphaPremultiplied = true;
+ if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
+ isAlphaPremultiplied = false;
+ }
+
+ return CreateTexturedEffect(mCurrentTextureHost,
+ mCurrentTextureSource,
+ aSamplingFilter,
+ isAlphaPremultiplied,
+ GetRenderState());
+}
+
+void
+ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
+{
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.RemoveElement(this);
+ }
+ mImageContainer = aImageContainer;
+ if (mImageContainer) {
+ mImageContainer->mImageHosts.AppendElement(this);
+ }
+}
+
+ImageHostOverlay::ImageHostOverlay()
+{
+ MOZ_COUNT_CTOR(ImageHostOverlay);
+}
+
+ImageHostOverlay::~ImageHostOverlay()
+{
+ if (mCompositor) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ MOZ_COUNT_DTOR(ImageHostOverlay);
+}
+
+/* static */ bool
+ImageHostOverlay::IsValid(OverlaySource aOverlay)
+{
+ if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) &&
+ aOverlay.handle().get_int32_t() != INVALID_OVERLAY) {
+ return true;
+ } else if (aOverlay.handle().type() == OverlayHandle::TGonkNativeHandle) {
+ return true;
+ }
+ return false;
+}
+
+void
+ImageHostOverlay::SetCompositor(Compositor* aCompositor)
+{
+ if (mCompositor && (mCompositor != aCompositor)) {
+ mCompositor->RemoveImageHostOverlay(this);
+ }
+ if (aCompositor) {
+ aCompositor->AddImageHostOverlay(this);
+ }
+ mCompositor = aCompositor;
+}
+
+void
+ImageHostOverlay::Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion)
+{
+ MOZ_ASSERT(mCompositor == aCompositor);
+
+ if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
+ return;
+ }
+
+ Color hollow(0.0f, 0.0f, 0.0f, 0.0f);
+ aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow);
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE);
+
+ gfx::Rect rect;
+ gfx::Rect clipRect(aClipRect.x, aClipRect.y,
+ aClipRect.width, aClipRect.height);
+ rect.SetRect(mPictureRect.x, mPictureRect.y,
+ mPictureRect.width, mPictureRect.height);
+
+ aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+ aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
+ rect, aClipRect, aTransform, aFlashCounter);
+}
+
+LayerRenderState
+ImageHostOverlay::GetRenderState()
+{
+ LayerRenderState state;
+ return state;
+}
+
+void
+ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay,
+ const nsIntRect& aPictureRect)
+{
+ mOverlay = aOverlay;
+ mPictureRect = aPictureRect;
+}
+
+IntSize
+ImageHostOverlay::GetImageSize() const
+{
+ return IntSize(mPictureRect.width, mPictureRect.height);
+}
+
+void
+ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get();
+
+ AppendToString(aStream, mPictureRect, " [picture-rect=", "]");
+
+ if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ aStream << nsPrintfCString("Overlay: %d", mOverlay.handle().get_int32_t()).get();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h
new file mode 100644
index 000000000..b8d23afee
--- /dev/null
+++ b/gfx/layers/composite/ImageHost.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGEHOST_H
+#define MOZILLA_GFX_IMAGEHOST_H
+
+#include <stdio.h> // for FILE
+#include "CompositableHost.h" // for CompositableHost
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+struct EffectChain;
+class ImageContainerParent;
+class ImageHostOverlay;
+
+/**
+ * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ */
+class ImageHost : public CompositableHost
+{
+public:
+ explicit ImageHost(const TextureInfo& aTextureInfo);
+ ~ImageHost();
+
+ virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+
+ virtual void RemoveTextureHost(TextureHost* aTexture) override;
+
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect) override;
+
+ virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void SetImageContainer(ImageContainerParent* aImageContainer) override;
+
+ gfx::IntSize GetImageSize() const override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix = "",
+ bool aDumpHtml = false) override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+ void SetCurrentTextureHost(TextureHost* aTexture);
+
+ virtual void CleanupResources() override;
+
+ int32_t GetFrameID()
+ {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mFrameID : -1;
+ }
+
+ int32_t GetProducerID()
+ {
+ const TimedImage* img = ChooseImage();
+ return img ? img->mProducerID : -1;
+ }
+
+ int32_t GetLastFrameID() const { return mLastFrameID; }
+ int32_t GetLastProducerID() const { return mLastProducerID; }
+
+ enum Bias {
+ // Don't apply bias to frame times
+ BIAS_NONE,
+ // Apply a negative bias to frame times to keep them before the vsync time
+ BIAS_NEGATIVE,
+ // Apply a positive bias to frame times to keep them after the vsync time
+ BIAS_POSITIVE,
+ };
+
+ bool IsOpaque();
+
+protected:
+ struct TimedImage {
+ CompositableTextureHostRef mTextureHost;
+ TimeStamp mTimeStamp;
+ gfx::IntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+
+ // Use a simple RefPtr because the same texture is already held by a
+ // a CompositableTextureHostRef in the array of TimedImage.
+ // See the comment in CompositableTextureRef for more details.
+ RefPtr<TextureHost> mCurrentTextureHost;
+ CompositableTextureSourceRef mCurrentTextureSource;
+ // When doing texture uploads it's best to alternate between two (or three)
+ // texture sources so that the texture we upload to isn't being used by
+ // the GPU to composite the previous frame.
+ RefPtr<TextureSource> mExtraTextureSource;
+
+ /**
+ * ChooseImage is guaranteed to return the same TimedImage every time it's
+ * called during the same composition, up to the end of Composite() ---
+ * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
+ * mBias is updated at the end of Composite().
+ */
+ const TimedImage* ChooseImage() const;
+ TimedImage* ChooseImage();
+ int ChooseImageIndex() const;
+
+ nsTArray<TimedImage> mImages;
+ // Weak reference, will be null if mImageContainer has been destroyed.
+ ImageContainerParent* mImageContainer;
+ int32_t mLastFrameID;
+ int32_t mLastProducerID;
+ /**
+ * Bias to apply to the next frame.
+ */
+ Bias mBias;
+
+ bool mLocked;
+
+ RefPtr<ImageHostOverlay> mImageHostOverlay;
+};
+
+/**
+ * ImageHostOverlay handles OverlaySource compositing
+ */
+class ImageHostOverlay {
+protected:
+ virtual ~ImageHostOverlay();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay)
+ ImageHostOverlay();
+
+ static bool IsValid(OverlaySource aOverlay);
+
+ void SetCompositor(Compositor* aCompositor);
+
+ virtual void Composite(Compositor* aCompositor,
+ uint32_t aFlashCounter,
+ LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion);
+ virtual LayerRenderState GetRenderState();
+ virtual void UseOverlaySource(OverlaySource aOverlay,
+ const gfx::IntRect& aPictureRect);
+ virtual gfx::IntSize GetImageSize() const;
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+protected:
+ RefPtr<Compositor> mCompositor;
+ gfx::IntRect mPictureRect;
+ OverlaySource mOverlay;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp
new file mode 100644
index 000000000..bac9f3790
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageLayerComposite.h"
+#include "CompositableHost.h" // for CompositableHost
+#include "Layers.h" // for WriteSnapshotToDumpFile, etc
+#include "gfx2DGlue.h" // for ToFilter
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/ImageHost.h" // for ImageHost
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+ImageLayerComposite::ImageLayerComposite(LayerManagerComposite* aManager)
+ : ImageLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mImageHost(nullptr)
+{
+ MOZ_COUNT_CTOR(ImageLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+ImageLayerComposite::~ImageLayerComposite()
+{
+ MOZ_COUNT_DTOR(ImageLayerComposite);
+ MOZ_ASSERT(mDestroyed);
+
+ CleanupResources();
+}
+
+bool
+ImageLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::IMAGE:
+ mImageHost = static_cast<ImageHost*>(aHost);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+ImageLayerComposite::Disconnect()
+{
+ Destroy();
+}
+
+LayerRenderState
+ImageLayerComposite::GetRenderState()
+{
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost->GetRenderState();
+ }
+ return LayerRenderState();
+}
+
+Layer*
+ImageLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mImageHost) {
+ mImageHost->SetCompositor(mCompositor);
+ }
+}
+
+void
+ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
+{
+ if (!mImageHost || !mImageHost->IsAttached()) {
+ return;
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+ mCompositor->MakeCurrent();
+
+ RenderWithAllMasks(this, mCompositor, aClipRect,
+ [&](EffectChain& effectChain, const IntRect& clipRect) {
+ mImageHost->SetCompositor(mCompositor);
+ mImageHost->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransformForBuffer(),
+ GetSamplingFilter(),
+ clipRect);
+ });
+ mImageHost->BumpFlashCounter();
+}
+
+void
+ImageLayerComposite::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+{
+ gfx::Matrix4x4 local = GetLocalTransform();
+
+ // Snap image edges to pixel boundaries
+ gfxRect sourceRect(0, 0, 0, 0);
+ if (mImageHost &&
+ mImageHost->IsAttached()) {
+ IntSize size = mImageHost->GetImageSize();
+ sourceRect.SizeTo(size.width, size.height);
+ }
+ // Snap our local transform first, and snap the inherited transform as well.
+ // This makes our snapping equivalent to what would happen if our content
+ // was drawn into a PaintedLayer (gfxContext would snap using the local
+ // transform, then we'd snap again when compositing the PaintedLayer).
+ mEffectiveTransform =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+
+ if (mScaleMode != ScaleMode::SCALE_NONE &&
+ sourceRect.width != 0.0 && sourceRect.height != 0.0) {
+ NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+ "No other scalemodes than stretch and none supported yet.");
+ local.PreScale(mScaleToSize.width / sourceRect.width,
+ mScaleToSize.height / sourceRect.height, 1.0);
+
+ mEffectiveTransformForBuffer =
+ SnapTransform(local, sourceRect, nullptr) *
+ SnapTransformTranslation(aTransformToSurface, nullptr);
+ } else {
+ mEffectiveTransformForBuffer = mEffectiveTransform;
+ }
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool
+ImageLayerComposite::IsOpaque()
+{
+ if (!mImageHost ||
+ !mImageHost->IsAttached()) {
+ return false;
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ return mImageHost->IsOpaque();
+ }
+ return false;
+}
+
+nsIntRegion
+ImageLayerComposite::GetFullyRenderedRegion()
+{
+ if (!mImageHost ||
+ !mImageHost->IsAttached()) {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+
+ if (mScaleMode == ScaleMode::STRETCH) {
+ nsIntRegion shadowVisibleRegion;
+ shadowVisibleRegion.And(GetShadowVisibleRegion().ToUnknownRegion(), nsIntRegion(gfx::IntRect(0, 0, mScaleToSize.width, mScaleToSize.height)));
+ return shadowVisibleRegion;
+ }
+
+ return GetShadowVisibleRegion().ToUnknownRegion();
+}
+
+CompositableHost*
+ImageLayerComposite::GetCompositableHost()
+{
+ if (mImageHost && mImageHost->IsAttached()) {
+ return mImageHost.get();
+ }
+
+ return nullptr;
+}
+
+void
+ImageLayerComposite::CleanupResources()
+{
+ if (mImageHost) {
+ mImageHost->CleanupResources();
+ mImageHost->Detach(this);
+ }
+ mImageHost = nullptr;
+}
+
+gfx::SamplingFilter
+ImageLayerComposite::GetSamplingFilter()
+{
+ return mSamplingFilter;
+}
+
+void
+ImageLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mImageHost->GenEffect(GetSamplingFilter());
+}
+
+void
+ImageLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ ImageLayer::PrintInfo(aStream, aPrefix);
+ if (mImageHost && mImageHost->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mImageHost->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/ImageLayerComposite.h b/gfx/layers/composite/ImageLayerComposite.h
new file mode 100644
index 000000000..445917a75
--- /dev/null
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_ImageLayerComposite_H
+#define GFX_ImageLayerComposite_H
+
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "nscore.h" // for nsACString
+#include "CompositableHost.h" // for CompositableHost
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+class Layer;
+
+class ImageLayerComposite : public ImageLayer,
+ public LayerComposite
+{
+ typedef gl::TextureImage TextureImage;
+
+public:
+ explicit ImageLayerComposite(LayerManagerComposite* aManager);
+
+protected:
+ virtual ~ImageLayerComposite();
+
+public:
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void Disconnect() override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
+
+ virtual void CleanupResources() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual const char* Name() const override { return "ImageLayerComposite"; }
+
+ virtual bool IsOpaque() override;
+
+ virtual nsIntRegion GetFullyRenderedRegion() override;
+
+protected:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter();
+
+private:
+ RefPtr<ImageHost> mImageHost;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_ImageLayerComposite_H */
diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp
new file mode 100644
index 000000000..800478d55
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerManagerComposite.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t, uint32_t
+#include "CanvasLayerComposite.h" // for CanvasLayerComposite
+#include "ColorLayerComposite.h" // for ColorLayerComposite
+#include "Composer2D.h" // for Composer2D
+#include "CompositableHost.h" // for CompositableHost
+#include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc
+#include "FPSCounter.h" // for FPSState, FPSCounter
+#include "FrameMetrics.h" // for FrameMetrics
+#include "GeckoProfiler.h" // for profiler_set_frame_number, etc
+#include "ImageLayerComposite.h" // for ImageLayerComposite
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "LayerScope.h" // for LayerScope Tool
+#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "TiledContentHost.h"
+#include "Units.h" // for ScreenIntRect
+#include "UnitTransforms.h" // for ViewAs
+#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "gfxPrefs.h" // for gfxPrefs
+#ifdef XP_MACOSX
+#include "gfxPlatformMac.h"
+#endif
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for frame color util
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersTypes.h" // for etc
+#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext
+#include "ipc/CompositorBench.h" // for CompositorBench
+#include "ipc/ShadowLayerUtils.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsAppRunner.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#ifdef MOZ_WIDGET_ANDROID
+#include <android/log.h>
+#include <android/native_window.h>
+#endif
+#if defined(MOZ_WIDGET_ANDROID)
+#include "opengl/CompositorOGL.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "ScopedGLHelpers.h"
+#endif
+#include "GeckoProfiler.h"
+#include "TextRenderer.h" // for TextRenderer
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "TreeTraversal.h" // for ForEachNode
+
+#ifdef USE_SKIA
+#include "PaintCounter.h" // For PaintCounter
+#endif
+
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ImageLayer;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+static LayerComposite*
+ToLayerComposite(Layer* aLayer)
+{
+ return static_cast<LayerComposite*>(aLayer->ImplData());
+}
+
+static void ClearSubtree(Layer* aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ ToLayerComposite(layer)->CleanupResources();
+ });
+}
+
+void
+LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
+{
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ Layer* subtree = aSubtree ? aSubtree : mRoot.get();
+ if (!subtree) {
+ return;
+ }
+
+ ClearSubtree(subtree);
+ // FIXME [bjacob]
+ // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
+ // Do we need that?
+}
+
+/**
+ * LayerManagerComposite
+ */
+LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
+: mWarningLevel(0.0f)
+, mUnusedApzTransformWarning(false)
+, mDisabledApzWarning(false)
+, mCompositor(aCompositor)
+, mInTransaction(false)
+, mIsCompositorReady(false)
+, mDebugOverlayWantsNextFrame(false)
+, mGeometryChanged(true)
+, mLastFrameMissedHWC(false)
+, mWindowOverlayChanged(false)
+, mLastPaintTime(TimeDuration::Forever())
+, mRenderStartTime(TimeStamp::Now())
+{
+ mTextRenderer = new TextRenderer(aCompositor);
+ MOZ_ASSERT(aCompositor);
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+}
+
+LayerManagerComposite::~LayerManagerComposite()
+{
+ Destroy();
+}
+
+
+void
+LayerManagerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mCompositor->GetWidget()->CleanupWindowEffects();
+ if (mRoot) {
+ RootLayer()->Destroy();
+ }
+ mRoot = nullptr;
+ mClonedLayerTreeProperties = nullptr;
+ mDestroyed = true;
+
+#ifdef USE_SKIA
+ mPaintCounter = nullptr;
+#endif
+ }
+}
+
+void
+LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect)
+{
+ mRenderBounds = aRect;
+}
+
+bool
+LayerManagerComposite::AreComponentAlphaLayersEnabled()
+{
+ return mCompositor->GetBackendType() != LayersBackend::LAYERS_BASIC &&
+ mCompositor->SupportsEffect(EffectTypes::COMPONENT_ALPHA) &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+bool
+LayerManagerComposite::BeginTransaction()
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return false;
+ }
+
+ mIsCompositorReady = true;
+ return true;
+}
+
+void
+LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const IntRect& aRect)
+{
+ mInTransaction = true;
+
+ if (!mCompositor->Ready()) {
+ return;
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ mIsCompositorReady = true;
+ mCompositor->SetTargetContext(aTarget, aRect);
+ mTarget = aTarget;
+ mTargetBounds = aRect;
+}
+
+/**
+ * Get accumulated transform of from the context creating layer to the
+ * given layer.
+ */
+static Matrix4x4
+GetAccTransformIn3DContext(Layer* aLayer) {
+ Matrix4x4 transform = aLayer->GetLocalTransform();
+ for (Layer* layer = aLayer->GetParent();
+ layer && layer->Extend3DContext();
+ layer = layer->GetParent()) {
+ transform = transform * layer->GetLocalTransform();
+ }
+ return transform;
+}
+
+void
+LayerManagerComposite::PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors)
+{
+ if (aLayer->Extend3DContext()) {
+ // For layers participating 3D rendering context, their visible
+ // region should be empty (invisible), so we pass through them
+ // without doing anything.
+
+ // Direct children of the establisher may have a clip, becaue the
+ // item containing it; ex. of nsHTMLScrollFrame, may give it one.
+ Maybe<ParentLayerIntRect> layerClip =
+ aLayer->AsLayerComposite()->GetShadowClipRect();
+ Maybe<ParentLayerIntRect> ancestorClipForChildren =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "Only direct children of the establisher could have a clip");
+
+ for (Layer* child = aLayer->GetLastChild();
+ child;
+ child = child->GetPrevSibling()) {
+ PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
+ ancestorClipForChildren);
+ }
+ return;
+ }
+
+ nsIntRegion localOpaque;
+ // Treat layers on the path to the root of the 3D rendering context as
+ // a giant layer if it is a leaf.
+ Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
+ Matrix transform2d;
+ Maybe<IntPoint> integerTranslation;
+ // If aLayer has a simple transform (only an integer translation) then we
+ // can easily convert aOpaqueRegion into pre-transform coordinates and include
+ // that region.
+ if (transform.Is2D(&transform2d)) {
+ if (transform2d.IsIntegerTranslation()) {
+ integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
+ localOpaque = aOpaqueRegion;
+ localOpaque.MoveBy(-*integerTranslation);
+ }
+ }
+
+ // Compute a clip that's the combination of our layer clip with the clip
+ // from our ancestors.
+ LayerComposite* composite = aLayer->AsLayerComposite();
+ Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
+ MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
+ "The layer with a clip should not participate "
+ "a 3D rendering context");
+ Maybe<ParentLayerIntRect> outsideClip =
+ IntersectMaybeRects(layerClip, aClipFromAncestors);
+
+ // Convert the combined clip into our pre-transform coordinate space, so
+ // that it can later be intersected with our visible region.
+ // If our transform is a perspective, there's no meaningful insideClip rect
+ // we can compute (it would need to be a cone).
+ Maybe<LayerIntRect> insideClip;
+ if (outsideClip && !transform.HasPerspectiveComponent()) {
+ Matrix4x4 inverse = transform;
+ if (inverse.Invert()) {
+ Maybe<LayerRect> insideClipFloat =
+ UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse),
+ ParentLayerRect(*outsideClip),
+ LayerRect::MaxIntRect());
+ if (insideClipFloat) {
+ insideClipFloat->RoundOut();
+ LayerIntRect insideClipInt;
+ if (insideClipFloat->ToIntRect(&insideClipInt)) {
+ insideClip = Some(insideClipInt);
+ }
+ }
+ }
+ }
+
+ Maybe<ParentLayerIntRect> ancestorClipForChildren;
+ if (insideClip) {
+ ancestorClipForChildren =
+ Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
+ }
+
+ // Save the value of localOpaque, which currently stores the region obscured
+ // by siblings (and uncles and such), before our descendants contribute to it.
+ nsIntRegion obscured = localOpaque;
+
+ // Recurse on our descendants, in front-to-back order. In this process:
+ // - Occlusions are computed for them, and they contribute to localOpaque.
+ // - They recalculate their visible regions, taking ancestorClipForChildren
+ // into account, and accumulate them into descendantsVisibleRegion.
+ LayerIntRegion descendantsVisibleRegion;
+ bool hasPreserve3DChild = false;
+ for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+ PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren);
+ if (child->Extend3DContext()) {
+ hasPreserve3DChild = true;
+ }
+ }
+
+ // Recalculate our visible region.
+ LayerIntRegion visible = composite->GetShadowVisibleRegion();
+
+ // If we have descendants, throw away the visible region stored on this
+ // layer, and use the region accumulated by our descendants instead.
+ if (aLayer->GetFirstChild() && !hasPreserve3DChild) {
+ visible = descendantsVisibleRegion;
+ }
+
+ // Subtract any areas that we know to be opaque.
+ if (!obscured.IsEmpty()) {
+ visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
+ }
+
+ // Clip the visible region using the combined clip.
+ if (insideClip) {
+ visible.AndWith(*insideClip);
+ }
+ composite->SetShadowVisibleRegion(visible);
+
+ // Transform the newly calculated visible region into our parent's space,
+ // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
+ // for the caller to use.
+ ParentLayerIntRegion visibleParentSpace = TransformBy(
+ ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
+ if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
+ visibleParentSpace.AndWith(*clipRect);
+ }
+ aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
+ PixelCastJustification::MovingDownToChildren));
+
+ // If we have a simple transform, then we can add our opaque area into
+ // aOpaqueRegion.
+ if (integerTranslation &&
+ !aLayer->HasMaskLayers() &&
+ aLayer->IsOpaqueForVisibility()) {
+ if (aLayer->IsOpaque()) {
+ localOpaque.OrWith(composite->GetFullyRenderedRegion());
+ }
+ localOpaque.MoveBy(*integerTranslation);
+ if (layerClip) {
+ localOpaque.AndWith(layerClip->ToUnknownRect());
+ }
+ aOpaqueRegion.OrWith(localOpaque);
+ }
+}
+
+void
+LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags)
+{
+ NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
+ NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
+ "Shouldn't get END_NO_COMPOSITE here");
+ mInTransaction = false;
+ mRenderStartTime = TimeStamp::Now();
+
+ if (!mIsCompositorReady) {
+ return;
+ }
+ mIsCompositorReady = false;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ // Set composition timestamp here because we need it in
+ // ComputeEffectiveTransforms (so the correct video frame size is picked) and
+ // also to compute invalid regions properly.
+ mCompositor->SetCompositionTime(aTimeStamp);
+
+ if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
+ MOZ_ASSERT(!aTimeStamp.IsNull());
+ UpdateAndRender();
+ mCompositor->FlushPendingNotifyNotUsed();
+ } else {
+ // Modified the layer tree.
+ mGeometryChanged = true;
+ }
+
+ mCompositor->ClearTargetContext();
+ mTarget = nullptr;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+}
+
+void
+LayerManagerComposite::UpdateAndRender()
+{
+ nsIntRegion invalid;
+ bool didEffectiveTransforms = false;
+
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+
+ if (mClonedLayerTreeProperties) {
+ // Effective transforms are needed by ComputeDifferences().
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ didEffectiveTransforms = true;
+
+ // We need to compute layer tree differences even if we're not going to
+ // immediately use the resulting damage area, since ComputeDifferences
+ // is also responsible for invalidates intermediate surfaces in
+ // ContainerLayers.
+ nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
+
+ if (mTarget) {
+ // Since we're composing to an external target, we're not going to use
+ // the damage region from layers changes - we want to composite
+ // everything in the target bounds. Instead we accumulate the layers
+ // damage region for the next window composite.
+ mInvalidRegion.Or(mInvalidRegion, changed);
+ } else {
+ invalid = Move(changed);
+ }
+ }
+
+ if (mTarget) {
+ invalid.Or(invalid, mTargetBounds);
+ } else {
+ // If we didn't have a previous layer tree, invalidate the entire render
+ // area.
+ if (!mClonedLayerTreeProperties) {
+ invalid.Or(invalid, mRenderBounds);
+ }
+
+ // Add any additional invalid rects from the window manager or previous
+ // damage computed during ComposeToTarget().
+ invalid.Or(invalid, mInvalidRegion);
+ mInvalidRegion.SetEmpty();
+ }
+
+ if (invalid.IsEmpty() && !mWindowOverlayChanged) {
+ // Composition requested, but nothing has changed. Don't do any work.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+ return;
+ }
+
+ // We don't want our debug overlay to cause more frames to happen
+ // so we will invalidate after we've decided if something changed.
+ InvalidateDebugOverlay(invalid, mRenderBounds);
+
+ if (!didEffectiveTransforms) {
+ // The results of our drawing always go directly into a pixel buffer,
+ // so we don't need to pass any global transform here.
+ mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
+ }
+
+ Render(invalid, opaque);
+#if defined(MOZ_WIDGET_ANDROID)
+ RenderToPresentationSurface();
+#endif
+ mGeometryChanged = false;
+ mWindowOverlayChanged = false;
+
+ // Update cached layer tree information.
+ mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
+}
+
+already_AddRefed<DrawTarget>
+LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<PaintedLayer>
+LayerManagerComposite::CreatePaintedLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ContainerLayer>
+LayerManagerComposite::CreateContainerLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<ImageLayer>
+LayerManagerComposite::CreateImageLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+already_AddRefed<ColorLayer>
+LayerManagerComposite::CreateColorLayer()
+{
+ MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+ "this should only be called on the drawing side");
+ RefPtr<ColorLayer> layer = new ColorLayerComposite(this);
+ return layer.forget();
+}
+
+already_AddRefed<CanvasLayer>
+LayerManagerComposite::CreateCanvasLayer()
+{
+ NS_RUNTIMEABORT("Should only be called on the drawing side");
+ return nullptr;
+}
+
+LayerComposite*
+LayerManagerComposite::RootLayer() const
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+
+ return ToLayerComposite(mRoot);
+}
+
+#ifdef MOZ_PROFILING
+// Only build the QR feature when profiling to avoid bloating
+// our data section.
+// This table was generated using qrencode and is a binary
+// encoding of the qrcodes 0-255.
+#include "qrcode_table.h"
+#endif
+
+void
+LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ if (drawFps || drawFrameCounter) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256));
+ }
+ if (drawFrameColorBars) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height));
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect()));
+ }
+#endif
+}
+
+#ifdef USE_SKIA
+void
+LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor)
+{
+ if (!mPaintCounter) {
+ mPaintCounter = new PaintCounter();
+ }
+
+ TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
+ mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
+}
+#endif
+
+static uint16_t sFrameCount = 0;
+void
+LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
+{
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
+ bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+
+ TimeStamp now = TimeStamp::Now();
+
+ if (drawFps) {
+ if (!mFPS) {
+ mFPS = MakeUnique<FPSState>();
+ }
+
+ float alpha = 1;
+#ifdef ANDROID
+ // Draw a translation delay warning overlay
+ int width;
+ int border;
+ if (!mWarnTime.IsNull() && (now - mWarnTime).ToMilliseconds() < kVisualWarningDuration) {
+ EffectChain effects;
+
+ // Black blorder
+ border = 4;
+ width = 6;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ // Content
+ border = 5;
+ width = 4;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1.f - mWarningLevel, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+ SetDebugOverlayWantsNextFrame(true);
+ }
+#endif
+
+ float fillRatio = mCompositor->GetFillRatio();
+ mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 1, 2, unsigned(fillRatio), mCompositor);
+
+ if (mUnusedApzTransformWarning) {
+ // If we have an unused APZ transform on this composite, draw a 20x20 red box
+ // in the top-right corner
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mUnusedApzTransformWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+ if (mDisabledApzWarning) {
+ // If we have a disabled APZ on this composite, draw a 20x20 yellow box
+ // in the top-right corner, to the left of the unused-apz-transform
+ // warning box
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1, 0, 1));
+ mCompositor->DrawQuad(gfx::Rect(aBounds.width - 40, 0, 20, 20),
+ aBounds, effects, alpha, gfx::Matrix4x4());
+
+ mDisabledApzWarning = false;
+ SetDebugOverlayWantsNextFrame(true);
+ }
+
+
+ // Each frame is invalidate by the previous frame for simplicity
+ } else {
+ mFPS = nullptr;
+ }
+
+ if (drawFrameColorBars) {
+ gfx::IntRect sideRect(0, 0, 10, aBounds.height);
+
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount));
+ mCompositor->DrawQuad(Rect(sideRect),
+ sideRect,
+ effects,
+ 1.0,
+ gfx::Matrix4x4());
+
+ }
+
+#ifdef MOZ_PROFILING
+ if (drawFrameCounter) {
+ profiler_set_frame_number(sFrameCount);
+ const char* qr = sQRCodeTable[sFrameCount%256];
+
+ int size = 21;
+ int padding = 2;
+ float opacity = 1.0;
+ const uint16_t bitWidth = 5;
+ gfx::IntRect clip(0,0, bitWidth*640, bitWidth*640);
+
+ // Draw the white squares at once
+ gfx::Color bitColor(1.0, 1.0, 1.0, 1.0);
+ EffectChain effects;
+ effects.mPrimaryEffect = new EffectSolidColor(bitColor);
+ int totalSize = (size + padding * 2) * bitWidth;
+ mCompositor->DrawQuad(gfx::Rect(0, 0, totalSize, totalSize),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+
+ // Draw a black square for every bit set in qr[index]
+ effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1.0));
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++) {
+ // Select the right bit from the binary encoding
+ int currBit = 128 >> ((x + y * 21) % 8);
+ int i = (x + y * 21) / 8;
+ if (qr[i] & currBit) {
+ mCompositor->DrawQuad(gfx::Rect(bitWidth * (x + padding),
+ bitWidth * (y + padding),
+ bitWidth, bitWidth),
+ clip,
+ effects,
+ opacity,
+ gfx::Matrix4x4());
+ }
+ }
+ }
+
+ }
+#endif
+
+ if (drawFrameColorBars || drawFrameCounter) {
+ // We intentionally overflow at 2^16.
+ sFrameCount++;
+ }
+
+#ifdef USE_SKIA
+ bool drawPaintTimes = gfxPrefs::AlwaysPaint();
+ if (drawPaintTimes) {
+ DrawPaintTimes(mCompositor);
+ }
+#endif
+}
+
+RefPtr<CompositingRenderTarget>
+LayerManagerComposite::PushGroupForLayerEffects()
+{
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(gfxPrefs::LayersEffectInvert() ||
+ gfxPrefs::LayersEffectGrayscale() ||
+ gfxPrefs::LayersEffectContrast() != 0.0);
+
+ RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget();
+ // make our render target the same size as the destination target
+ // so that we don't have to change size if the drawing area changes.
+ IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize());
+ // XXX: I'm not sure if this is true or not...
+ MOZ_ASSERT(rect.x == 0 && rect.y == 0);
+ if (!mTwoPassTmpTarget ||
+ mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() ||
+ mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) {
+ mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE);
+ }
+ MOZ_ASSERT(mTwoPassTmpTarget);
+ mCompositor->SetRenderTarget(mTwoPassTmpTarget);
+ return previousTarget;
+}
+void
+LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect)
+{
+ MOZ_ASSERT(mTwoPassTmpTarget);
+
+ // This is currently true, so just making sure that any new use of this
+ // method is flagged for investigation
+ MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0);
+
+ mCompositor->SetRenderTarget(aPreviousTarget);
+
+ EffectChain effectChain(RootLayer());
+ Matrix5x4 effectMatrix;
+ if (aGrayscaleEffect) {
+ // R' = G' = B' = luminance
+ // R' = 0.2126*R + 0.7152*G + 0.0722*B
+ // G' = 0.2126*R + 0.7152*G + 0.0722*B
+ // B' = 0.2126*R + 0.7152*G + 0.0722*B
+ Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0,
+ 0.7152f, 0.7152f, 0.7152f, 0,
+ 0.0722f, 0.0722f, 0.0722f, 0,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0);
+ effectMatrix = grayscaleMatrix;
+ }
+
+ if (aInvertEffect) {
+ // R' = 1 - R
+ // G' = 1 - G
+ // B' = 1 - B
+ Matrix5x4 colorInvertMatrix(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 0, 0, 0, 1,
+ 1, 1, 1, 0);
+ effectMatrix = effectMatrix * colorInvertMatrix;
+ }
+
+ if (aContrastEffect != 0.0) {
+ // Multiplying with:
+ // R' = (1 + c) * (R - 0.5) + 0.5
+ // G' = (1 + c) * (G - 0.5) + 0.5
+ // B' = (1 + c) * (B - 0.5) + 0.5
+ float cP1 = aContrastEffect + 1;
+ float hc = 0.5*aContrastEffect;
+ Matrix5x4 contrastMatrix( cP1, 0, 0, 0,
+ 0, cP1, 0, 0,
+ 0, 0, cP1, 0,
+ 0, 0, 0, 1,
+ -hc, -hc, -hc, 0);
+ effectMatrix = effectMatrix * contrastMatrix;
+ }
+
+ effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget);
+ effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix);
+
+ mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), aClipRect, effectChain, 1.,
+ Matrix4x4());
+}
+
+// Used to clear the 'mLayerComposited' flag at the beginning of each Render().
+static void
+ClearLayerFlags(Layer* aLayer) {
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer* layer)
+ {
+ if (layer->AsLayerComposite()) {
+ layer->AsLayerComposite()->SetLayerComposited(false);
+ }
+ });
+}
+
+void
+LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
+{
+ PROFILER_LABEL("LayerManagerComposite", "Render",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+
+ ClearLayerFlags(mRoot);
+
+ // At this time, it doesn't really matter if these preferences change
+ // during the execution of the function; we should be safe in all
+ // permutations. However, may as well just get the values onces and
+ // then use them, just in case the consistency becomes important in
+ // the future.
+ bool invertVal = gfxPrefs::LayersEffectInvert();
+ bool grayscaleVal = gfxPrefs::LayersEffectGrayscale();
+ float contrastVal = gfxPrefs::LayersEffectContrast();
+ bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
+
+ // Set LayerScope begin/end frame
+ LayerScopeAutoFrame frame(PR_Now());
+
+ // Dump to console
+ if (gfxPrefs::LayersDump()) {
+ this->Dump(/* aSorted= */true);
+ } else if (profiler_feature_active("layersdump")) {
+ std::stringstream ss;
+ Dump(ss);
+ profiler_log(ss.str().c_str());
+ }
+
+ // Dump to LayerScope Viewer
+ if (LayerScope::CheckSendable()) {
+ // Create a LayersPacket, dump Layers into it and transfer the
+ // packet('s ownership) to LayerScope.
+ auto packet = MakeUnique<layerscope::Packet>();
+ layerscope::LayersPacket* layersPacket = packet->mutable_layers();
+ this->Dump(layersPacket);
+ LayerScope::SendLayerDump(Move(packet));
+ }
+
+ /** Our more efficient but less powerful alter ego, if one is available. */
+ RefPtr<Composer2D> composer2D;
+ composer2D = mCompositor->GetWidget()->GetComposer2D();
+
+ // We can't use composert2D if we have layer effects
+ if (!mTarget && !haveLayerEffects &&
+ gfxPrefs::Composer2DCompositionEnabled() &&
+ composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot,
+ mCompositor->GetWidget()->RealWidget(),
+ mGeometryChanged,
+ mCompositor->HasImageHostOverlays()))
+ {
+ LayerScope::SetHWComposed();
+ if (mFPS) {
+ double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
+ if (gfxPrefs::LayersDrawFPS()) {
+ printf_stderr("HWComposer: FPS is %g\n", fps);
+ }
+ }
+ mCompositor->EndFrameForExternalComposition(Matrix());
+ mLastFrameMissedHWC = false;
+ return;
+ } else if (!mTarget && !haveLayerEffects) {
+ mLastFrameMissedHWC = !!composer2D;
+ }
+
+ mozilla::widget::WidgetRenderingContext widgetContext;
+#if defined(XP_MACOSX)
+ widgetContext.mLayerManager = this;
+#elif defined(MOZ_WIDGET_ANDROID)
+ widgetContext.mCompositor = GetCompositor();
+#endif
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "PreRender",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
+ return;
+ }
+ }
+
+ ParentLayerIntRect clipRect;
+ IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
+ IntRect actualBounds;
+
+ CompositorBench(mCompositor, bounds);
+
+ MOZ_ASSERT(mRoot->GetOpacity() == 1);
+#if defined(MOZ_WIDGET_ANDROID)
+ LayerMetricsWrapper wrapper = GetRootContentLayer();
+ if (wrapper) {
+ mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor());
+ } else {
+ mCompositor->SetClearColorToDefault();
+ }
+#endif
+ if (mRoot->GetClipRect()) {
+ clipRect = *mRoot->GetClipRect();
+ IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+ mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
+ } else {
+ gfx::IntRect rect;
+ mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds);
+ clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
+ }
+
+ if (actualBounds.IsEmpty()) {
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return;
+ }
+
+ // Allow widget to render a custom background.
+ mCompositor->GetWidget()->DrawWindowUnderlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ RefPtr<CompositingRenderTarget> previousTarget;
+ if (haveLayerEffects) {
+ previousTarget = PushGroupForLayerEffects();
+ } else {
+ mTwoPassTmpTarget = nullptr;
+ }
+
+ // Render our layers.
+ RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
+ RootLayer()->RenderLayer(clipRect.ToUnknownRect());
+
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
+ }
+ }
+
+ if (mTwoPassTmpTarget) {
+ MOZ_ASSERT(haveLayerEffects);
+ PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(),
+ grayscaleVal, invertVal, contrastVal);
+ }
+
+ // Allow widget to render a custom foreground.
+ mCompositor->GetWidget()->DrawWindowOverlay(
+ &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
+
+ // Debugging
+ RenderDebugOverlay(actualBounds);
+
+ {
+ PROFILER_LABEL("LayerManagerComposite", "EndFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mCompositor->EndFrame();
+
+ // Call after EndFrame()
+ mCompositor->SetDispAcquireFence(mRoot);
+ }
+
+ if (composer2D) {
+ composer2D->Render(mCompositor->GetWidget()->RealWidget());
+ }
+
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+
+ RecordFrame();
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+class ScopedCompositorProjMatrix {
+public:
+ ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
+ mCompositor(aCompositor),
+ mOriginalProjMatrix(mCompositor->GetProjMatrix())
+ {
+ mCompositor->SetProjMatrix(aProjMatrix);
+ }
+
+ ~ScopedCompositorProjMatrix()
+ {
+ mCompositor->SetProjMatrix(mOriginalProjMatrix);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const Matrix4x4 mOriginalProjMatrix;
+};
+
+class ScopedCompostitorSurfaceSize {
+public:
+ ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) :
+ mCompositor(aCompositor),
+ mOriginalSize(mCompositor->GetDestinationSurfaceSize())
+ {
+ mCompositor->SetDestinationSurfaceSize(aSize);
+ }
+ ~ScopedCompostitorSurfaceSize()
+ {
+ mCompositor->SetDestinationSurfaceSize(mOriginalSize);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const gfx::IntSize mOriginalSize;
+};
+
+class ScopedCompositorRenderOffset {
+public:
+ ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) :
+ mCompositor(aCompositor),
+ mOriginalOffset(mCompositor->GetScreenRenderOffset())
+ {
+ mCompositor->SetScreenRenderOffset(aOffset);
+ }
+ ~ScopedCompositorRenderOffset()
+ {
+ mCompositor->SetScreenRenderOffset(mOriginalOffset);
+ }
+private:
+ CompositorOGL* const mCompositor;
+ const ScreenPoint mOriginalOffset;
+};
+
+class ScopedContextSurfaceOverride {
+public:
+ ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) :
+ mContext(aContext)
+ {
+ MOZ_ASSERT(aSurface);
+ mContext->SetEGLSurfaceOverride(aSurface);
+ mContext->MakeCurrent(true);
+ }
+ ~ScopedContextSurfaceOverride()
+ {
+ mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ mContext->MakeCurrent(true);
+ }
+private:
+ GLContextEGL* const mContext;
+};
+
+void
+LayerManagerComposite::RenderToPresentationSurface()
+{
+#ifdef MOZ_WIDGET_ANDROID
+ nsIWidget* const widget = mCompositor->GetWidget()->RealWidget();
+ auto window = static_cast<ANativeWindow*>(
+ widget->GetNativeData(NS_PRESENTATION_WINDOW));
+
+ if (!window) {
+ return;
+ }
+
+ EGLSurface surface = widget->GetNativeData(NS_PRESENTATION_SURFACE);
+
+ if (!surface) {
+ //create surface;
+ surface = GLContextProviderEGL::CreateEGLSurface(window);
+ if (!surface) {
+ return;
+ }
+
+ widget->SetNativeData(NS_PRESENTATION_SURFACE,
+ reinterpret_cast<uintptr_t>(surface));
+ }
+
+ CompositorOGL* compositor = mCompositor->AsCompositorOGL();
+ GLContext* gl = compositor->gl();
+ GLContextEGL* egl = GLContextEGL::Cast(gl);
+
+ if (!egl) {
+ return;
+ }
+
+ const IntSize windowSize(ANativeWindow_getWidth(window),
+ ANativeWindow_getHeight(window));
+
+#endif
+
+ if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
+ return;
+ }
+
+ ScreenRotation rotation = compositor->GetScreenRotation();
+
+ const int actualWidth = windowSize.width;
+ const int actualHeight = windowSize.height;
+
+ const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
+ const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);
+
+ int pageWidth = originalSize.width;
+ int pageHeight = originalSize.height;
+ if (rotation == ROTATION_90 || rotation == ROTATION_270) {
+ pageWidth = originalSize.height;
+ pageHeight = originalSize.width;
+ }
+
+ float scale = 1.0;
+
+ if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) {
+ const float scaleWidth = (float)actualWidth / (float)pageWidth;
+ const float scaleHeight = (float)actualHeight / (float)pageHeight;
+ scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight;
+ }
+
+ const gfx::IntSize actualSize(actualWidth, actualHeight);
+ ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);
+
+ const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
+ ScopedContextSurfaceOverride overrideSurface(egl, surface);
+
+ Matrix viewMatrix = ComputeTransformForRotation(originalRect,
+ rotation);
+ viewMatrix.Invert(); // unrotate
+ viewMatrix.PostScale(scale, scale);
+ viewMatrix.PostTranslate(offset.x, offset.y);
+ Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
+
+ mRoot->ComputeEffectiveTransforms(matrix);
+ nsIntRegion opaque;
+ LayerIntRegion visible;
+ PostProcessLayers(mRoot, opaque, visible, Nothing());
+
+ nsIntRegion invalid;
+ IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight);
+ IntRect rect, actualBounds;
+ MOZ_ASSERT(mRoot->GetOpacity() == 1);
+ mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds);
+
+ // The Java side of Fennec sets a scissor rect that accounts for
+ // chrome such as the URL bar. Override that so that the entire frame buffer
+ // is cleared.
+ ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
+ egl->fClearColor(0.0, 0.0, 0.0, 0.0);
+ egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
+
+ const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
+
+ RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
+ RootLayer()->RenderLayer(clipRect);
+
+ mCompositor->EndFrame();
+}
+#endif
+
+already_AddRefed<PaintedLayerComposite>
+LayerManagerComposite::CreatePaintedLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<PaintedLayerComposite>(new PaintedLayerComposite(this)).forget();
+}
+
+already_AddRefed<ContainerLayerComposite>
+LayerManagerComposite::CreateContainerLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
+}
+
+already_AddRefed<ImageLayerComposite>
+LayerManagerComposite::CreateImageLayerComposite()
+{
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
+}
+
+already_AddRefed<ColorLayerComposite>
+LayerManagerComposite::CreateColorLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
+}
+
+already_AddRefed<CanvasLayerComposite>
+LayerManagerComposite::CreateCanvasLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
+}
+
+already_AddRefed<RefLayerComposite>
+LayerManagerComposite::CreateRefLayerComposite()
+{
+ if (LayerManagerComposite::mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return nullptr;
+ }
+ return RefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
+}
+
+LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
+ EffectChain& aEffects)
+ : mCompositable(nullptr), mFailed(false)
+{
+ if (!aMaskLayer) {
+ return;
+ }
+
+ mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost();
+ if (!mCompositable) {
+ NS_WARNING("Mask layer with no compositable host");
+ mFailed = true;
+ return;
+ }
+
+ if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform())) {
+ mCompositable = nullptr;
+ mFailed = true;
+ }
+}
+
+LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect()
+{
+ if (!mCompositable) {
+ return;
+ }
+
+ mCompositable->RemoveMaskEffect();
+}
+
+void
+LayerManagerComposite::ChangeCompositor(Compositor* aNewCompositor)
+{
+ mCompositor = aNewCompositor;
+ mTextRenderer = new TextRenderer(aNewCompositor);
+ mTwoPassTmpTarget = nullptr;
+}
+
+LayerComposite::LayerComposite(LayerManagerComposite *aManager)
+ : mCompositeManager(aManager)
+ , mCompositor(aManager->GetCompositor())
+ , mShadowOpacity(1.0)
+ , mShadowTransformSetByAnimation(false)
+ , mShadowOpacitySetByAnimation(false)
+ , mDestroyed(false)
+ , mLayerComposited(false)
+{ }
+
+LayerComposite::~LayerComposite()
+{
+}
+
+void
+LayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void
+LayerComposite::AddBlendModeEffect(EffectChain& aEffectChain)
+{
+ gfx::CompositionOp blendMode = GetLayer()->GetEffectiveMixBlendMode();
+ if (blendMode == gfx::CompositionOp::OP_OVER) {
+ return;
+ }
+
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(blendMode);
+ return;
+}
+
+bool
+LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize)
+{
+ return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width,
+ aSize.height));
+}
+
+void
+LayerManagerComposite::NotifyShadowTreeTransaction()
+{
+ if (mFPS) {
+ mFPS->NotifyShadowTreeTransaction();
+ }
+}
+
+void
+LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ mCompositeManager = aManager;
+ mCompositor = aManager->GetCompositor();
+}
+
+bool
+LayerManagerComposite::AsyncPanZoomEnabled() const
+{
+ if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
+ return bridge->AsyncPanZoomEnabled();
+ }
+ return false;
+}
+
+nsIntRegion
+LayerComposite::GetFullyRenderedRegion() {
+ if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
+ : nullptr) {
+ nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion();
+ // Discard the region which hasn't been drawn yet when doing
+ // progressive drawing. Note that if the shadow visible region
+ // shrunk the tiled valig region may not have discarded this yet.
+ shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
+ return shadowVisibleRegion;
+ } else {
+ return GetShadowVisibleRegion().ToUnknownRegion();
+ }
+}
+
+Matrix4x4
+LayerComposite::GetShadowTransform() {
+ Matrix4x4 transform = mShadowTransform;
+ Layer* layer = GetLayer();
+
+ transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
+ if (const ContainerLayer* c = layer->AsContainerLayer()) {
+ transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+ }
+
+ return transform;
+}
+
+bool
+LayerComposite::HasStaleCompositor() const
+{
+ return mCompositeManager->GetCompositor() != mCompositor;
+}
+
+static bool
+LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
+{
+ bool answer = false;
+ for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
+ if (!i.Metrics().IsScrollable()) {
+ continue;
+ }
+ if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
+ if (aOutColor) {
+ *aOutColor = i.Metadata().GetBackgroundColor();
+ }
+ answer = true;
+ break;
+ }
+ break;
+ }
+ return answer;
+}
+
+bool
+LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor)
+{
+ return GetLayer()->Manager()->AsyncPanZoomEnabled() &&
+ (GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ GetLayer()->IsOpaqueForVisibility() &&
+ LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor);
+}
+
+#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h
new file mode 100644
index 000000000..8fe7a6b34
--- /dev/null
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LayerManagerComposite_H
+#define GFX_LayerManagerComposite_H
+
+#include <stdint.h> // for int32_t, uint32_t
+#include "GLDefs.h" // for GLenum
+#include "Layers.h"
+#include "Units.h" // for ParentLayerIntRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+#include "LayerTreeInvalidation.h"
+
+class gfxContext;
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CanvasLayerComposite;
+class ColorLayerComposite;
+class CompositableHost;
+class Compositor;
+class ContainerLayerComposite;
+struct EffectChain;
+class ImageLayer;
+class ImageLayerComposite;
+class LayerComposite;
+class RefLayerComposite;
+class PaintedLayerComposite;
+class TextRenderer;
+class CompositingRenderTarget;
+struct FPSState;
+class PaintCounter;
+
+static const int kVisualWarningDuration = 150; // ms
+
+class LayerManagerComposite final : public LayerManager
+{
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::IntSize IntSize;
+ typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
+public:
+ explicit LayerManagerComposite(Compositor* aCompositor);
+ ~LayerManagerComposite();
+
+ virtual void Destroy() override;
+
+ /**
+ * Sets the clipping region for this layer manager. This is important on
+ * windows because using OGL we no longer have GDI's native clipping. Therefor
+ * widget must tell us what part of the screen is being invalidated,
+ * and we should clip to this.
+ *
+ * \param aClippingRegion Region to clip to. Setting an empty region
+ * will disable clipping.
+ */
+ void SetClippingRegion(const nsIntRegion& aClippingRegion)
+ {
+ mClippingRegion = aClippingRegion;
+ }
+
+ /**
+ * LayerManager implementation.
+ */
+ virtual LayerManagerComposite* AsLayerManagerComposite() override
+ {
+ return this;
+ }
+
+ void UpdateRenderBounds(const gfx::IntRect& aRect);
+
+ virtual bool BeginTransaction() override;
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override
+ {
+ MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
+ return false;
+ }
+ void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aRect);
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ return false;
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override
+ {
+ MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+ }
+ void EndTransaction(const TimeStamp& aTimeStamp,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ virtual void SetRoot(Layer* aLayer) override { mRoot = aLayer; }
+
+ // XXX[nrc]: never called, we should move this logic to ClientLayerManager
+ // (bug 946926).
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
+
+ virtual int32_t GetMaxTextureSize() const override
+ {
+ MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
+ }
+
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ already_AddRefed<PaintedLayerComposite> CreatePaintedLayerComposite();
+ already_AddRefed<ContainerLayerComposite> CreateContainerLayerComposite();
+ already_AddRefed<ImageLayerComposite> CreateImageLayerComposite();
+ already_AddRefed<ColorLayerComposite> CreateColorLayerComposite();
+ already_AddRefed<CanvasLayerComposite> CreateCanvasLayerComposite();
+ already_AddRefed<RefLayerComposite> CreateRefLayerComposite();
+
+ virtual LayersBackend GetBackendType() override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+ virtual void GetBackendName(nsAString& name) override
+ {
+ MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+ }
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ virtual already_AddRefed<DrawTarget>
+ CreateOptimalMaskDrawTarget(const IntSize &aSize) override;
+
+ virtual const char* Name() const override { return ""; }
+
+ /**
+ * Post-processes layers before composition. This performs the following:
+ *
+ * - Applies occlusion culling. This restricts the shadow visible region
+ * of layers that are covered with opaque content.
+ * |aOpaqueRegion| is the region already known to be covered with opaque
+ * content, in the post-transform coordinate space of aLayer.
+ *
+ * - Recomputes visible regions to account for async transforms.
+ * Each layer accumulates into |aVisibleRegion| its post-transform
+ * (including async transforms) visible region.
+ */
+ void PostProcessLayers(Layer* aLayer,
+ nsIntRegion& aOpaqueRegion,
+ LayerIntRegion& aVisibleRegion,
+ const Maybe<ParentLayerIntRect>& aClipFromAncestors);
+
+ /**
+ * RAII helper class to add a mask effect with the compositable from aMaskLayer
+ * to the EffectChain aEffect and notify the compositable when we are done.
+ */
+ class AutoAddMaskEffect
+ {
+ public:
+ AutoAddMaskEffect(Layer* aMaskLayer,
+ EffectChain& aEffect);
+ ~AutoAddMaskEffect();
+
+ bool Failed() const { return mFailed; }
+ private:
+ CompositableHost* mCompositable;
+ bool mFailed;
+ };
+
+ /**
+ * returns true if PlatformAllocBuffer will return a buffer that supports
+ * direct texturing
+ */
+ static bool SupportsDirectTexturing();
+
+ static void PlatformSyncBeforeReplyUpdate();
+
+ void AddInvalidRegion(const nsIntRegion& aRegion)
+ {
+ mInvalidRegion.Or(mInvalidRegion, aRegion);
+ }
+
+ void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
+ const Maybe<uint32_t>& aPresShellId)
+ {
+ for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+ if (iter.Key().mLayersId == aLayersId &&
+ (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
+ iter.Remove();
+ }
+ }
+ }
+
+ void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+ {
+ CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
+ MOZ_ASSERT(regionForScrollFrame);
+
+ *regionForScrollFrame = aRegion;
+ }
+
+ CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
+ {
+ return mVisibleRegions.Get(aGuid);
+ }
+
+ Compositor* GetCompositor() const
+ {
+ return mCompositor;
+ }
+
+ // Called by CompositorBridgeParent when a new compositor has been created due
+ // to a device reset. The layer manager must clear any cached resources
+ // attached to the old compositor, and make a best effort at ignoring
+ // layer or texture updates against the old compositor.
+ void ChangeCompositor(Compositor* aNewCompositor);
+
+ /**
+ * LayerManagerComposite provides sophisticated debug overlays
+ * that can request a next frame.
+ */
+ bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+ void SetDebugOverlayWantsNextFrame(bool aVal)
+ { mDebugOverlayWantsNextFrame = aVal; }
+
+ void NotifyShadowTreeTransaction();
+
+ TextRenderer* GetTextRenderer() { return mTextRenderer; }
+
+ /**
+ * Add an on frame warning.
+ * @param severity ranges from 0 to 1. It's used to compute the warning color.
+ */
+ void VisualFrameWarning(float severity) {
+ mozilla::TimeStamp now = TimeStamp::Now();
+ if (mWarnTime.IsNull() ||
+ severity > mWarningLevel ||
+ mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) < now) {
+ mWarnTime = now;
+ mWarningLevel = severity;
+ }
+ }
+
+ void UnusedApzTransformWarning() {
+ mUnusedApzTransformWarning = true;
+ }
+ void DisabledApzWarning() {
+ mDisabledApzWarning = true;
+ }
+
+ bool LastFrameMissedHWC() { return mLastFrameMissedHWC; }
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void AppendImageCompositeNotification(const ImageCompositeNotification& aNotification)
+ {
+ // Only send composite notifications when we're drawing to the screen,
+ // because that's what they mean.
+ // Also when we're not drawing to the screen, DidComposite will not be
+ // called to extract and send these notifications, so they might linger
+ // and contain stale ImageContainerParent pointers.
+ if (!mCompositor->GetTargetContext()) {
+ mImageCompositeNotifications.AppendElement(aNotification);
+ }
+ }
+ void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+ {
+ aNotifications->AppendElements(Move(mImageCompositeNotifications));
+ }
+
+ // Indicate that we need to composite even if nothing in our layers has
+ // changed, so that the widget can draw something different in its window
+ // overlay.
+ void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+ void ForcePresent() { mCompositor->ForcePresent(); }
+
+ void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+
+private:
+ /** Region we're clipping our current drawing to. */
+ nsIntRegion mClippingRegion;
+ gfx::IntRect mRenderBounds;
+
+ /** Current root layer. */
+ LayerComposite* RootLayer() const;
+
+ /**
+ * Update the invalid region and render it.
+ */
+ void UpdateAndRender();
+
+ /**
+ * Render the current layer tree to the active target.
+ */
+ void Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion);
+#if defined(MOZ_WIDGET_ANDROID)
+ void RenderToPresentationSurface();
+#endif
+
+ /**
+ * We need to know our invalid region before we're ready to render.
+ */
+ void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const gfx::IntRect& aBounds);
+
+ /**
+ * Render debug overlays such as the FPS/FrameCounter above the frame.
+ */
+ void RenderDebugOverlay(const gfx::IntRect& aBounds);
+
+
+ RefPtr<CompositingRenderTarget> PushGroupForLayerEffects();
+ void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
+ gfx::IntRect aClipRect,
+ bool aGrayscaleEffect,
+ bool aInvertEffect,
+ float aContrastEffect);
+
+ void ChangeCompositorInternal(Compositor* aNewCompositor);
+
+ float mWarningLevel;
+ mozilla::TimeStamp mWarnTime;
+ bool mUnusedApzTransformWarning;
+ bool mDisabledApzWarning;
+ RefPtr<Compositor> mCompositor;
+ UniquePtr<LayerProperties> mClonedLayerTreeProperties;
+
+ nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
+
+ /**
+ * Context target, nullptr when drawing directly to our swap chain.
+ */
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ nsIntRegion mInvalidRegion;
+
+ typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
+ CSSIntRegion> VisibleRegions;
+ VisibleRegions mVisibleRegions;
+
+ UniquePtr<FPSState> mFPS;
+
+ bool mInTransaction;
+ bool mIsCompositorReady;
+ bool mDebugOverlayWantsNextFrame;
+
+ RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
+ RefPtr<TextRenderer> mTextRenderer;
+ bool mGeometryChanged;
+
+ // Testing property. If hardware composer is supported, this will return
+ // true if the last frame was deemed 'too complicated' to be rendered.
+ bool mLastFrameMissedHWC;
+
+ bool mWindowOverlayChanged;
+ TimeDuration mLastPaintTime;
+ TimeStamp mRenderStartTime;
+
+#ifdef USE_SKIA
+ /**
+ * Render paint and composite times above the frame.
+ */
+ void DrawPaintTimes(Compositor* aCompositor);
+ RefPtr<PaintCounter> mPaintCounter;
+#endif
+};
+
+/**
+ * Composite layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Basic layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree, all rendering is
+ * done by a Compositor (see Compositor.h). As such, composite layers are
+ * platform-independent and can be used on any platform for which there is a
+ * Compositor implementation.
+ *
+ * The composite layer tree reflects exactly the basic layer tree. To
+ * composite to screen, the layer manager walks the layer tree calling render
+ * methods which in turn call into their CompositableHosts' Composite methods.
+ * These call Compositor::DrawQuad to do the rendering.
+ *
+ * Mostly, layers are updated during the layers transaction. This is done from
+ * CompositableClient to CompositableHost without interacting with the layer.
+ *
+ * A reference to the Compositor is stored in LayerManagerComposite.
+ */
+class LayerComposite
+{
+public:
+ explicit LayerComposite(LayerManagerComposite* aManager);
+
+ virtual ~LayerComposite();
+
+ virtual LayerComposite* GetFirstChildComposite()
+ {
+ return nullptr;
+ }
+
+ /* Do NOT call this from the generic LayerComposite destructor. Only from the
+ * concrete class destructor
+ */
+ virtual void Destroy();
+
+ virtual Layer* GetLayer() = 0;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager);
+
+ LayerManagerComposite* GetLayerManager() const { return mCompositeManager; }
+
+ /**
+ * Perform a first pass over the layer tree to render all of the intermediate
+ * surfaces that we can. This allows us to avoid framebuffer switches in the
+ * middle of our render which is inefficient especially on mobile GPUs. This
+ * must be called before RenderLayer.
+ */
+ virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
+
+ // TODO: This should also take RenderTargetIntRect like Prepare.
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
+
+ virtual bool SetCompositableHost(CompositableHost*)
+ {
+ // We must handle this gracefully, see bug 967824
+ NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
+ return false;
+ }
+ virtual CompositableHost* GetCompositableHost() = 0;
+
+ virtual void CleanupResources() = 0;
+
+ virtual void DestroyFrontBuffer() { }
+
+ void AddBlendModeEffect(EffectChain& aEffectChain);
+
+ virtual void GenEffectChain(EffectChain& aEffect) { }
+
+ /**
+ * The following methods are
+ *
+ * CONSTRUCTION PHASE ONLY
+ *
+ * They are analogous to the Layer interface.
+ */
+ void SetShadowVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ mShadowVisibleRegion = aRegion;
+ }
+
+ void SetShadowOpacity(float aOpacity)
+ {
+ mShadowOpacity = aOpacity;
+ }
+ void SetShadowOpacitySetByAnimation(bool aSetByAnimation)
+ {
+ mShadowOpacitySetByAnimation = aSetByAnimation;
+ }
+
+ void SetShadowClipRect(const Maybe<ParentLayerIntRect>& aRect)
+ {
+ mShadowClipRect = aRect;
+ }
+
+ void SetShadowBaseTransform(const gfx::Matrix4x4& aMatrix)
+ {
+ mShadowTransform = aMatrix;
+ }
+ void SetShadowTransformSetByAnimation(bool aSetByAnimation)
+ {
+ mShadowTransformSetByAnimation = aSetByAnimation;
+ }
+
+ void SetLayerComposited(bool value)
+ {
+ mLayerComposited = value;
+ }
+
+ void SetClearRect(const gfx::IntRect& aRect)
+ {
+ mClearRect = aRect;
+ }
+
+ // These getters can be used anytime.
+ float GetShadowOpacity() { return mShadowOpacity; }
+ const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
+ const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
+ const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
+ gfx::Matrix4x4 GetShadowTransform();
+ bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
+ bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
+ bool HasLayerBeenComposited() { return mLayerComposited; }
+ gfx::IntRect GetClearRect() { return mClearRect; }
+
+ // Returns false if the layer is attached to an older compositor.
+ bool HasStaleCompositor() const;
+
+ /**
+ * Return the part of the visible region that has been fully rendered.
+ * While progressive drawing is in progress this region will be
+ * a subset of the shadow visible region.
+ */
+ virtual nsIntRegion GetFullyRenderedRegion();
+
+ /**
+ * Return true if a checkerboarding background color needs to be drawn
+ * for this layer.
+ */
+ bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
+
+protected:
+ gfx::Matrix4x4 mShadowTransform;
+ LayerIntRegion mShadowVisibleRegion;
+ Maybe<ParentLayerIntRect> mShadowClipRect;
+ LayerManagerComposite* mCompositeManager;
+ RefPtr<Compositor> mCompositor;
+ float mShadowOpacity;
+ bool mShadowTransformSetByAnimation;
+ bool mShadowOpacitySetByAnimation;
+ bool mDestroyed;
+ bool mLayerComposited;
+ gfx::IntRect mClearRect;
+};
+
+// Render aLayer using aCompositor and apply all mask layers of aLayer: The
+// layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
+// layers.
+// If more than one mask layer needs to be applied, we use intermediate surfaces
+// (CompositingRenderTargets) for rendering, applying one mask layer at a time.
+// Callers need to provide a callback function aRenderCallback that does the
+// actual rendering of the source. It needs to have the following form:
+// void (EffectChain& effectChain, const Rect& clipRect)
+// aRenderCallback is called exactly once, inside this function, unless aLayer's
+// visible region is completely clipped out (in that case, aRenderCallback won't
+// be called at all).
+// This function calls aLayer->AsLayerComposite()->AddBlendModeEffect for the
+// final rendering pass.
+//
+// (This function should really live in LayerManagerComposite.cpp, but we
+// need to use templates for passing lambdas until bug 1164522 is resolved.)
+template<typename RenderCallbackType>
+void
+RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
+ const gfx::IntRect& aClipRect,
+ RenderCallbackType aRenderCallback)
+{
+ Layer* firstMask = nullptr;
+ size_t maskLayerCount = 0;
+ size_t nextAncestorMaskLayer = 0;
+
+ size_t ancestorMaskLayerCount = aLayer->GetAncestorMaskLayerCount();
+ if (Layer* ownMask = aLayer->GetMaskLayer()) {
+ firstMask = ownMask;
+ maskLayerCount = ancestorMaskLayerCount + 1;
+ nextAncestorMaskLayer = 0;
+ } else if (ancestorMaskLayerCount > 0) {
+ firstMask = aLayer->GetAncestorMaskLayerAt(0);
+ maskLayerCount = ancestorMaskLayerCount;
+ nextAncestorMaskLayer = 1;
+ } else {
+ // no mask layers at all
+ }
+
+ if (maskLayerCount <= 1) {
+ // This is the common case. Render in one pass and return.
+ EffectChain effectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ autoMaskEffect(firstMask, effectChain);
+ aLayer->AsLayerComposite()->AddBlendModeEffect(effectChain);
+ aRenderCallback(effectChain, aClipRect);
+ return;
+ }
+
+ // We have multiple mask layers.
+ // We split our list of mask layers into three parts:
+ // (1) The first mask
+ // (2) The list of intermediate masks (every mask except first and last)
+ // (3) The final mask.
+ // Part (2) can be empty.
+ // For parts (1) and (2) we need to allocate intermediate surfaces to render
+ // into. The final mask gets rendered into the original render target.
+
+ // Calculate the size of the intermediate surfaces.
+ gfx::Rect visibleRect(aLayer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
+ // TODO: Use RenderTargetIntRect and TransformBy here
+ gfx::IntRect surfaceRect =
+ RoundedOut(transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
+ if (surfaceRect.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<CompositingRenderTarget> originalTarget =
+ aCompositor->GetCurrentRenderTarget();
+
+ RefPtr<CompositingRenderTarget> firstTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!firstTarget) {
+ return;
+ }
+
+ // Render the source while applying the first mask.
+ aCompositor->SetRenderTarget(firstTarget);
+ {
+ EffectChain firstEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ firstMaskEffect(firstMask, firstEffectChain);
+ aRenderCallback(firstEffectChain, aClipRect - surfaceRect.TopLeft());
+ // firstTarget now contains the transformed source with the first mask and
+ // opacity already applied.
+ }
+
+ // Apply the intermediate masks.
+ gfx::IntRect intermediateClip(surfaceRect - surfaceRect.TopLeft());
+ RefPtr<CompositingRenderTarget> previousTarget = firstTarget;
+ for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) {
+ Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i);
+ RefPtr<CompositingRenderTarget> intermediateTarget =
+ aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+ if (!intermediateTarget) {
+ break;
+ }
+ aCompositor->SetRenderTarget(intermediateTarget);
+ EffectChain intermediateEffectChain(aLayer);
+ LayerManagerComposite::AutoAddMaskEffect
+ intermediateMaskEffect(intermediateMask, intermediateEffectChain);
+ if (intermediateMaskEffect.Failed()) {
+ continue;
+ }
+ intermediateEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), intermediateClip,
+ intermediateEffectChain, 1.0, gfx::Matrix4x4());
+ previousTarget = intermediateTarget;
+ }
+
+ aCompositor->SetRenderTarget(originalTarget);
+
+ // Apply the final mask, rendering into originalTarget.
+ EffectChain finalEffectChain(aLayer);
+ finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+ Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
+
+ // The blend mode needs to be applied in this final step, because this is
+ // where we're blending with the actual background (which is in originalTarget).
+ aLayer->AsLayerComposite()->AddBlendModeEffect(finalEffectChain);
+ LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask, finalEffectChain);
+ if (!autoMaskEffect.Failed()) {
+ aCompositor->DrawQuad(gfx::Rect(surfaceRect), aClipRect,
+ finalEffectChain, 1.0, gfx::Matrix4x4());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_LayerManagerComposite_H */
diff --git a/gfx/layers/composite/PaintCounter.cpp b/gfx/layers/composite/PaintCounter.cpp
new file mode 100644
index 000000000..56e57aab4
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "mozilla/Sprintf.h"
+
+#include "mozilla/gfx/HelpersSkia.h"
+#include "PaintCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+// Positioned below the chrome UI
+IntRect PaintCounter::mRect = IntRect(0, 175, 300, 60);
+
+PaintCounter::PaintCounter()
+{
+ mFormat = SurfaceFormat::B8G8R8A8;
+ mSurface = Factory::CreateDataSourceSurface(mRect.Size(), mFormat);
+ mStride = mSurface->Stride();
+
+ mCanvas.reset(
+ SkCanvas::NewRasterDirect(MakeSkiaImageInfo(mRect.Size(), mFormat),
+ mSurface->GetData(), mStride));
+ mCanvas->clear(SK_ColorWHITE);
+}
+
+PaintCounter::~PaintCounter()
+{
+ mSurface = nullptr;
+ mTextureSource = nullptr;
+ mTexturedEffect = nullptr;
+}
+
+void
+PaintCounter::Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime) {
+ char buffer[48];
+ SprintfLiteral(buffer, "P: %.2f C: %.2f",
+ aPaintTime.ToMilliseconds(),
+ aCompositeTime.ToMilliseconds());
+
+ SkPaint paint;
+ paint.setTextSize(32);
+ paint.setColor(SkColorSetRGB(0, 255, 0));
+ paint.setAntiAlias(true);
+
+ mCanvas->clear(SK_ColorTRANSPARENT);
+ mCanvas->drawText(buffer, strlen(buffer), 10, 30, paint);
+ mCanvas->flush();
+
+ if (!mTextureSource) {
+ mTextureSource = aCompositor->CreateDataTextureSource();
+ mTexturedEffect = CreateTexturedEffect(mFormat, mTextureSource,
+ SamplingFilter::POINT, true);
+ mTexturedEffect->mTextureCoords = Rect(0, 0, 1.0f, 1.0f);
+ }
+
+ mTextureSource->Update(mSurface);
+
+ EffectChain effectChain;
+ effectChain.mPrimaryEffect = mTexturedEffect;
+
+ gfx::Matrix4x4 identity;
+ Rect rect(mRect.x, mRect.y, mRect.width, mRect.height);
+ aCompositor->DrawQuad(rect, mRect, effectChain, 1.0, identity);
+}
+
+} // end namespace layers
+} // end namespace mozilla
diff --git a/gfx/layers/composite/PaintCounter.h b/gfx/layers/composite/PaintCounter.h
new file mode 100644
index 000000000..b5296939f
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PaintCounter_h_
+#define mozilla_layers_PaintCounter_h_
+
+#include <map> // for std::map
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
+#include "skia/include/core/SkCanvas.h"
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+// Keeps track and paints how long a full invalidation paint takes to rasterize
+// and composite.
+class PaintCounter {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PaintCounter)
+
+ PaintCounter();
+ void Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime);
+ static IntRect GetPaintRect() { return PaintCounter::mRect; }
+
+private:
+ virtual ~PaintCounter();
+
+ SurfaceFormat mFormat;
+ sk_sp<SkCanvas> mCanvas;
+ IntSize mSize;
+ int mStride;
+
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTextureSource;
+ RefPtr<TexturedEffect> mTexturedEffect;
+ static IntRect mRect;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_PaintCounter_h_
diff --git a/gfx/layers/composite/PaintedLayerComposite.cpp b/gfx/layers/composite/PaintedLayerComposite.cpp
new file mode 100644
index 000000000..b58f5d690
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PaintedLayerComposite.h"
+#include "CompositableHost.h" // for TiledLayerProperties, etc
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for CSSRect, LayerPixel, etc
+#include "gfxEnv.h" // for gfxEnv
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for RoundedToInt, Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter::LINEAR
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ContentHost.h" // for ContentHost
+#include "mozilla/layers/Effects.h" // for EffectChain
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsMathUtils.h" // for NS_lround
+#include "nsString.h" // for nsAutoCString
+#include "TextRenderer.h"
+#include "GeckoProfiler.h"
+
+namespace mozilla {
+namespace layers {
+
+PaintedLayerComposite::PaintedLayerComposite(LayerManagerComposite *aManager)
+ : PaintedLayer(aManager, nullptr)
+ , LayerComposite(aManager)
+ , mBuffer(nullptr)
+{
+ MOZ_COUNT_CTOR(PaintedLayerComposite);
+ mImplData = static_cast<LayerComposite*>(this);
+}
+
+PaintedLayerComposite::~PaintedLayerComposite()
+{
+ MOZ_COUNT_DTOR(PaintedLayerComposite);
+ CleanupResources();
+}
+
+bool
+PaintedLayerComposite::SetCompositableHost(CompositableHost* aHost)
+{
+ switch (aHost->GetType()) {
+ case CompositableType::CONTENT_TILED:
+ case CompositableType::CONTENT_SINGLE:
+ case CompositableType::CONTENT_DOUBLE:
+ mBuffer = static_cast<ContentHost*>(aHost);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void
+PaintedLayerComposite::Disconnect()
+{
+ Destroy();
+}
+
+void
+PaintedLayerComposite::Destroy()
+{
+ if (!mDestroyed) {
+ CleanupResources();
+ mDestroyed = true;
+ }
+}
+
+Layer*
+PaintedLayerComposite::GetLayer()
+{
+ return this;
+}
+
+void
+PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+ LayerComposite::SetLayerManager(aManager);
+ mManager = aManager;
+ if (mBuffer && mCompositor) {
+ mBuffer->SetCompositor(mCompositor);
+ }
+}
+
+LayerRenderState
+PaintedLayerComposite::GetRenderState()
+{
+ if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
+ return LayerRenderState();
+ }
+ return mBuffer->GetRenderState();
+}
+
+void
+PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+{
+ if (!mBuffer || !mBuffer->IsAttached()) {
+ return;
+ }
+ PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ Compositor* compositor = mCompositeManager->GetCompositor();
+
+ MOZ_ASSERT(mBuffer->GetCompositor() == compositor &&
+ mBuffer->GetLayer() == this,
+ "buffer is corrupted");
+
+ const nsIntRegion visibleRegion = GetLocalVisibleRegion().ToUnknownRegion();
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
+ if (surf) {
+ WriteSnapshotToDumpFile(this, surf);
+ }
+ }
+#endif
+
+
+ RenderWithAllMasks(this, compositor, aClipRect,
+ [&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
+ mBuffer->SetPaintWillResample(MayResample());
+
+ mBuffer->Composite(this, effectChain,
+ GetEffectiveOpacity(),
+ GetEffectiveTransform(),
+ GetSamplingFilter(),
+ clipRect,
+ &visibleRegion);
+ });
+
+ mBuffer->BumpFlashCounter();
+
+ compositor->MakeCurrent();
+}
+
+CompositableHost*
+PaintedLayerComposite::GetCompositableHost()
+{
+ if (mBuffer && mBuffer->IsAttached()) {
+ return mBuffer.get();
+ }
+
+ return nullptr;
+}
+
+void
+PaintedLayerComposite::CleanupResources()
+{
+ if (mBuffer) {
+ mBuffer->Detach(this);
+ }
+ mBuffer = nullptr;
+}
+
+void
+PaintedLayerComposite::GenEffectChain(EffectChain& aEffect)
+{
+ aEffect.mLayerRef = this;
+ aEffect.mPrimaryEffect = mBuffer->GenEffect(GetSamplingFilter());
+}
+
+void
+PaintedLayerComposite::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mBuffer && mBuffer->IsAttached()) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mBuffer->PrintInfo(aStream, pfx.get());
+ }
+}
+
+const gfx::TiledIntRegion&
+PaintedLayerComposite::GetInvalidRegion()
+{
+ if (mBuffer) {
+ nsIntRegion region = mInvalidRegion.GetRegion();
+ mBuffer->AddAnimationInvalidation(region);
+ }
+ return mInvalidRegion;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/PaintedLayerComposite.h b/gfx/layers/composite/PaintedLayerComposite.h
new file mode 100644
index 000000000..45a89eccf
--- /dev/null
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_PaintedLayerComposite_H
+#define GFX_PaintedLayerComposite_H
+
+#include "Layers.h" // for Layer (ptr only), etc
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PaintedLayers use ContentHosts for their compsositable host.
+ * By using different ContentHosts, PaintedLayerComposite support tiled and
+ * non-tiled PaintedLayers and single or double buffering.
+ */
+
+class CompositableHost;
+class ContentHost;
+
+class PaintedLayerComposite : public PaintedLayer,
+ public LayerComposite
+{
+public:
+ explicit PaintedLayerComposite(LayerManagerComposite *aManager);
+
+protected:
+ virtual ~PaintedLayerComposite();
+
+public:
+ virtual void Disconnect() override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ CompositableHost* GetCompositableHost() override;
+
+ virtual void Destroy() override;
+
+ virtual Layer* GetLayer() override;
+
+ virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+
+ virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+
+ virtual void CleanupResources() override;
+
+ virtual void GenEffectChain(EffectChain& aEffect) override;
+
+ virtual bool SetCompositableHost(CompositableHost* aHost) override;
+
+ virtual LayerComposite* AsLayerComposite() override { return this; }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_RUNTIMEABORT("PaintedLayerComposites can't fill invalidated regions");
+ }
+
+ void SetValidRegion(const nsIntRegion& aRegion)
+ {
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ValidRegion", this));
+ mValidRegion = aRegion;
+ Mutated();
+ }
+
+ const virtual gfx::TiledIntRegion& GetInvalidRegion() override;
+
+ MOZ_LAYER_DECL_NAME("PaintedLayerComposite", TYPE_PAINTED)
+
+protected:
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+private:
+ gfx::SamplingFilter GetSamplingFilter() { return gfx::SamplingFilter::LINEAR; }
+
+private:
+ RefPtr<ContentHost> mBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_PaintedLayerComposite_H */
diff --git a/gfx/layers/composite/TextRenderer.cpp b/gfx/layers/composite/TextRenderer.cpp
new file mode 100644
index 000000000..be59cb246
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextRenderer.h"
+#include "FontData.h"
+#include "png.h"
+#include "mozilla/Base64.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/Effects.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace std;
+
+const Float sBackgroundOpacity = 0.6f;
+const SurfaceFormat sTextureFormat = SurfaceFormat::B8G8R8A8;
+
+static void PNGAPI info_callback(png_structp png_ptr, png_infop info_ptr)
+{
+ png_read_update_info(png_ptr, info_ptr);
+}
+
+static void PNGAPI row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
+{
+ MOZ_ASSERT(sTextureFormat == SurfaceFormat::B8G8R8A8);
+
+ DataSourceSurface::MappedSurface map = static_cast<TextRenderer*>(png_get_progressive_ptr(png_ptr))->GetSurfaceMap();
+
+ uint32_t* dst = (uint32_t*)(map.mData + map.mStride * row_num);
+
+ for (uint32_t x = 0; x < sTextureWidth; x++) {
+ // We blend to a transparent white background, this will make text readable
+ // even if it's on a dark background. Without hurting our ability to
+ // interact with the content behind the text.
+ Float alphaValue = Float(0xFF - new_row[x]) / 255.0f;
+ Float baseValue = sBackgroundOpacity * (1.0f - alphaValue);
+ Color pixelColor(baseValue, baseValue, baseValue, baseValue + alphaValue);
+ dst[x] = pixelColor.ToABGR();
+ }
+}
+
+TextRenderer::~TextRenderer()
+{
+ if (mGlyphBitmaps) {
+ mGlyphBitmaps->Unmap();
+ }
+}
+
+void
+TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin,
+ const Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth)
+{
+ EnsureInitialized();
+
+ // For now we only have a bitmap font with a 16px cell size, so we just
+ // scale it up if the user wants larger text.
+ Float scaleFactor = Float(aTextSize) / Float(sCellHeight);
+
+ aTargetPixelWidth /= scaleFactor;
+
+ uint32_t numLines = 1;
+ uint32_t maxWidth = 0;
+ uint32_t lineWidth = 0;
+ // Calculate the size of the surface needed to draw all the glyphs.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ // Insert a line break if we go past the TargetPixelWidth.
+ // XXX - this has the downside of overrunning the intended width, causing
+ // things at the edge of a window to be cut off.
+ if (aText[i] == '\n' || (aText[i] == ' ' && lineWidth > aTargetPixelWidth)) {
+ numLines++;
+ lineWidth = 0;
+ continue;
+ }
+
+ lineWidth += sGlyphWidths[uint32_t(aText[i])];
+ maxWidth = std::max(lineWidth, maxWidth);
+ }
+
+ // Create a surface to draw our glyphs to.
+ RefPtr<DataSourceSurface> textSurf =
+ Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
+ if (NS_WARN_IF(!textSurf)) {
+ return;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (NS_WARN_IF(!textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map))) {
+ return;
+ }
+
+ // Initialize the surface to transparent white.
+ memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
+ numLines * sCellHeight * map.mStride);
+
+ uint32_t currentXPos = 0;
+ uint32_t currentYPos = 0;
+
+ // Copy our glyphs onto the surface.
+ for (uint32_t i = 0; i < aText.length(); i++) {
+ if (aText[i] == '\n' || (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) {
+ currentYPos += sCellHeight;
+ currentXPos = 0;
+ continue;
+ }
+
+ uint32_t glyphXOffset = aText[i] % (sTextureWidth / sCellWidth) * sCellWidth * BytesPerPixel(sTextureFormat);
+ uint32_t truncatedLine = aText[i] / (sTextureWidth / sCellWidth);
+ uint32_t glyphYOffset = truncatedLine * sCellHeight * mMap.mStride;
+
+ for (int y = 0; y < 16; y++) {
+ memcpy(map.mData + (y + currentYPos) * map.mStride + currentXPos * BytesPerPixel(sTextureFormat),
+ mMap.mData + glyphYOffset + y * mMap.mStride + glyphXOffset,
+ sGlyphWidths[uint32_t(aText[i])] * BytesPerPixel(sTextureFormat));
+ }
+
+ currentXPos += sGlyphWidths[uint32_t(aText[i])];
+ }
+
+ textSurf->Unmap();
+
+ RefPtr<DataTextureSource> src = mCompositor->CreateDataTextureSource();
+
+ if (!src->Update(textSurf)) {
+ // Upload failed.
+ return;
+ }
+
+ RefPtr<EffectRGB> effect = new EffectRGB(src, true, SamplingFilter::LINEAR);
+ EffectChain chain;
+ chain.mPrimaryEffect = effect;
+
+ Matrix4x4 transform = aTransform;
+ transform.PreScale(scaleFactor, scaleFactor, 1.0f);
+ mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
+ IntRect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
+}
+
+void
+TextRenderer::EnsureInitialized()
+{
+ if (mGlyphBitmaps) {
+ return;
+ }
+
+ mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
+ if (NS_WARN_IF(!mGlyphBitmaps)) {
+ return;
+ }
+
+ if (NS_WARN_IF(!mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
+ return;
+ }
+
+ png_structp png_ptr = NULL;
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+
+ png_set_progressive_read_fn(png_ptr, this, info_callback, row_callback, nullptr);
+ png_infop info_ptr = NULL;
+ info_ptr = png_create_info_struct(png_ptr);
+
+ png_process_data(png_ptr, info_ptr, (uint8_t*)sFontPNG, sizeof(sFontPNG));
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextRenderer.h b/gfx/layers/composite/TextRenderer.h
new file mode 100644
index 000000000..7665558eb
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TextRenderer_H
+#define GFX_TextRenderer_H
+
+#include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+class TextRenderer
+{
+ ~TextRenderer();
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextRenderer)
+
+ explicit TextRenderer(Compositor *aCompositor)
+ : mCompositor(aCompositor), mMap({nullptr, 0})
+ {
+ }
+
+ void RenderText(const std::string& aText, const gfx::IntPoint& aOrigin,
+ const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
+ uint32_t aTargetPixelWidth);
+
+ gfx::DataSourceSurface::MappedSurface& GetSurfaceMap() { return mMap; }
+
+private:
+
+ // Note that this may still fail to set mGlyphBitmaps to a valid value
+ // if the underlying CreateDataSourceSurface fails for some reason.
+ void EnsureInitialized();
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
+ gfx::DataSourceSurface::MappedSurface mMap;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 000000000..8c5b8c7b7
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,1142 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "LayersLogging.h" // for AppendToString
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/GPUVideoTextureHost.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "mozilla/layers/PTextureParent.h"
+#include "mozilla/Unused.h"
+#include <limits>
+#include "../opengl/CompositorOGL.h"
+#include "gfxPrefs.h"
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+#ifdef MOZ_ENABLE_D3D10_LAYER
+#include "../d3d11/CompositorD3D11.h"
+#endif
+
+#ifdef MOZ_X11
+#include "mozilla/layers/X11TextureHost.h"
+#endif
+
+#ifdef XP_MACOSX
+#include "../opengl/MacIOSurfaceTextureHostOGL.h"
+#endif
+
+#ifdef XP_WIN
+#include "mozilla/layers/TextureDIB.h"
+#endif
+
+#if 0
+#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#define RECYCLE_LOG(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * TextureParent is the host-side IPDL glue between TextureClient and TextureHost.
+ * It is an IPDL actor just like LayerParent, CompositableParent, etc.
+ */
+class TextureParent : public ParentActor<PTextureParent>
+{
+public:
+ explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial);
+
+ ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) override;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ virtual void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ virtual bool RecvDestroySync() override {
+ DestroyIfNeeded();
+ return true;
+ }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent*
+TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t &&
+ !aAllocator->IsSameProcess())
+ {
+ NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
+ return nullptr;
+ }
+ TextureParent* actor = new TextureParent(aAllocator, aSerial);
+ if (!actor->Init(aSharedData, aLayersBackend, aFlags)) {
+ delete actor;
+ return nullptr;
+ }
+ return actor;
+}
+
+// static
+bool
+TextureHost::DestroyIPDLActor(PTextureParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+// static
+bool
+TextureHost::SendDeleteIPDLActor(PTextureParent* actor)
+{
+ return PTextureParent::Send__delete__(actor);
+}
+
+// static
+TextureHost*
+TextureHost::AsTextureHost(PTextureParent* actor)
+{
+ if (!actor) {
+ return nullptr;
+ }
+ return static_cast<TextureParent*>(actor)->mTextureHost;
+}
+
+// static
+uint64_t
+TextureHost::GetTextureSerial(PTextureParent* actor)
+{
+ if (!actor) {
+ return UINT64_MAX;
+ }
+ return static_cast<TextureParent*>(actor)->mSerial;
+}
+
+PTextureParent*
+TextureHost::GetIPDLActor()
+{
+ return mActor;
+}
+
+void
+TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId)
+{
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+}
+
+// implemented in TextureHostOGL.cpp
+already_AddRefed<TextureHost> CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureHostBasic.cpp
+already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D11.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D9.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D9(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+already_AddRefed<TextureHost>
+TextureHost::Create(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags)
+{
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL) {
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ return MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D9:
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ if (aBackend == LayersBackend::LAYERS_D3D9) {
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
+ }
+#endif
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+}
+
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ const SurfaceDescriptorBuffer& bufferDesc = aDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ switch (data.type()) {
+ case MemoryOrShmem::TShmem: {
+ result = new ShmemTextureHost(data.get_Shmem(),
+ bufferDesc.desc(),
+ aDeallocator,
+ aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(),
+ aFlags);
+ break;
+ }
+ default:
+ gfxCriticalError() << "Failed texture host for backend " << (int)data.type();
+ MOZ_CRASH("GFX: No texture host for backend");
+ }
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
+ result = new GPUVideoTextureHost(aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ result = new DIBTextureHost(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping: {
+ result = new TextureHostFileMapping(aFlags, aDesc);
+ break;
+ }
+#endif
+ default: {
+ NS_WARNING("No backend independent TextureHost for this descriptor type");
+ }
+ }
+ return result.forget();
+}
+
+TextureHost::TextureHost(TextureFlags aFlags)
+ : AtomicRefCountedWithFinalize("TextureHost")
+ , mActor(nullptr)
+ , mFlags(aFlags)
+ , mCompositableCount(0)
+ , mFwdTransactionId(0)
+{
+}
+
+TextureHost::~TextureHost()
+{
+ // If we still have a ReadLock, unlock it. At this point we don't care about
+ // the texture client being written into on the other side since it should be
+ // destroyed by now. But we will hit assertions if we don't ReadUnlock before
+ // destroying the lock itself.
+ ReadUnlock();
+}
+
+void TextureHost::Finalize()
+{
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void
+TextureHost::UnbindTextureSource()
+{
+ if (mReadLock) {
+ auto compositor = GetCompositor();
+ // This TextureHost is not used anymore. Since most compositor backends are
+ // working asynchronously under the hood a compositor could still be using
+ // this texture, so it is generally best to wait until the end of the next
+ // composition before calling ReadUnlock. We ask the compositor to take care
+ // of that for us.
+ if (compositor) {
+ compositor->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ }
+ }
+}
+
+void
+TextureHost::RecycleTexture(TextureFlags aFlags)
+{
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(aFlags & TextureFlags::RECYCLE);
+ mFlags = aFlags;
+}
+
+void
+TextureHost::NotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+
+ // Do not need to call NotifyNotUsed() if TextureHost does not have
+ // TextureFlags::RECYCLE flag.
+ if (!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ auto compositor = GetCompositor();
+ // The following cases do not need to defer NotifyNotUsed until next Composite.
+ // - TextureHost does not have Compositor.
+ // - Compositor is BasicCompositor.
+ // - TextureHost has intermediate buffer.
+ // end of buffer usage.
+ if (!compositor ||
+ compositor->IsDestroyed() ||
+ compositor->AsBasicCompositor() ||
+ HasIntermediateBuffer()) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+
+ compositor->NotifyNotUsedAfterComposition(this);
+}
+
+void
+TextureHost::CallNotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+void
+TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ // Note: the TextureHost needs to be locked before it is safe to call
+ // GetSize() and GetFormat() on it.
+ if (Lock()) {
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ Unlock();
+ }
+ AppendToString(aStream, mFlags, " [flags=", "]");
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+void
+TextureHost::Updated(const nsIntRegion* aRegion)
+{
+ LayerScope::ContentChanged(this);
+ UpdatedInternal(aRegion);
+}
+
+TextureSource::TextureSource()
+: mCompositableCount(0)
+{
+ MOZ_COUNT_CTOR(TextureSource);
+}
+
+TextureSource::~TextureSource()
+{
+ MOZ_COUNT_DTOR(TextureSource);
+}
+
+const char*
+TextureSource::Name() const
+{
+ MOZ_CRASH("GFX: TextureSource without class name");
+ return "TextureSource";
+}
+
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: TextureHost(aFlags)
+, mCompositor(nullptr)
+, mUpdateSerial(1)
+, mLocked(false)
+, mNeedsFullUpdate(false)
+{
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.ySize();
+ mFormat = gfx::SurfaceFormat::YUV;
+ mHasIntermediateBuffer = ycbcr.hasIntermediateBuffer();
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+ mSize = rgb.size();
+ mFormat = rgb.format();
+ mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+ if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+ // One texture of a component alpha texture pair will start out all white.
+ // This hack allows us to easily make sure that white will be uploaded.
+ // See bug 1138934
+ mNeedsFullUpdate = true;
+ }
+}
+
+BufferTextureHost::~BufferTextureHost()
+{}
+
+void
+BufferTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ ++mUpdateSerial;
+ // If the last frame wasn't uploaded yet, and we -don't- have a partial update,
+ // we still need to update the full surface.
+ if (aRegion && !mNeedsFullUpdate) {
+ mMaybeUpdatedRegion.OrWith(*aRegion);
+ } else {
+ mNeedsFullUpdate = true;
+ }
+ if (GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) {
+ DebugOnly<bool> result = MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+ NS_WARNING_ASSERTION(result, "Failed to upload a texture");
+ }
+}
+
+void
+BufferTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ if (mCompositor == aCompositor) {
+ return;
+ }
+ if (aCompositor && mCompositor &&
+ aCompositor->GetBackendType() == mCompositor->GetBackendType()) {
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(aCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mCompositor = aCompositor;
+}
+
+void
+BufferTextureHost::DeallocateDeviceData()
+{
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ return;
+ }
+
+ if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+ mFirstSource = nullptr;
+ return;
+ }
+
+ mFirstSource->SetOwner(nullptr);
+
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->DeallocateDeviceData();
+ it = it->GetNextSibling();
+ }
+}
+
+bool
+BufferTextureHost::Lock()
+{
+ MOZ_ASSERT(!mLocked);
+ if (!MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr)) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void
+BufferTextureHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void
+TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator)
+{
+ RefPtr<TextureReadLock> lock = TextureReadLock::Deserialize(aDesc, aAllocator);
+ if (!lock) {
+ return;
+ }
+
+ // If mReadLock is not null it means we haven't unlocked it yet and the content
+ // side should not have been able to write into this texture and send a new lock!
+ MOZ_ASSERT(!mReadLock);
+ mReadLock = lock.forget();
+}
+
+void
+TextureHost::ReadUnlock()
+{
+ if (mReadLock) {
+ mReadLock->ReadUnlock();
+ mReadLock = nullptr;
+ }
+}
+
+bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+ MOZ_ASSERT(!mHasIntermediateBuffer);
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ return true;
+ }
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mCompositor->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+ }
+
+ if (!mFirstSource) {
+ // BasicCompositor::CreateDataTextureSourceAround never returns null
+ // and we don't expect to take this branch if we are using another backend.
+ // Returning false is fine but if we get into this situation it probably
+ // means something fishy is going on, like a texture being used with
+ // several compositor backends.
+ NS_WARNING("Failed to use a BufferTextureHost without intermediate buffer");
+ return false;
+ }
+
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
+static
+bool IsCompatibleTextureSource(TextureSource* aTexture,
+ const BufferDescriptor& aDescriptor,
+ Compositor* aCompositor)
+{
+ if (!aCompositor) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
+ && aTexture->GetSize() == ycbcr.ySize();
+ }
+
+ if (aTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || aTexture->GetSize() != ycbcr.ySize()) {
+ return false;
+ }
+
+ auto cbTexture = aTexture->GetSubSource(1);
+ if (!cbTexture
+ || cbTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || cbTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ auto crTexture = aTexture->GetSubSource(2);
+ if (!crTexture
+ || crTexture->GetFormat() != gfx::SurfaceFormat::A8
+ || crTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ return true;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
+ return aTexture->GetFormat() == rgb.format()
+ && aTexture->GetSize() == rgb.size();
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+void
+BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ // Reuse WrappingTextureSourceYCbCrBasic to reduce memory consumption.
+ if (mFormat == gfx::SurfaceFormat::YUV &&
+ !mHasIntermediateBuffer &&
+ aTexture.get() &&
+ aTexture->AsWrappingTextureSourceYCbCrBasic() &&
+ aTexture->NumCompositableRefs() <= 1 &&
+ aTexture->GetSize() == GetSize()) {
+ aTexture->AsSourceBasic()->SetBufferTextureHost(this);
+ aTexture->AsDataTextureSource()->SetOwner(this);
+ mFirstSource = aTexture->AsDataTextureSource();
+ mNeedsFullUpdate = true;
+ }
+
+ if (!mHasIntermediateBuffer) {
+ EnsureWrappingTextureSource();
+ }
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTexture = mFirstSource.get();
+ return;
+ }
+
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+
+ bool compatibleFormats = texture && IsCompatibleTextureSource(texture,
+ mDescriptor,
+ mCompositor);
+
+ bool shouldCreateTexture = !compatibleFormats
+ || texture->NumCompositableRefs() > 1
+ || texture->HasOwner();
+
+ if (!shouldCreateTexture) {
+ mFirstSource = texture;
+ mFirstSource->SetOwner(this);
+ mNeedsFullUpdate = true;
+
+ // It's possible that texture belonged to a different compositor,
+ // so make sure we update it (and all of its siblings) to the
+ // current one.
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(mCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool
+BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+void
+BufferTextureHost::UnbindTextureSource()
+{
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->Unbind();
+ }
+ // This texture is not used by any layer anymore.
+ // If the texture doesn't have an intermediate buffer, it means we are
+ // compositing synchronously on the CPU, so we don't need to wait until
+ // the end of the next composition to ReadUnlock (which other textures do
+ // by default).
+ // If the texture has an intermediate buffer we don't care either because
+ // texture uploads are also performed synchronously for BufferTextureHost.
+ ReadUnlock();
+}
+
+gfx::SurfaceFormat
+BufferTextureHost::GetFormat() const
+{
+ // mFormat is the format of the data that we share with the content process.
+ // GetFormat, on the other hand, expects the format that we present to the
+ // Compositor (it is used to choose the effect type).
+ // if the compositor does not support YCbCr effects, we give it a RGBX texture
+ // instead (see BufferTextureHost::Upload)
+ if (mFormat == gfx::SurfaceFormat::YUV &&
+ mCompositor &&
+ !mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+YUVColorSpace
+BufferTextureHost::GetYUVColorSpace() const
+{
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+bool
+BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
+{
+ auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+ if (serial == mUpdateSerial) {
+ return true;
+ }
+
+ if (serial == 0) {
+ // 0 means the source has no valid content
+ aRegion = nullptr;
+ }
+
+ if (!Upload(aRegion)) {
+ return false;
+ }
+
+ if (mHasIntermediateBuffer) {
+ // We just did the texture upload, the content side can now freely write
+ // into the shared buffer.
+ ReadUnlock();
+ }
+
+ // We no longer have an invalid region.
+ mNeedsFullUpdate = false;
+ mMaybeUpdatedRegion.SetEmpty();
+
+ // If upload returns true we know mFirstSource is not null
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ return true;
+}
+
+bool
+BufferTextureHost::Upload(nsIntRegion *aRegion)
+{
+ uint8_t* buf = GetBuffer();
+ if (!buf) {
+ // We don't have a buffer; a possible cause is that the IPDL actor
+ // is already dead. This inevitably happens as IPDL actors can die
+ // at any time, so we want to silently return in this case.
+ // another possible cause is that IPDL failed to map the shmem when
+ // deserializing it.
+ return false;
+ }
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ return true;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return false;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ mFirstSource->Update(surf, aRegion);
+ return true;
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ mFirstSource = srcY;
+ mFirstSource->SetOwner(this);
+ srcY->SetNextSibling(srcU);
+ srcU->SetNextSibling(srcV);
+ } else {
+ // mFormat never changes so if this was created as a YCbCr host and already
+ // contains a source it should already have 3 sources.
+ // BufferTextureHost only uses DataTextureSources so it is safe to assume
+ // all 3 sources are DataTextureSource.
+ MOZ_ASSERT(mFirstSource->GetNextSibling());
+ MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
+ srcY = mFirstSource;
+ srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
+ srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
+ }
+
+ RefPtr<gfx::DataSourceSurface> tempY =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
+ desc.ySize().width,
+ desc.ySize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ // We don't support partial updates for Y U V textures
+ NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
+ if (!tempY ||
+ !tempCb ||
+ !tempCr ||
+ !srcY->Update(tempY) ||
+ !srcU->Update(tempCb) ||
+ !srcV->Update(tempCr)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ } else {
+ // non-YCbCr case
+ nsIntRegion* regionToUpdate = aRegion;
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ // Update the full region the first time for component alpha textures.
+ regionToUpdate = nullptr;
+ }
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+
+ if (!mFirstSource->Update(surf.get(), regionToUpdate)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ }
+ MOZ_ASSERT(mFirstSource);
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+BufferTextureHost::GetAsSurface()
+{
+ RefPtr<gfx::DataSourceSurface> result;
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return nullptr;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ GetBuffer(), mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+ } else {
+ result =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ }
+ return result.forget();
+}
+
+ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+: BufferTextureHost(aDesc, aFlags)
+, mDeallocator(aDeallocator)
+{
+ if (aShmem.IsReadable()) {
+ mShmem = MakeUnique<ipc::Shmem>(aShmem);
+ } else {
+ // This can happen if we failed to map the shmem on this process, perhaps
+ // because it was big and we didn't have enough contiguous address space
+ // available, even though we did on the child process.
+ // As a result this texture will be in an invalid state and Lock will
+ // always fail.
+
+ gfxCriticalNote << "Failed to create a valid ShmemTextureHost";
+ }
+
+ MOZ_COUNT_CTOR(ShmemTextureHost);
+}
+
+ShmemTextureHost::~ShmemTextureHost()
+{
+ MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(ShmemTextureHost);
+}
+
+void
+ShmemTextureHost::DeallocateSharedData()
+{
+ if (mShmem) {
+ MOZ_ASSERT(mDeallocator,
+ "Shared memory would leak without a ISurfaceAllocator");
+ mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem);
+ mShmem = nullptr;
+ }
+}
+
+void
+ShmemTextureHost::ForgetSharedData()
+{
+ if (mShmem) {
+ mShmem = nullptr;
+ }
+}
+
+void
+ShmemTextureHost::OnShutdown()
+{
+ mShmem = nullptr;
+}
+
+uint8_t* ShmemTextureHost::GetBuffer()
+{
+ return mShmem ? mShmem->get<uint8_t>() : nullptr;
+}
+
+size_t ShmemTextureHost::GetBufferSize()
+{
+ return mShmem ? mShmem->Size<uint8_t>() : 0;
+}
+
+MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: BufferTextureHost(aDesc, aFlags)
+, mBuffer(aBuffer)
+{
+ MOZ_COUNT_CTOR(MemoryTextureHost);
+}
+
+MemoryTextureHost::~MemoryTextureHost()
+{
+ MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(MemoryTextureHost);
+}
+
+void
+MemoryTextureHost::DeallocateSharedData()
+{
+ if (mBuffer) {
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ }
+ delete[] mBuffer;
+ mBuffer = nullptr;
+}
+
+void
+MemoryTextureHost::ForgetSharedData()
+{
+ mBuffer = nullptr;
+}
+
+uint8_t* MemoryTextureHost::GetBuffer()
+{
+ return mBuffer;
+}
+
+size_t MemoryTextureHost::GetBufferSize()
+{
+ // MemoryTextureHost just trusts that the buffer size is large enough to read
+ // anything we need to. That's because MemoryTextureHost has to trust the buffer
+ // pointer anyway, so the security model here is just that MemoryTexture's
+ // are restricted to same-process clients.
+ return std::numeric_limits<size_t>::max();
+}
+
+TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, uint64_t aSerial)
+: mSurfaceAllocator(aSurfaceAllocator)
+, mSerial(aSerial)
+{
+ MOZ_COUNT_CTOR(TextureParent);
+}
+
+TextureParent::~TextureParent()
+{
+ MOZ_COUNT_DTOR(TextureParent);
+}
+
+void
+TextureParent::NotifyNotUsed(uint64_t aTransactionId)
+{
+ if (!mTextureHost) {
+ return;
+ }
+ mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
+}
+
+bool
+TextureParent::Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aBackend,
+ const TextureFlags& aFlags)
+{
+ mTextureHost = TextureHost::Create(aSharedData,
+ mSurfaceAllocator,
+ aBackend,
+ aFlags);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void
+TextureParent::Destroy()
+{
+ if (!mTextureHost) {
+ return;
+ }
+
+ // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+ // protocol that created it.
+ mTextureHost->ReadUnlock();
+
+ if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTextureHost->ForgetSharedData();
+ }
+
+ mTextureHost->mActor = nullptr;
+ mTextureHost = nullptr;
+}
+
+void
+TextureHost::ReceivedDestroy(PTextureParent* aActor)
+{
+ static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
+bool
+TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
+{
+ if (!mTextureHost) {
+ return true;
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h
new file mode 100644
index 000000000..c224d8777
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.h
@@ -0,0 +1,900 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOST_H
+#define MOZILLA_GFX_TEXTUREHOST_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t, uint8_t
+#include "gfxTypes.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTraceRefcnt.h" // for MOZ_COUNT_CTOR, etc
+#include "nscore.h" // for nsACString
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class BufferDescriptor;
+class BufferTextureHost;
+class Compositor;
+class CompositableParentManager;
+class ReadLockDescriptor;
+class CompositorBridgeParent;
+class SurfaceDescriptor;
+class HostIPCAllocator;
+class ISurfaceAllocator;
+class TextureHostOGL;
+class TextureReadLock;
+class TextureSourceOGL;
+class TextureSourceD3D9;
+class TextureSourceD3D11;
+class TextureSourceBasic;
+class DataTextureSource;
+class PTextureParent;
+class TextureParent;
+class WrappingTextureSourceYCbCrBasic;
+
+/**
+ * A view on a TextureHost where the texture is internally represented as tiles
+ * (contrast with a tiled buffer, where each texture is a tile). For iteration by
+ * the texture's buffer host.
+ * This is only useful when the underlying surface is too big to fit in one
+ * device texture, which forces us to split it in smaller parts.
+ * Tiled Compositable is a different thing.
+ */
+class BigImageIterator
+{
+public:
+ virtual void BeginBigImageIteration() = 0;
+ virtual void EndBigImageIteration() {};
+ virtual gfx::IntRect GetTileRect() = 0;
+ virtual size_t GetTileCount() = 0;
+ virtual bool NextTile() = 0;
+};
+
+/**
+ * TextureSource is the interface for texture objects that can be composited
+ * by a given compositor backend. Since the drawing APIs are different
+ * between backends, the TextureSource interface is split into different
+ * interfaces (TextureSourceOGL, etc.), and TextureSource mostly provide
+ * access to these interfaces.
+ *
+ * This class is used on the compositor side.
+ */
+class TextureSource: public RefCounted<TextureSource>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureSource)
+
+ TextureSource();
+
+ virtual ~TextureSource();
+
+ virtual const char* Name() const = 0;
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+
+ /**
+ * Return the size of the texture in texels.
+ * If this is a tile iterator, GetSize must return the size of the current tile.
+ */
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Return the pixel format of this texture
+ */
+ virtual gfx::SurfaceFormat GetFormat() const { return gfx::SurfaceFormat::UNKNOWN; }
+
+ /**
+ * Cast to a TextureSource for for each backend..
+ */
+ virtual TextureSourceOGL* AsSourceOGL() {
+ gfxCriticalNote << "Failed to cast " << Name() << " into a TextureSourceOGL";
+ return nullptr;
+ }
+ virtual TextureSourceD3D9* AsSourceD3D9() { return nullptr; }
+ virtual TextureSourceD3D11* AsSourceD3D11() { return nullptr; }
+ virtual TextureSourceBasic* AsSourceBasic() { return nullptr; }
+ /**
+ * Cast to a DataTextureSurce.
+ */
+ virtual DataTextureSource* AsDataTextureSource() { return nullptr; }
+ virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() { return nullptr; }
+
+ /**
+ * Overload this if the TextureSource supports big textures that don't fit in
+ * one device texture and must be tiled internally.
+ */
+ virtual BigImageIterator* AsBigImageIterator() { return nullptr; }
+
+ virtual void SetCompositor(Compositor* aCompositor) {}
+
+ virtual void Unbind() {}
+
+ void SetNextSibling(TextureSource* aTexture) { mNextSibling = aTexture; }
+
+ TextureSource* GetNextSibling() const { return mNextSibling; }
+
+ /**
+ * In some rare cases we currently need to consider a group of textures as one
+ * TextureSource, that can be split in sub-TextureSources.
+ */
+ TextureSource* GetSubSource(int index)
+ {
+ switch (index) {
+ case 0: return this;
+ case 1: return GetNextSibling();
+ case 2: return GetNextSibling() ? GetNextSibling()->GetNextSibling() : nullptr;
+ }
+ return nullptr;
+ }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ void ReleaseCompositableRef() {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+protected:
+
+ RefPtr<TextureSource> mNextSibling;
+ int mCompositableCount;
+};
+
+/// Equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
+/// ReleaseCompositableRef in addition to the usual AddRef and Release.
+///
+/// The semantoics of these CompositableTextureRefs are important because they
+/// are used both as a synchronization/safety mechanism, and as an optimization
+/// mechanism. They are also tricky and subtle because we use them in a very
+/// implicit way (assigning to a CompositableTextureRef is less visible than
+/// explicitly calling a method or whatnot).
+/// It is Therefore important to be careful about the way we use this tool.
+///
+/// CompositableTextureRef is a mechanism that lets us count how many compositables
+/// are using a given texture (for TextureSource and TextureHost).
+/// We use it to run specific code when a texture is not used anymore, and also
+/// we trigger fast paths on some operations when we can see that the texture's
+/// CompositableTextureRef counter is equal to 1 (the texture is not shared
+/// between compositables).
+/// This means that it is important to observe the following rules:
+/// * CompositableHosts that receive UseTexture and similar messages *must* store
+/// all of the TextureHosts they receive in CompositableTextureRef slots for as
+/// long as they may be using them.
+/// * CompositableHosts must store each texture in a *single* CompositableTextureRef
+/// slot to ensure that the counter properly reflects how many compositables are
+/// using the texture.
+/// If a compositable needs to hold two references to a given texture (for example
+/// to have a pointer to the current texture in a list of textures that may be
+/// used), it can hold its extra references with RefPtr or whichever pointer type
+/// makes sense.
+template<typename T>
+class CompositableTextureRef {
+public:
+ CompositableTextureRef() {}
+
+ explicit CompositableTextureRef(const CompositableTextureRef& aOther)
+ {
+ *this = aOther;
+ }
+
+ explicit CompositableTextureRef(T* aOther)
+ {
+ *this = aOther;
+ }
+
+ ~CompositableTextureRef()
+ {
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ }
+
+ CompositableTextureRef& operator=(const CompositableTextureRef& aOther)
+ {
+ if (aOther.get()) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther.get();
+ return *this;
+ }
+
+ CompositableTextureRef& operator=(T* aOther)
+ {
+ if (aOther) {
+ aOther->AddCompositableRef();
+ }
+ if (mRef) {
+ mRef->ReleaseCompositableRef();
+ }
+ mRef = aOther;
+ return *this;
+ }
+
+ T* get() const { return mRef; }
+ operator T*() const { return mRef; }
+ T* operator->() const { return mRef; }
+ T& operator*() const { return *mRef; }
+
+private:
+ RefPtr<T> mRef;
+};
+
+typedef CompositableTextureRef<TextureSource> CompositableTextureSourceRef;
+typedef CompositableTextureRef<TextureHost> CompositableTextureHostRef;
+
+/**
+ * Interface for TextureSources that can be updated from a DataSourceSurface.
+ *
+ * All backend should implement at least one DataTextureSource.
+ */
+class DataTextureSource : public TextureSource
+{
+public:
+ DataTextureSource()
+ : mOwner(0)
+ , mUpdateSerial(0)
+ {}
+
+ virtual const char* Name() const override { return "DataTextureSource"; }
+
+ virtual DataTextureSource* AsDataTextureSource() override { return this; }
+
+ /**
+ * Upload a (portion of) surface to the TextureSource.
+ *
+ * The DataTextureSource doesn't own aSurface, although it owns and manage
+ * the device texture it uploads to internally.
+ */
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) = 0;
+
+ /**
+ * A facility to avoid reuploading when it is not necessary.
+ * The caller of Update can use GetUpdateSerial to see if the number has changed
+ * since last update, and call SetUpdateSerial after each successful update.
+ * The caller is responsible for managing the update serial except when the
+ * texture data is deallocated in which case the TextureSource should always
+ * reset the update serial to zero.
+ */
+ uint32_t GetUpdateSerial() const { return mUpdateSerial; }
+ void SetUpdateSerial(uint32_t aValue) { mUpdateSerial = aValue; }
+
+ // By default at least set the update serial to zero.
+ // overloaded versions should do that too.
+ virtual void DeallocateDeviceData() override
+ {
+ SetUpdateSerial(0);
+ }
+
+#ifdef DEBUG
+ /**
+ * Provide read access to the data as a DataSourceSurface.
+ *
+ * This is expected to be very slow and should be used for mostly debugging.
+ * XXX - implement everywhere and make it pure virtual.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() { return nullptr; };
+#endif
+
+ void SetOwner(TextureHost* aOwner)
+ {
+ auto newOwner = (uintptr_t)aOwner;
+ if (newOwner != mOwner) {
+ mOwner = newOwner;
+ SetUpdateSerial(0);
+ }
+ }
+
+ bool IsOwnedBy(TextureHost* aOwner) const { return mOwner == (uintptr_t)aOwner; }
+
+ bool HasOwner() const { return !IsOwnedBy(nullptr); }
+
+private:
+ // We store mOwner as an integer rather than as a pointer to make it clear
+ // it is not intended to be dereferenced.
+ uintptr_t mOwner;
+ uint32_t mUpdateSerial;
+};
+
+/**
+ * TextureHost is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * compositor-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureClient lives on the content-side.
+ *
+ * TextureHost only knows how to deserialize or synchronize generic image data
+ * (SurfaceDescriptor) and provide access to one or more TextureSource objects
+ * (these provide the necessary APIs for compositor backends to composite the
+ * image).
+ *
+ * A TextureHost implementation corresponds to one SurfaceDescriptor type, as
+ * opposed to TextureSource that corresponds to device textures.
+ * This means that for YCbCr planes, even though they are represented as
+ * 3 textures internally (3 TextureSources), we use 1 TextureHost and not 3,
+ * because the 3 planes are stored in the same buffer of shared memory, before
+ * they are uploaded separately.
+ *
+ * There is always one and only one TextureHost per TextureClient, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * reponsibility of the compositable (which would use two Texture pairs).
+ *
+ * The Lock/Unlock mecanism here mirrors Lock/Unlock in TextureClient.
+ *
+ */
+class TextureHost
+ : public AtomicRefCountedWithFinalize<TextureHost>
+{
+ /**
+ * Called once, just before the destructor.
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize();
+
+ friend class AtomicRefCountedWithFinalize<TextureHost>;
+public:
+ explicit TextureHost(TextureFlags aFlags);
+
+protected:
+ virtual ~TextureHost();
+
+public:
+ /**
+ * Factory method.
+ */
+ static already_AddRefed<TextureHost> Create(
+ const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags);
+
+ /**
+ * Lock the texture host for compositing.
+ */
+ virtual bool Lock() { return true; }
+ /**
+ * Unlock the texture host after compositing. Lock() and Unlock() should be
+ * called in pair.
+ */
+ virtual void Unlock() {}
+
+ /**
+ * Lock the texture host for compositing without using compositor.
+ */
+ virtual bool LockWithoutCompositor() { return true; }
+ /**
+ * Similar to Unlock(), but it should be called with LockWithoutCompositor().
+ */
+ virtual void UnlockWithoutCompositor() {}
+
+ /**
+ * Note that the texture host format can be different from its corresponding
+ * texture source's. For example a ShmemTextureHost can have the ycbcr
+ * format and produce 3 "alpha" textures sources.
+ */
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+ /**
+ * Return the format used for reading the texture.
+ * Apple's YCBCR_422 is R8G8B8X8.
+ */
+ virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); }
+
+ virtual YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; }
+
+ /**
+ * Called during the transaction. The TextureSource may or may not be composited.
+ *
+ * Note that this is called outside of lock/unlock.
+ */
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
+
+ /**
+ * Called at composition time, just before compositing the TextureSource composited.
+ *
+ * Note that this is called only withing lock/unlock.
+ */
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) = 0;
+
+ /**
+ * Called when another TextureHost will take over.
+ */
+ virtual void UnbindTextureSource();
+
+ /**
+ * Is called before compositing if the shared data has changed since last
+ * composition.
+ * This method should be overload in cases like when we need to do a texture
+ * upload for example.
+ *
+ * @param aRegion The region that has been changed, if nil, it means that the
+ * entire surface should be updated.
+ */
+ void Updated(const nsIntRegion* aRegion = nullptr);
+
+ /**
+ * Sets this TextureHost's compositor.
+ * A TextureHost can change compositor on certain occasions, in particular if
+ * it belongs to an async Compositable.
+ * aCompositor can be null, in which case the TextureHost must cleanup all
+ * of it's device textures.
+ */
+ virtual void SetCompositor(Compositor* aCompositor) {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is associated
+ * with the rendering backend, such as GL textures.
+ */
+ virtual void DeallocateDeviceData() {}
+
+ /**
+ * Should be overridden in order to deallocate the data that is shared with
+ * the content side, such as shared memory.
+ */
+ virtual void DeallocateSharedData() {}
+
+ /**
+ * Should be overridden in order to force the TextureHost to drop all references
+ * to it's shared data.
+ *
+ * This is important to ensure the correctness of the deallocation protocol.
+ */
+ virtual void ForgetSharedData() {}
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ /**
+ * Should be overridden if TextureHost supports crop rect.
+ */
+ virtual void SetCropRect(nsIntRect aCropRect) {}
+
+ /**
+ * Debug facility.
+ * XXX - cool kids use Moz2D. See bug 882113.
+ */
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() = 0;
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void SetFlags(TextureFlags aFlags) { mFlags = aFlags; }
+
+ /**
+ * XXX - Flags should only be set at creation time, this will be removed.
+ */
+ void AddFlag(TextureFlags aFlag) { mFlags |= aFlag; }
+
+ TextureFlags GetFlags() { return mFlags; }
+
+ /**
+ * Allocate and deallocate a TextureParent actor.
+ *
+ * TextureParent< is an implementation detail of TextureHost that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPTextureParent and DeallocPTextureParent).
+ */
+ static PTextureParent* CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial);
+ static bool DestroyIPDLActor(PTextureParent* actor);
+
+ /**
+ * Destroy the TextureChild/Parent pair.
+ */
+ static bool SendDeleteIPDLActor(PTextureParent* actor);
+
+ static void ReceivedDestroy(PTextureParent* actor);
+
+ /**
+ * Get the TextureHost corresponding to the actor passed in parameter.
+ */
+ static TextureHost* AsTextureHost(PTextureParent* actor);
+
+ static uint64_t GetTextureSerial(PTextureParent* actor);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureParent* GetIPDLActor();
+
+ /**
+ * Specific to B2G's Composer2D
+ * XXX - more doc here
+ */
+ virtual LayerRenderState GetRenderState()
+ {
+ // By default we return an empty render state, this should be overridden
+ // by the TextureHost implementations that are used on B2G with Composer2D
+ return LayerRenderState();
+ }
+
+ // If a texture host holds a reference to shmem, it should override this method
+ // to forget about the shmem _without_ releasing it.
+ virtual void OnShutdown() {}
+
+ // Forget buffer actor. Used only for hacky fix for bug 966446.
+ virtual void ForgetBufferActor() {}
+
+ virtual const char *Name() { return "TextureHost"; }
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Indicates whether the TextureHost implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureHost does not contend with locking the texture on the client side.
+ */
+ virtual bool HasIntermediateBuffer() const { return false; }
+
+ void AddCompositableRef() { ++mCompositableCount; }
+
+ void ReleaseCompositableRef()
+ {
+ --mCompositableCount;
+ MOZ_ASSERT(mCompositableCount >= 0);
+ if (mCompositableCount == 0) {
+ UnbindTextureSource();
+ // Send mFwdTransactionId to client side if necessary.
+ NotifyNotUsed();
+ }
+ }
+
+ int NumCompositableRefs() const { return mCompositableCount; }
+
+ void SetLastFwdTransactionId(uint64_t aTransactionId);
+
+ void DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator);
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ virtual Compositor* GetCompositor() = 0;
+
+ virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
+
+protected:
+ void ReadUnlock();
+
+ void RecycleTexture(TextureFlags aFlags);
+
+ virtual void UpdatedInternal(const nsIntRegion *Region) {}
+
+ /**
+ * Called when mCompositableCount becomes 0.
+ */
+ void NotifyNotUsed();
+
+ // for Compositor.
+ void CallNotifyNotUsed();
+
+ PTextureParent* mActor;
+ RefPtr<TextureReadLock> mReadLock;
+ TextureFlags mFlags;
+ int mCompositableCount;
+ uint64_t mFwdTransactionId;
+
+ friend class Compositor;
+ friend class TextureParent;
+ friend class TiledLayerBufferComposite;
+};
+
+/**
+ * TextureHost that wraps a random access buffer such as a Shmem or some raw
+ * memory.
+ *
+ * This TextureHost is backend-independent and the backend-specific bits are
+ * in the TextureSource.
+ * This class must be inherited to implement GetBuffer and DeallocSharedData
+ * (see ShmemTextureHost and MemoryTextureHost)
+ *
+ * Uploads happen when Lock is called.
+ *
+ * BufferTextureHost supports YCbCr and flavours of RGBA images (RGBX, A, etc.).
+ */
+class BufferTextureHost : public TextureHost
+{
+public:
+ BufferTextureHost(const BufferDescriptor& aDescriptor, TextureFlags aFlags);
+
+ ~BufferTextureHost();
+
+ virtual uint8_t* GetBuffer() = 0;
+
+ virtual size_t GetBufferSize() = 0;
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void UnbindTextureSource() override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ /**
+ * Return the format that is exposed to the compositor when calling
+ * BindTextureSource.
+ *
+ * If the shared format is YCbCr and the compositor does not support it,
+ * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
+ */
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual YUVColorSpace GetYUVColorSpace() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
+
+ virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
+
+ const BufferDescriptor& GetBufferDescriptor() const { return mDescriptor; }
+
+protected:
+ bool Upload(nsIntRegion *aRegion = nullptr);
+ bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+ bool EnsureWrappingTextureSource();
+
+ virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+
+ BufferDescriptor mDescriptor;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<DataTextureSource> mFirstSource;
+ nsIntRegion mMaybeUpdatedRegion;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ uint32_t mUpdateSerial;
+ bool mLocked;
+ bool mNeedsFullUpdate;
+ bool mHasIntermediateBuffer;
+
+ class DataTextureSourceYCbCrBasic;
+};
+
+/**
+ * TextureHost that wraps shared memory.
+ * the corresponding texture on the client side is ShmemTextureClient.
+ * This TextureHost is backend-independent.
+ */
+class ShmemTextureHost : public BufferTextureHost
+{
+public:
+ ShmemTextureHost(const mozilla::ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+protected:
+ ~ShmemTextureHost();
+
+public:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual const char *Name() override { return "ShmemTextureHost"; }
+
+ virtual void OnShutdown() override;
+
+protected:
+ UniquePtr<mozilla::ipc::Shmem> mShmem;
+ RefPtr<ISurfaceAllocator> mDeallocator;
+};
+
+/**
+ * TextureHost that wraps raw memory.
+ * The corresponding texture on the client side is MemoryTextureClient.
+ * Can obviously not be used in a cross process setup.
+ * This TextureHost is backend-independent.
+ */
+class MemoryTextureHost : public BufferTextureHost
+{
+public:
+ MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags);
+
+protected:
+ ~MemoryTextureHost();
+
+public:
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ virtual size_t GetBufferSize() override;
+
+ virtual const char *Name() override { return "MemoryTextureHost"; }
+
+protected:
+ uint8_t* mBuffer;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHost
+{
+public:
+ explicit AutoLockTextureHost(TextureHost* aTexture)
+ : mTexture(aTexture)
+ {
+ mLocked = mTexture ? mTexture->Lock() : false;
+ }
+
+ ~AutoLockTextureHost()
+ {
+ if (mTexture && mLocked) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+class MOZ_STACK_CLASS AutoLockTextureHostWithoutCompositor
+{
+public:
+ explicit AutoLockTextureHostWithoutCompositor(TextureHost* aTexture)
+ : mTexture(aTexture)
+ {
+ mLocked = mTexture ? mTexture->LockWithoutCompositor() : false;
+ }
+
+ ~AutoLockTextureHostWithoutCompositor()
+ {
+ if (mTexture && mLocked) {
+ mTexture->UnlockWithoutCompositor();
+ }
+ }
+
+ bool Failed() { return mTexture && !mLocked; }
+
+private:
+ RefPtr<TextureHost> mTexture;
+ bool mLocked;
+};
+
+/**
+ * This can be used as an offscreen rendering target by the compositor, and
+ * subsequently can be used as a source by the compositor.
+ */
+class CompositingRenderTarget: public TextureSource
+{
+public:
+
+ explicit CompositingRenderTarget(const gfx::IntPoint& aOrigin)
+ : mClearOnBind(false)
+ , mOrigin(aOrigin)
+ , mHasComplexProjection(false)
+ {}
+ virtual ~CompositingRenderTarget() {}
+
+ virtual const char* Name() const override { return "CompositingRenderTarget"; }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) { return nullptr; }
+#endif
+
+ /**
+ * Perform a clear when recycling a non opaque surface.
+ * The clear is deferred to when the render target is bound.
+ */
+ void ClearOnBind() {
+ mClearOnBind = true;
+ }
+
+ const gfx::IntPoint& GetOrigin() const { return mOrigin; }
+ gfx::IntRect GetRect() { return gfx::IntRect(GetOrigin(), GetSize()); }
+
+ /**
+ * If a Projection matrix is set, then it is used for rendering to
+ * this render target instead of generating one. If no explicit
+ * projection is set, Compositors are expected to generate an
+ * orthogonal maaping that maps 0..1 to the full size of the render
+ * target.
+ */
+ bool HasComplexProjection() const { return mHasComplexProjection; }
+ void ClearProjection() { mHasComplexProjection = false; }
+ void SetProjection(const gfx::Matrix4x4& aNewMatrix, bool aEnableDepthBuffer,
+ float aZNear, float aZFar)
+ {
+ mProjectionMatrix = aNewMatrix;
+ mEnableDepthBuffer = aEnableDepthBuffer;
+ mZNear = aZNear;
+ mZFar = aZFar;
+ mHasComplexProjection = true;
+ }
+ void GetProjection(gfx::Matrix4x4& aMatrix, bool& aEnableDepth, float& aZNear, float& aZFar)
+ {
+ MOZ_ASSERT(mHasComplexProjection);
+ aMatrix = mProjectionMatrix;
+ aEnableDepth = mEnableDepthBuffer;
+ aZNear = mZNear;
+ aZFar = mZFar;
+ }
+protected:
+ bool mClearOnBind;
+
+private:
+ gfx::IntPoint mOrigin;
+
+ gfx::Matrix4x4 mProjectionMatrix;
+ float mZNear, mZFar;
+ bool mHasComplexProjection;
+ bool mEnableDepthBuffer;
+};
+
+/**
+ * Creates a TextureHost that can be used with any of the existing backends
+ * Not all SurfaceDescriptor types are supported
+ */
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp
new file mode 100644
index 000000000..7458c7497
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TiledContentHost.h"
+#include "gfxPrefs.h" // for gfxPrefs
+#include "PaintedLayerComposite.h" // for PaintedLayerComposite
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/Compositor.h" // for Compositor
+//#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "nsAString.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsPoint.h" // for IntPoint
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "nsRect.h" // for IntRect
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+using namespace gfx;
+namespace layers {
+
+class Layer;
+
+float
+TileHost::GetFadeInOpacity(float aOpacity)
+{
+ TimeStamp now = TimeStamp::Now();
+ if (!gfxPrefs::LayerTileFadeInEnabled() ||
+ mFadeStart.IsNull() ||
+ now < mFadeStart)
+ {
+ return aOpacity;
+ }
+
+ float duration = gfxPrefs::LayerTileFadeInDuration();
+ float elapsed = (now - mFadeStart).ToMilliseconds();
+ if (elapsed > duration) {
+ mFadeStart = TimeStamp();
+ return aOpacity;
+ }
+ return aOpacity * (elapsed / duration);
+}
+
+TiledLayerBufferComposite::TiledLayerBufferComposite()
+ : mFrameResolution()
+{}
+
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+ Clear();
+}
+
+void
+TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ for (TileHost& tile : mRetainedTiles) {
+ if (tile.IsPlaceholderTile()) continue;
+ tile.mTextureHost->SetCompositor(aCompositor);
+ if (tile.mTextureHostOnWhite) {
+ tile.mTextureHostOnWhite->SetCompositor(aCompositor);
+ }
+ }
+}
+
+void
+TiledLayerBufferComposite::AddAnimationInvalidation(nsIntRegion& aRegion)
+{
+ // We need to invalidate rects where we have a tile that is in the
+ // process of fading in.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ if (!mRetainedTiles[i].mFadeStart.IsNull()) {
+ TileIntPoint position = mTiles.TilePosition(i);
+ IntPoint offset = GetTileOffset(position);
+ nsIntRegion tileRegion = IntRect(offset, GetScaledTileSize());
+ aRegion.OrWith(tileRegion);
+ }
+ }
+}
+
+TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
+ : ContentHost(aTextureInfo)
+ , mTiledBuffer(TiledLayerBufferComposite())
+ , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
+{
+ MOZ_COUNT_CTOR(TiledContentHost);
+}
+
+TiledContentHost::~TiledContentHost()
+{
+ MOZ_COUNT_DTOR(TiledContentHost);
+}
+
+already_AddRefed<TexturedEffect>
+TiledContentHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
+{
+ // If we can use hwc for this TiledContentHost, it implies that we have exactly
+ // one high precision tile. Please check TiledContentHost::GetRenderState() for
+ // all condition.
+ MOZ_ASSERT(mTiledBuffer.GetTileCount() == 1 && mLowPrecisionTiledBuffer.GetTileCount() == 0);
+ MOZ_ASSERT(mTiledBuffer.GetTile(0).mTextureHost);
+
+ TileHost& tile = mTiledBuffer.GetTile(0);
+ if (!tile.mTextureHost->BindTextureSource(tile.mTextureSource)) {
+ return nullptr;
+ }
+
+ return CreateTexturedEffect(tile.mTextureSource,
+ nullptr,
+ aSamplingFilter,
+ true,
+ tile.mTextureHost->GetRenderState());
+}
+
+void
+TiledContentHost::Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags /* = NO_FLAGS */)
+{
+ CompositableHost::Attach(aLayer, aCompositor, aFlags);
+}
+
+void
+TiledContentHost::Detach(Layer* aLayer,
+ AttachFlags aFlags /* = NO_FLAGS */)
+{
+ if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
+ // Clear the TiledLayerBuffers, which will take care of releasing the
+ // copy-on-write locks.
+ mTiledBuffer.Clear();
+ mLowPrecisionTiledBuffer.Clear();
+ }
+ CompositableHost::Detach(aLayer,aFlags);
+}
+
+bool
+TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor)
+{
+ if (aTiledDescriptor.resolution() < 1) {
+ if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ } else {
+ if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+ CompositableTextureSourceRef& aTextureSource,
+ const IntRect& aUpdateRect,
+ Compositor* aCompositor)
+{
+ MOZ_ASSERT(aTexture);
+ if (!aTexture) {
+ return;
+ }
+
+ if (aCompositor) {
+ aTexture->SetCompositor(aCompositor);
+ }
+
+ if (!aUpdateRect.IsEmpty()) {
+ // For !HasIntermediateBuffer() textures, this is likely a no-op.
+ nsIntRegion region = aUpdateRect;
+ aTexture->Updated(&region);
+ }
+
+ aTexture->PrepareTextureSource(aTextureSource);
+}
+
+class TextureSourceRecycler
+{
+public:
+ explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet)
+ : mTiles(Move(aTileSet))
+ , mFirstPossibility(0)
+ {}
+
+ // Attempts to recycle a texture source that is already bound to the
+ // texture host for aTile.
+ void RecycleTextureSourceForTile(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ // Skip over existing tiles without a retained texture source
+ // and make sure we don't iterate them in the future.
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ // If this tile matches, then copy across the retained texture source (if
+ // any).
+ if (aTile.mTextureHost == mTiles[i].mTextureHost) {
+ aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ // Attempts to recycle any texture source to avoid needing to allocate
+ // a new one.
+ void RecycleTextureSource(TileHost& aTile) {
+ for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+ if (!mTiles[i].mTextureSource) {
+ if (i == mFirstPossibility) {
+ mFirstPossibility++;
+ }
+ continue;
+ }
+
+ if (mTiles[i].mTextureSource &&
+ mTiles[i].mTextureHost->GetFormat() == aTile.mTextureHost->GetFormat()) {
+ aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+ if (aTile.mTextureHostOnWhite) {
+ aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+ }
+ break;
+ }
+ }
+ }
+
+ void RecycleTileFading(TileHost& aTile) {
+ for (size_t i = 0; i < mTiles.Length(); i++) {
+ if (mTiles[i].mTextureHost == aTile.mTextureHost) {
+ aTile.mFadeStart = mTiles[i].mFadeStart;
+ }
+ }
+ }
+
+protected:
+ nsTArray<TileHost> mTiles;
+ size_t mFirstPossibility;
+};
+
+bool
+TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+ Compositor* aCompositor,
+ ISurfaceAllocator* aAllocator)
+{
+ if (mResolution != aTiles.resolution() ||
+ aTiles.tileSize() != mTileSize) {
+ Clear();
+ }
+ MOZ_ASSERT(aAllocator);
+ MOZ_ASSERT(aCompositor);
+ if (!aAllocator || !aCompositor) {
+ return false;
+ }
+
+ if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+ // There are divisions by mResolution so this protects the compositor process
+ // against malicious content processes and fuzzing.
+ return false;
+ }
+
+ TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
+ aTiles.retainedWidth(), aTiles.retainedHeight());
+
+ const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+ TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles));
+ mRetainedTiles.SetLength(tileDescriptors.Length());
+
+ // Step 1, deserialize the incoming set of tiles into mRetainedTiles, and attempt
+ // to recycle the TextureSource for any repeated tiles.
+ //
+ // Since we don't have any retained 'tile' object, we have to search for instances
+ // of the same TextureHost in the old tile set. The cost of binding a TextureHost
+ // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really
+ // high, so we avoid this whenever possible.
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+
+ TileHost& tile = mRetainedTiles[i];
+
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ NS_WARNING_ASSERTION(
+ tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor,
+ "Unrecognised tile descriptor type");
+ continue;
+ }
+
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+ tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent());
+ tile.mTextureHost->SetCompositor(aCompositor);
+ tile.mTextureHost->DeserializeReadLock(texturedDesc.sharedLock(), aAllocator);
+
+ if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+ tile.mTextureHostOnWhite = TextureHost::AsTextureHost(
+ texturedDesc.textureOnWhite().get_PTextureParent()
+ );
+ tile.mTextureHostOnWhite->DeserializeReadLock(
+ texturedDesc.sharedLockOnWhite(), aAllocator
+ );
+ }
+
+ tile.mTilePosition = newTiles.TilePosition(i);
+
+ // If this same tile texture existed in the old tile set then this will move the texture
+ // source into our new tile.
+ oldRetainedTiles.RecycleTextureSourceForTile(tile);
+
+ // If this tile is in the process of fading, we need to keep that going
+ oldRetainedTiles.RecycleTileFading(tile);
+
+ if (aTiles.isProgressive() &&
+ texturedDesc.wasPlaceholder())
+ {
+ // This is a progressive paint, and the tile used to be a placeholder.
+ // We need to begin fading it in (if enabled via layers.tiles.fade-in.enabled)
+ tile.mFadeStart = TimeStamp::Now();
+
+ aCompositor->CompositeUntil(tile.mFadeStart +
+ TimeDuration::FromMilliseconds(gfxPrefs::LayerTileFadeInDuration()));
+ }
+ }
+
+ // Step 2, attempt to recycle unused texture sources from the old tile set into new tiles.
+ //
+ // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way
+ // to ensure that any implicit locking on the old gralloc image is released.
+ for (TileHost& tile : mRetainedTiles) {
+ if (!tile.mTextureHost || tile.mTextureSource) {
+ continue;
+ }
+ oldRetainedTiles.RecycleTextureSource(tile);
+ }
+
+ // Step 3, handle the texture uploads, texture source binding and release the
+ // copy-on-write locks for textures with an internal buffer.
+ for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+ TileHost& tile = mRetainedTiles[i];
+ if (!tile.mTextureHost) {
+ continue;
+ }
+
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+ UseTileTexture(tile.mTextureHost,
+ tile.mTextureSource,
+ texturedDesc.updateRect(),
+ aCompositor);
+
+ if (tile.mTextureHostOnWhite) {
+ UseTileTexture(tile.mTextureHostOnWhite,
+ tile.mTextureSourceOnWhite,
+ texturedDesc.updateRect(),
+ aCompositor);
+ }
+ }
+
+ mTiles = newTiles;
+ mTileSize = aTiles.tileSize();
+ mTileOrigin = aTiles.tileOrigin();
+ mValidRegion = aTiles.validRegion();
+ mResolution = aTiles.resolution();
+ mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+ aTiles.frameYResolution());
+
+ return true;
+}
+
+void
+TiledLayerBufferComposite::Clear()
+{
+ mRetainedTiles.Clear();
+ mTiles.mFirst = TileIntPoint();
+ mTiles.mSize = TileIntSize();
+ mValidRegion = nsIntRegion();
+ mResolution = 1.0;
+}
+
+void
+TiledContentHost::Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion /* = nullptr */)
+{
+ MOZ_ASSERT(mCompositor);
+ // Reduce the opacity of the low-precision buffer to make it a
+ // little more subtle and less jarring. In particular, text
+ // rendered at low-resolution and scaled tends to look pretty
+ // heavy and this helps mitigate that. When we reduce the opacity
+ // we also make sure to draw the background color behind the
+ // reduced-opacity tile so that content underneath doesn't show
+ // through.
+ // However, in cases where the background is transparent, or the layer
+ // already has some opacity, we want to skip this behaviour. Otherwise
+ // we end up changing the expected overall transparency of the content,
+ // and it just looks wrong.
+ Color backgroundColor;
+ if (aOpacity == 1.0f && gfxPrefs::LowPrecisionOpacity() < 1.0f) {
+ // Background colors are only stored on scrollable layers. Grab
+ // the one from the nearest scrollable ancestor layer.
+ for (LayerMetricsWrapper ancestor(GetLayer(), LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
+ if (ancestor.Metrics().IsScrollable()) {
+ backgroundColor = ancestor.Metadata().GetBackgroundColor();
+ break;
+ }
+ }
+ }
+ float lowPrecisionOpacityReduction =
+ (aOpacity == 1.0f && backgroundColor.a == 1.0f)
+ ? gfxPrefs::LowPrecisionOpacity() : 1.0f;
+
+ nsIntRegion tmpRegion;
+ const nsIntRegion* renderRegion = aVisibleRegion;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (PaintWillResample()) {
+ // If we're resampling, then the texture image will contain exactly the
+ // entire visible region's bounds, and we should draw it all in one quad
+ // to avoid unexpected aliasing.
+ tmpRegion = aVisibleRegion->GetBounds();
+ renderRegion = &tmpRegion;
+ }
+#endif
+
+ // Render the low and high precision buffers.
+ RenderLayerBuffer(mLowPrecisionTiledBuffer,
+ lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
+ aEffectChain, lowPrecisionOpacityReduction * aOpacity,
+ aSamplingFilter, aClipRect, *renderRegion, aTransform);
+ RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
+ aClipRect, *renderRegion, aTransform);
+}
+
+
+void
+TiledContentHost::RenderTile(TileHost& aTile,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion,
+ const IntPoint& aTextureOffset,
+ const IntSize& aTextureBounds,
+ const gfx::Rect& aVisibleRect)
+{
+ MOZ_ASSERT(!aTile.IsPlaceholderTile());
+
+ AutoLockTextureHost autoLock(aTile.mTextureHost);
+ AutoLockTextureHost autoLockOnWhite(aTile.mTextureHostOnWhite);
+ if (autoLock.Failed() ||
+ autoLockOnWhite.Failed()) {
+ NS_WARNING("Failed to lock tile");
+ return;
+ }
+
+ if (!aTile.mTextureHost->BindTextureSource(aTile.mTextureSource)) {
+ return;
+ }
+
+ if (aTile.mTextureHostOnWhite && !aTile.mTextureHostOnWhite->BindTextureSource(aTile.mTextureSourceOnWhite)) {
+ return;
+ }
+
+ RefPtr<TexturedEffect> effect =
+ CreateTexturedEffect(aTile.mTextureSource,
+ aTile.mTextureSourceOnWhite,
+ aSamplingFilter,
+ true,
+ aTile.mTextureHost->GetRenderState());
+ if (!effect) {
+ return;
+ }
+
+ float opacity = aTile.GetFadeInOpacity(aOpacity);
+ aEffectChain.mPrimaryEffect = effect;
+
+ for (auto iter = aScreenRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
+ Rect textureRect(rect.x - aTextureOffset.x, rect.y - aTextureOffset.y,
+ rect.width, rect.height);
+
+ effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
+ textureRect.y / aTextureBounds.height,
+ textureRect.width / aTextureBounds.width,
+ textureRect.height / aTextureBounds.height);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
+ }
+ DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
+ if (aTile.mTextureHostOnWhite) {
+ flags |= DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ mCompositor->DrawDiagnostics(flags,
+ aScreenRegion, aClipRect, aTransform, mFlashCounter);
+}
+
+void
+TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aVisibleRegion,
+ gfx::Matrix4x4 aTransform)
+{
+ if (!mCompositor) {
+ NS_WARNING("Can't render tiled content host - no compositor");
+ return;
+ }
+ float resolution = aLayerBuffer.GetResolution();
+ gfx::Size layerScale(1, 1);
+
+ // We assume that the current frame resolution is the one used in our high
+ // precision layer buffer. Compensate for a changing frame resolution when
+ // rendering the low precision buffer.
+ if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
+ const CSSToParentLayerScale2D& layerResolution = aLayerBuffer.GetFrameResolution();
+ const CSSToParentLayerScale2D& localResolution = mTiledBuffer.GetFrameResolution();
+ layerScale.width = layerResolution.xScale / localResolution.xScale;
+ layerScale.height = layerResolution.yScale / localResolution.yScale;
+ aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure we don't render at low resolution where we have valid high
+ // resolution content, to avoid overdraw and artifacts with semi-transparent
+ // layers.
+ nsIntRegion maskRegion;
+ if (resolution != mTiledBuffer.GetResolution()) {
+ maskRegion = mTiledBuffer.GetValidRegion();
+ // XXX This should be ScaleRoundIn, but there is no such function on
+ // nsIntRegion.
+ maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
+ }
+
+ // Make sure the resolution and difference in frame resolution are accounted
+ // for in the layer transform.
+ aTransform.PreScale(1/(resolution * layerScale.width),
+ 1/(resolution * layerScale.height), 1);
+
+ DiagnosticFlags componentAlphaDiagnostic = DiagnosticFlags::NO_DIAGNOSTIC;
+
+ nsIntRegion compositeRegion = aLayerBuffer.GetValidRegion();
+ compositeRegion.AndWith(aVisibleRegion);
+ compositeRegion.SubOut(maskRegion);
+
+ IntRect visibleRect = aVisibleRegion.GetBounds();
+
+ if (compositeRegion.IsEmpty()) {
+ return;
+ }
+
+ if (aBackgroundColor) {
+ nsIntRegion backgroundRegion = compositeRegion;
+ backgroundRegion.ScaleRoundOut(resolution, resolution);
+ EffectChain effect;
+ effect.mPrimaryEffect = new EffectSolidColor(*aBackgroundColor);
+ for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
+ mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
+ }
+ }
+
+ for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
+ TileHost& tile = aLayerBuffer.GetTile(i);
+ if (tile.IsPlaceholderTile()) {
+ continue;
+ }
+
+ TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i);
+ // A sanity check that catches a lot of mistakes.
+ MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y);
+
+ IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
+ tileDrawRegion.AndWith(compositeRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ tileDrawRegion.ScaleRoundOut(resolution, resolution);
+ RenderTile(tile, aEffectChain, aOpacity,
+ aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
+ tileOffset * resolution, aLayerBuffer.GetTileSize(),
+ gfx::Rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height));
+ if (tile.mTextureHostOnWhite) {
+ componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
+ }
+ }
+
+ gfx::Rect rect(visibleRect.x, visibleRect.y,
+ visibleRect.width, visibleRect.height);
+ GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTENT | componentAlphaDiagnostic,
+ rect, aClipRect, aTransform, mFlashCounter);
+}
+
+void
+TiledContentHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get();
+
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+void
+TiledContentHost::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml)
+{
+ mTiledBuffer.Dump(aStream, aPrefix, aDumpHtml,
+ TextureDumpMode::DoNotCompress /* compression not supported on host side */);
+}
+
+void
+TiledContentHost::AddAnimationInvalidation(nsIntRegion& aRegion)
+{
+ return mTiledBuffer.AddAnimationInvalidation(aRegion);
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h
new file mode 100644
index 000000000..4b52394de
--- /dev/null
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TILEDCONTENTHOST_H
+#define GFX_TILEDCONTENTHOST_H
+
+#include <stdint.h> // for uint16_t
+#include <stdio.h> // for FILE
+#include <algorithm> // for swap
+#include "ContentHost.h" // for ContentHost
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer, etc
+#include "CompositableHost.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h" // for SamplingFilter
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsACString
+
+namespace mozilla {
+
+namespace layers {
+
+class Compositor;
+class ISurfaceAllocator;
+class Layer;
+class ThebesBufferData;
+class TextureReadLock;
+struct EffectChain;
+
+
+class TileHost {
+public:
+ // Constructs a placeholder TileHost. See the comments above
+ // TiledLayerBuffer for more information on what this is used for;
+ // essentially, this is a sentinel used to represent an invalid or blank
+ // tile.
+ TileHost()
+ {}
+
+ // Constructs a TileHost from a TextureReadLock and TextureHost.
+ TileHost(TextureReadLock* aSharedLock,
+ TextureHost* aTextureHost,
+ TextureHost* aTextureHostOnWhite,
+ TextureSource* aSource,
+ TextureSource* aSourceOnWhite)
+ : mTextureHost(aTextureHost)
+ , mTextureHostOnWhite(aTextureHostOnWhite)
+ , mTextureSource(aSource)
+ , mTextureSourceOnWhite(aSourceOnWhite)
+ {}
+
+ TileHost(const TileHost& o) {
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTilePosition = o.mTilePosition;
+ }
+ TileHost& operator=(const TileHost& o) {
+ if (this == &o) {
+ return *this;
+ }
+ mTextureHost = o.mTextureHost;
+ mTextureHostOnWhite = o.mTextureHostOnWhite;
+ mTextureSource = o.mTextureSource;
+ mTextureSourceOnWhite = o.mTextureSourceOnWhite;
+ mTilePosition = o.mTilePosition;
+ return *this;
+ }
+
+ bool operator== (const TileHost& o) const {
+ return mTextureHost == o.mTextureHost;
+ }
+ bool operator!= (const TileHost& o) const {
+ return mTextureHost != o.mTextureHost;
+ }
+
+ bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
+
+ void Dump(std::stringstream& aStream) {
+ aStream << "TileHost(...)"; // fill in as needed
+ }
+
+ void DumpTexture(std::stringstream& aStream, TextureDumpMode /* aCompress, ignored for host tiles */) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ CompositableHost::DumpTextureHost(aStream, mTextureHost);
+ }
+
+ /**
+ * This does a linear tween of the passed opacity (which is assumed
+ * to be between 0.0 and 1.0). The duration of the fade is controlled
+ * by the 'layers.tiles.fade-in.duration-ms' preference. It is enabled
+ * via 'layers.tiles.fade-in.enabled'
+ */
+ float GetFadeInOpacity(float aOpacity);
+
+ CompositableTextureHostRef mTextureHost;
+ CompositableTextureHostRef mTextureHostOnWhite;
+ mutable CompositableTextureSourceRef mTextureSource;
+ mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+ // This is not strictly necessary but makes debugging whole lot easier.
+ TileIntPoint mTilePosition;
+ TimeStamp mFadeStart;
+};
+
+class TiledLayerBufferComposite
+ : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
+{
+ friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
+
+public:
+ TiledLayerBufferComposite();
+ ~TiledLayerBufferComposite();
+
+ bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+ Compositor* aCompositor,
+ ISurfaceAllocator* aAllocator);
+
+ void Clear();
+
+ TileHost GetPlaceholderTile() const { return TileHost(); }
+
+ // Stores the absolute resolution of the containing frame, calculated
+ // by the sum of the resolutions of all parent layers' FrameMetrics.
+ const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
+
+ void SetCompositor(Compositor* aCompositor);
+
+ void AddAnimationInvalidation(nsIntRegion& aRegion);
+protected:
+
+ CSSToParentLayerScale2D mFrameResolution;
+};
+
+/**
+ * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
+ * flakes, we have a unique update process. All the textures that back the
+ * tiles are added in the usual way, but Updated is called on the host side
+ * in response to a message that describes the transaction for every tile.
+ * Composition happens in the normal way.
+ *
+ * TiledContentHost has a TiledLayerBufferComposite which keeps hold of the tiles.
+ * Each tile has a reference to a texture host. During the layers transaction, we
+ * receive a list of descriptors for the client-side tile buffer tiles
+ * (UseTiledLayerBuffer). If we receive two transactions before a composition,
+ * we immediately unlock and discard the unused buffer.
+ *
+ * When the content host is composited, we first validate the TiledLayerBuffer
+ * (Upload), which calls Updated on each tile's texture host to make sure the
+ * texture data has been uploaded. For single-buffered tiles, we unlock at this
+ * point, for double-buffered tiles we unlock and discard the last composited
+ * buffer after compositing a new one. Rendering takes us to RenderTile which
+ * is similar to Composite for non-tiled ContentHosts.
+ */
+class TiledContentHost : public ContentHost
+{
+public:
+ explicit TiledContentHost(const TextureInfo& aTextureInfo);
+
+protected:
+ ~TiledContentHost();
+
+public:
+ virtual LayerRenderState GetRenderState() override
+ {
+ // If we have exactly one high precision tile, then we can support hwc.
+ if (mTiledBuffer.GetTileCount() == 1 &&
+ mLowPrecisionTiledBuffer.GetTileCount() == 0) {
+ TextureHost* host = mTiledBuffer.GetTile(0).mTextureHost;
+ if (host) {
+ MOZ_ASSERT(!mTiledBuffer.GetTile(0).mTextureHostOnWhite, "Component alpha not supported!");
+
+ gfx::IntPoint offset = mTiledBuffer.GetTileOffset(mTiledBuffer.GetPlacement().TilePosition(0));
+
+ // Don't try to use HWC if the content doesn't start at the top-left of the tile.
+ if (offset != GetValidRegion().GetBounds().TopLeft()) {
+ return LayerRenderState();
+ }
+
+ LayerRenderState state = host->GetRenderState();
+ state.SetOffset(offset);
+ return state;
+ }
+ }
+ return LayerRenderState();
+ }
+
+ // Generate effect for layerscope when using hwc.
+ virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool UpdateThebes(const ThebesBufferData& aData,
+ const nsIntRegion& aUpdated,
+ const nsIntRegion& aOldValidRegionBack,
+ nsIntRegion* aUpdatedRegionBack) override
+ {
+ NS_ERROR("N/A for tiled layers");
+ return false;
+ }
+
+ const nsIntRegion& GetValidLowPrecisionRegion() const
+ {
+ return mLowPrecisionTiledBuffer.GetValidRegion();
+ }
+
+ const nsIntRegion& GetValidRegion() const
+ {
+ return mTiledBuffer.GetValidRegion();
+ }
+
+ virtual void SetCompositor(Compositor* aCompositor) override
+ {
+ MOZ_ASSERT(aCompositor);
+ CompositableHost::SetCompositor(aCompositor);
+ mTiledBuffer.SetCompositor(aCompositor);
+ mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+ }
+
+ bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+ const SurfaceDescriptorTiles& aTiledDescriptor);
+
+ virtual void Composite(LayerComposite* aLayer,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion* aVisibleRegion = nullptr) override;
+
+ virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
+
+ virtual TiledContentHost* AsTiledContentHost() override { return this; }
+
+ virtual void Attach(Layer* aLayer,
+ Compositor* aCompositor,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Detach(Layer* aLayer = nullptr,
+ AttachFlags aFlags = NO_FLAGS) override;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false) override;
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ virtual void AddAnimationInvalidation(nsIntRegion& aRegion) override;
+
+private:
+
+ void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
+ const gfx::Color* aBackgroundColor,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ nsIntRegion aMaskRegion,
+ gfx::Matrix4x4 aTransform);
+
+ // Renders a single given tile.
+ void RenderTile(TileHost& aTile,
+ EffectChain& aEffectChain,
+ float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::SamplingFilter aSamplingFilter,
+ const gfx::IntRect& aClipRect,
+ const nsIntRegion& aScreenRegion,
+ const gfx::IntPoint& aTextureOffset,
+ const gfx::IntSize& aTextureBounds,
+ const gfx::Rect& aVisibleRect);
+
+ void EnsureTileStore() {}
+
+ TiledLayerBufferComposite mTiledBuffer;
+ TiledLayerBufferComposite mLowPrecisionTiledBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/composite/X11TextureHost.cpp b/gfx/layers/composite/X11TextureHost.cpp
new file mode 100644
index 000000000..7ca42426d
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "X11TextureHost.h"
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#ifdef GL_PROVIDER_GLX
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/X11TextureSourceOGL.h"
+#endif
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureHost::X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor)
+ : TextureHost(aFlags)
+{
+ RefPtr<gfxXlibSurface> surface = aDescriptor.OpenForeign();
+ mSurface = surface.get();
+
+ if (!(aFlags & TextureFlags::DEALLOCATE_CLIENT)) {
+ mSurface->TakePixmap();
+ }
+}
+
+bool
+X11TextureHost::Lock()
+{
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ switch (mCompositor->GetBackendType()) {
+ case LayersBackend::LAYERS_BASIC:
+ mTextureSource =
+ new X11TextureSourceBasic(mCompositor->AsBasicCompositor(), mSurface);
+ break;
+#ifdef GL_PROVIDER_GLX
+ case LayersBackend::LAYERS_OPENGL:
+ mTextureSource =
+ new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface);
+ break;
+#endif
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+X11TextureHost::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+SurfaceFormat
+X11TextureHost::GetFormat() const
+{
+ gfxContentType type = mSurface->GetContentType();
+#ifdef GL_PROVIDER_GLX
+ if (mCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+ }
+#endif
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+IntSize
+X11TextureHost::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+X11TextureHost::GetAsSurface()
+{
+ if (!mTextureSource || !mTextureSource->AsSourceBasic()) {
+ return nullptr;
+ }
+ RefPtr<DrawTarget> tempDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ GetSize(), GetFormat());
+ if (!tempDT) {
+ return nullptr;
+ }
+ RefPtr<SourceSurface> surf = mTextureSource->AsSourceBasic()->GetSurface(tempDT);
+ if (!surf) {
+ return nullptr;
+ }
+ return surf->GetDataSurface();
+}
+
+}
+}
diff --git a/gfx/layers/composite/X11TextureHost.h b/gfx/layers/composite/X11TextureHost.h
new file mode 100644
index 000000000..1f1d34409
--- /dev/null
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTUREHOST__H
+#define MOZILLA_GFX_X11TEXTUREHOST__H
+
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/gfx/Types.h"
+
+#include "gfxXlibSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureSource : public TextureSource
+{
+public:
+ // Called when the underlying X surface has been changed.
+ // Useful for determining whether to rebind a GLXPixmap to a texture.
+ virtual void Updated() = 0;
+
+ virtual const char* Name() const override { return "X11TextureSource"; }
+};
+
+// TextureHost for Xlib-backed TextureSources.
+class X11TextureHost : public TextureHost
+{
+public:
+ X11TextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorX11& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "X11TextureHost"; }
+#endif
+
+protected:
+ virtual void UpdatedInternal(const nsIntRegion*) override
+ {
+ if (mTextureSource)
+ mTextureSource->Updated();
+ }
+
+ RefPtr<Compositor> mCompositor;
+ RefPtr<X11TextureSource> mTextureSource;
+ RefPtr<gfxXlibSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTUREHOST__H
diff --git a/gfx/layers/composite/qrcode_table.h b/gfx/layers/composite/qrcode_table.h
new file mode 100644
index 000000000..553be28c1
--- /dev/null
+++ b/gfx/layers/composite/qrcode_table.h
@@ -0,0 +1,259 @@
+const char * const sQRCodeTable[] = {
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB2\xC7\x11\x94\xE3\x6\x63\xA0\xF1\x74\xAA\x80\x78\xE3\xFA\x94\xD0\x43\xBA\xBA\x73\xFD\xD4\x83\xEE\x8A\x23\x5\x4D\x6F\xEF\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x20\xA5\x11\xB4\xF2\x22\x6A\x84\x62\xF0\xAB\x80\x5A\xAB\xFA\x5D\xF0\x53\xBA\xBA\x97\x6D\xD2\x11\xAE\xA2\x23\x5\x84\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB7\xE4\x11\xAB\xCB\x7\x9B\xA0\xF8\xB6\xAA\x0\x48\xE3\xFB\x94\xD0\x4F\xBA\xBA\x13\xFD\xD7\x83\xEE\x8A\x23\x5\xD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x25\x86\x11\x8B\xDA\x23\x92\x84\x6B\x32\xAB\x0\x6A\xAB\xFB\x5D\xF0\x5F\xBA\xBA\xF7\x6D\xD1\x11\xAE\xA2\x23\x5\xC4\x4F\xE8\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB1\xAD\x11\xAF\xC3\x7\xC8\x20\xFB\x6C\xAA\x80\x68\xEB\xFB\x14\xD0\x4F\xBA\xBA\x13\xFD\xD4\x83\xEE\x82\x23\x5\x4D\x6F\xE9\x8E\x0",
+ "\xFE\xFB\xFC\x15\x50\x6E\xA0\xBB\x75\xE5\xDB\xA0\xAE\xC1\x69\x7\xFA\xAF\xE0\xF\x0\xCE\x51\x7E\x69\xCA\xE2\xE4\xF9\x53\x4D\x4C\x7A\xE2\x80\x67\x17\xF8\xB0\x50\x5B\x28\xBA\xD3\xFD\xD0\xCA\xEE\x9C\xF9\x5\xCD\x6F\xEE\xE3\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x8E\x11\x82\xA2\x22\x79\x4\x62\xAE\xAA\x0\x4A\xA3\xFA\x5D\xF0\x53\xBA\xBA\xD7\x6D\xD3\x11\xAE\xA2\x23\x5\x44\x4F\xEA\xAA\x1",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x26\xEC\x11\xB0\xFA\x22\x39\x4\x61\x2A\xAB\x0\x7A\xA3\xFA\xDD\xF0\x53\xBA\xBA\x97\x6D\xD1\x11\xAE\xAA\x23\x5\xC4\x4F\xEE\xAA\x1",
+ "\xFE\x5B\xFC\x16\x90\x6E\xB2\xBB\x74\xA5\xDB\xA8\xAE\xC1\x4D\x7\xFA\xAF\xE0\x1F\x0\xD3\x63\xB6\x2A\x11\x81\x83\x7\x7A\xE0\xFE\xEC\xAA\x80\x58\xE7\xFA\x14\xD0\x43\xBA\xBA\x13\xFD\xD6\x83\xEE\x8A\x23\x5\xCD\x6F\xED\x8E\x0",
+ "\xFE\x5B\xFC\x13\x90\x6E\xB6\xBB\x74\xA5\xDB\xA2\xAE\xC1\x5\x7\xFA\xAF\xE0\x1B\x0\xEF\xF6\x24\x48\x11\xA1\x92\x23\x73\xC4\x6D\x68\xAB\x80\x7A\xAF\xFA\xDD\xF0\x53\xBA\xBA\xF7\x6D\xD0\x11\xAE\xA2\x23\x5\x4\x4F\xE8\xAA\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xB\xAA\x82\xDD\xCA\xAB\x3B\x86\xED\x73\x80\x71\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAE\xAA\xA5\xB5\xC9\xFB\x3B\x82\x23\x73\x80\x61\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x24\xAA\xBC\xD5\xCB\xFB\xFB\xB4\x69\x73\x80\x61\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x81\xAA\x89\xF4\xEC\xE2\xDF\x20\xA7\x73\x80\x63\x53\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6E\xAA\xA7\xC5\xCA\x1B\xBB\x94\xAF\x73\x80\x41\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCB\xAA\x80\xAD\xC9\x4B\xBB\x90\x61\x73\x80\x51\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x0\xAA\x94\xBD\xCA\xF3\x7B\xAC\x6D\x72\x0\x51\x1B\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA5\xAA\xB3\xD5\xC9\xA3\x7B\xA8\xA3\x72\x0\x41\x1B\xFB\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4A\xAA\x8F\xAD\xCB\x13\x3B\x8C\xAB\x72\x0\x71\x1B\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEF\xAA\xA8\xC5\xC8\x43\x3B\x88\x65\x72\x0\x61\x1B\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x65\xAA\xB1\xA5\xCA\x43\xFB\xBE\x2F\x72\x0\x61\x1B\xFB\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC0\xAA\x96\xCD\xC9\x13\xFB\xBA\xE1\x72\x0\x71\x1B\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2F\xAA\xAA\xB5\xCB\xA3\xBB\x9E\xE9\x72\x0\x41\x1B\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8A\xAA\x8D\xDD\xC8\xF3\xBB\x9A\x27\x72\x0\x51\x1B\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x62\xAA\xA6\xE5\xCA\xB3\x7B\xAF\xE9\x73\x0\x61\x1B\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xC7\xAA\x93\xC4\xED\xAA\x5F\x3B\x27\x73\x0\x63\x53\xFB\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x28\xAA\xBD\xF5\xCB\x53\x3B\x8F\x2F\x73\x0\x41\x1B\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8D\xAA\x9A\x9D\xC8\x3\x3B\x8B\xE1\x73\x0\x51\x1B\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x7\xAA\x83\xFD\xCA\x3\xFB\xBD\xAB\x73\x0\x51\x1B\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA2\xAA\xA4\x95\xC9\x53\xFB\xB9\x65\x73\x0\x41\x1B\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4D\xAA\x98\xED\xCB\xE3\xBB\x9D\x6D\x73\x0\x71\x1B\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE8\xAA\xBF\x85\xC8\xB3\xBB\x99\xA3\x73\x0\x61\x1B\xFB\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x90\xB5\xCA\xA0\xFB\xAF\xB7\x72\x80\x71\x13\xFA\x22\x10\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x50\xC4\x49\x39\xE5\x2A\x7A\xC3\x48\xF7\x4A\x0\x6F\x2B\xFA\x2C\x30\x4B\xC8\xBA\xE9\x25\xD7\x49\x2E\xB5\x39\x5\x83\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x8B\xA5\xCB\x40\xBB\x8F\x71\x72\x80\x51\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA6\xAA\xAC\xCD\xC8\x10\xBB\x8B\xBF\x72\x80\x41\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xB5\xAD\xCA\x10\x7B\xBD\xF5\x72\x80\x41\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x92\xC5\xC9\x40\x7B\xB9\x3B\x72\x80\x51\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xBC\xF4\xEF\xB9\x1F\xD\x33\x72\x80\x73\x5B\xFA\x6B\x30\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x89\xD5\xC8\xA0\x3B\x99\xFD\x72\x80\x71\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2B\xAA\xA2\xED\xCA\xE0\xFB\xAC\x33\x73\x80\x41\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x85\x85\xC9\xB0\xFB\xA8\xFD\x73\x80\x51\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x61\xAA\xAB\xB4\xEF\x49\x9F\x1C\xF5\x73\x80\x73\x5B\xFB\x6B\x30\x44\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC4\xAA\x9E\x95\xC8\x50\xBB\x88\x3B\x73\x80\x71\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4E\xAA\x87\xF5\xCA\x50\x7B\xBE\x71\x73\x80\x71\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xEB\xAA\xB2\xD4\xED\x49\x5F\x2A\xBF\x73\x80\x73\x5B\xFA\xEB\x30\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4\xAA\x8E\xAC\xEF\xF9\x1F\xE\xB7\x73\x80\x43\x5B\xFA\xEB\x30\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xA1\xAA\xA9\xC4\xEC\xA9\x1F\xA\x79\x73\x80\x53\x5B\xFB\x6B\x30\x48\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x6A\xAA\xAF\x9D\xCB\x58\xFB\xA6\x75\x72\x0\x41\x13\xFB\x22\x10\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xCF\xAA\x88\xF5\xC8\x8\xFB\xA2\xBB\x72\x0\x51\x13\xFA\xA2\x10\x54\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x20\xAA\xB4\x8D\xCA\xB8\xBB\x86\xB3\x72\x0\x61\x13\xFA\xA2\x10\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\xC\x6A\x55\x54\xF9\xB8\x5A\x60\xE7\x28\x27\x0\x5C\xA7\xF8\x3E\x70\x47\xB9\xBA\xFB\x6D\xD5\xC7\x2E\x88\x8F\x5\x8D\x5F\xEB\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xF\xAA\x8A\x85\xCB\xE8\x7B\xB4\x37\x72\x0\x71\x13\xFA\xA2\x10\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xAA\xAA\xAD\xED\xC8\xB8\x7B\xB0\xF9\x72\x0\x61\x13\xFB\x22\x10\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x7B\xFC\x12\x90\x6E\x90\xBB\x75\xE5\xDB\xAB\xAE\xC1\x15\x7\xFA\xAF\xE0\xF\x0\xC7\x48\xC0\xAA\x55\x5F\xAD\x29\xA2\x81\x71\xA4\x27\x0\x7F\xAB\xFA\xAC\x30\x5B\xB9\xBA\x9\x25\xD1\x41\x2E\x98\x8F\x5\x81\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xE0\xAA\xB6\xFD\xC9\x58\x3B\x90\x3F\x72\x0\x41\x13\xFA\xA2\x10\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8\xAA\x9D\xC5\xCB\x18\xFB\xA5\xF1\x73\x0\x71\x13\xFB\xA2\x10\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAD\xAA\xBA\xAD\xC8\x48\xFB\xA1\x3F\x73\x0\x61\x13\xFA\x22\x10\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x42\xAA\x86\xD5\xCA\xF8\xBB\x85\x37\x73\x0\x51\x13\xFA\x22\x10\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE7\xAA\xA1\xBD\xC9\xA8\xBB\x81\xF9\x73\x0\x41\x13\xFB\xA2\x10\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x6D\xAA\xAA\x94\xEF\xE1\x5F\x27\xB3\x73\x0\x53\x5B\xFA\x6B\x30\x48\x46\xBA\x6E\x3D\xD7\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xC8\xAA\x8D\xFC\xEC\xB1\x5F\x23\x7D\x73\x0\x43\x5B\xFB\xEB\x30\x48\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x27\xAA\xA3\xCD\xCA\x48\x3B\x97\x75\x73\x0\x61\x13\xFB\xA2\x10\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x82\xAA\x84\xA5\xC9\x18\x3B\x93\xBB\x73\x0\x71\x13\xFA\x22\x10\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xBE\xF5\xCA\x12\x3B\xAA\x37\x72\x80\x41\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x8B\xD4\xED\xB\x1F\x3E\xF9\x72\x80\x43\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xA5\xE5\xCB\xF2\x7B\x8A\xF1\x72\x80\x61\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x57\x9\x49\xC\xB5\x2B\x28\x43\x6D\xB1\x4A\x0\x7F\x27\xFA\xAC\x30\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\xC3\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x89\xA4\xEE\xEB\x9F\x28\x75\x72\x80\x63\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE\xAA\xAE\xCC\xED\xBB\x9F\x2C\xBB\x72\x80\x73\x57\xFB\x6B\x30\x48\x46\xBA\xE\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE1\xAA\x80\xFD\xCB\x42\xFB\x98\xB3\x72\x80\x51\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xA7\x95\xC8\x12\xFB\x9C\x7D\x72\x80\x41\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xAC\xAA\x8C\xAD\xCA\x52\x3B\xA9\xB3\x73\x80\x71\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xAB\xC5\xC9\x2\x3B\xAD\x7D\x73\x80\x61\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x97\xBD\xCB\xB2\x7B\x89\x75\x73\x80\x51\x1F\xFA\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xB0\xD5\xC8\xE2\x7B\x8D\xBB\x73\x80\x41\x1F\xFB\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC9\xAA\xA9\xB5\xCA\xE2\xBB\xBB\xF1\x73\x80\x41\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6C\xAA\x8E\xDD\xC9\xB2\xBB\xBF\x3F\x73\x80\x51\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xB2\xA5\xCB\x2\xFB\x9B\x37\x73\x80\x61\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x12\x90\x6E\x98\xBB\x74\xE5\xDB\xA2\xAE\xC1\xD\x7\xFA\xAF\xE0\x1F\x0\xDA\x6A\x8\xC9\x55\x52\xD1\xB9\xE0\x20\xFA\xAC\x26\x80\x5C\xAB\xF9\x3E\x70\x4B\xB9\xBA\xFB\x6D\xD7\xC7\x2E\x90\x8F\x5\xD\x5F\xE9\x7\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x81\xDD\xCB\xEA\x3B\xA3\xF5\x72\x0\x71\x1F\xFA\x22\x10\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x48\xAA\xA6\xB5\xC8\xBA\x3B\xA7\x3B\x72\x0\x61\x1F\xFB\xA2\x10\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x9A\xCD\xCA\xA\x7B\x83\x33\x72\x0\x51\x1F\xFB\xA2\x10\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2\xAA\xAF\xEC\xED\x13\x5F\x17\xFD\x72\x0\x53\x57\xFA\x6B\x30\x44\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\xF2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x88\xAA\xA4\xC5\xCB\x5A\xBB\xB1\xB7\x72\x0\x41\x1F\xFB\xA2\x10\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2D\xAA\x83\xAD\xC8\xA\xBB\xB5\x79\x72\x0\x51\x1F\xFA\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\xC2\xAA\xAD\x9C\xEE\xF3\xDF\x1\x71\x72\x0\x73\x57\xFA\x6B\x30\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x67\xAA\x8A\xF4\xED\xA3\xDF\x5\xBF\x72\x0\x63\x57\xFB\xEB\x30\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x8F\xAA\xA1\xCC\xEF\xE3\x1F\x30\x71\x73\x0\x53\x57\xFA\xEB\x30\x48\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x2A\xAA\x94\xED\xC8\xFA\x3B\xA4\xBF\x73\x0\x51\x1F\xFB\x22\x10\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xC5\xAA\xA8\x95\xCA\x4A\x7B\x80\xB7\x73\x0\x61\x1F\xFB\x22\x10\x54\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x60\xAA\x8F\xFD\xC9\x1A\x7B\x84\x79\x73\x0\x71\x1F\xFA\xA2\x10\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEA\xAA\x96\x9D\xCB\x1A\xBB\xB2\x33\x73\x0\x71\x1F\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x4F\xAA\xA3\xBC\xEC\x3\x9F\x26\xFD\x73\x0\x73\x57\xFA\xEB\x30\x44\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA0\xAA\x8D\x8D\xCA\xFA\xFB\x92\xF5\x73\x0\x51\x1F\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x5\xAA\xAA\xE5\xC9\xAA\xFB\x96\x3B\x73\x0\x41\x1F\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xA4\xAA\x85\xD5\xCB\xB9\xBB\xA0\x2F\x72\x80\x51\x17\xFA\xA2\x10\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x1\xAA\xA2\xBD\xC8\xE9\xBB\xA4\xE1\x72\x80\x41\x17\xFB\x22\x10\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xEE\xAA\x9E\xC5\xCA\x59\xFB\x80\xE9\x72\x80\x71\x17\xFB\x22\x10\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7F\xED\x71\xD4\x9B\x13\x9B\xB2\xA0\xB5\x3B\x80\x4C\xA3\xF9\xCF\xB0\x50\xD4\xBA\xAE\x3D\xD1\x71\xEE\x8B\x1\x5\xB2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x27\xAA\xA4\xCC\xEA\xB2\xDF\x2D\x23\x73\x80\x73\x53\xFB\xEB\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x64\xAA\x3D\xBD\xCE\x79\x3B\xB3\x79\x73\x80\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x4C\xAA\xF3\xED\xCE\x6A\xFB\xB0\x2F\x73\x80\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xF\xAA\x6A\x9C\xEA\xA1\x1F\x2E\x75\x73\x80\x43\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x85\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xED\xAA\x9F\xBD\xC9\x3B\x3B\xB5\x7B\x73\x80\x41\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xAE\xAA\x14\x85\xC9\xB9\xFB\xBB\x21\x73\x80\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x86\xAA\xDA\xD5\xC9\xAA\x3B\xB8\x77\x73\x80\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xC5\xAA\x51\xED\xC9\x28\xFB\xB6\x2D\x73\x80\x71\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x53\xAA\x49\x1F\xD5\x2E\x21\xC3\x5A\x63\x4B\x0\x7F\x23\xFB\xAC\x70\x4B\xC8\xBA\xE9\x25\xD4\x49\x2E\xBD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xC1\xAA\x1A\xD5\xCD\x29\x3B\xB7\xB7\x73\x80\x61\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xE9\xAA\xC6\xCC\xE9\x73\xDF\x24\xE1\x73\x80\x43\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x85\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xAA\xAA\x5F\xBD\xCD\xB8\x3B\xBA\xBB\x73\x80\x41\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2\xAA\xA3\xC5\xCB\x8B\x7B\x91\x73\x73\x80\x71\x1B\xFB\x22\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x41\xAA\x28\xFD\xCB\x9\xBB\x9F\x29\x73\x80\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x69\xAA\xE6\xAD\xCB\x1A\x7B\x9C\x7F\x73\x80\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x2A\xAA\x6D\x95\xCB\x98\xBB\x92\x25\x73\x80\x41\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x6D\xAA\xAD\x95\xCF\x1B\xBB\x9D\xE5\x73\x80\x41\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x2E\xAA\x26\xAD\xCF\x99\x7B\x93\xBF\x73\x80\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6\xAA\xE8\xFD\xCF\x8A\xBB\x90\xE9\x73\x80\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x45\xAA\x63\xC5\xCF\x8\x7B\x9E\xB3\x73\x80\x71\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xA7\xAA\x84\xAD\xC8\xDB\x7B\x95\xBD\x73\x80\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xE4\xAA\x1D\xDC\xEC\x10\x9F\xB\xE7\x73\x80\x63\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xCC\xAA\xC1\xC5\xC8\x4A\x7B\x98\xB1\x73\x80\x41\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x8F\xAA\x4A\xFD\xC8\xC8\xBB\x96\xEB\x73\x80\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xC8\xAA\x8A\xFD\xCC\x4B\xBB\x99\x2B\x73\x80\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x2D\x71\x6C\xF3\x16\x5B\x32\xB3\xE3\x3A\x80\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\xAE\x3D\xD2\x71\xEE\x8B\x1\x5\xF2\xAF\xED\x3F\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xA3\xAA\xCF\x95\xCC\xDA\xBB\x94\x27\x73\x80\x71\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xE0\xAA\x44\xAD\xCC\x58\x7B\x9A\x7D\x73\x80\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x6C\xAA\x82\xF4\xEF\x2A\x9F\x39\xB1\x72\x0\x73\x53\xFA\xEB\x70\x40\x46\xBA\x4E\x3D\xD5\x38\xEE\x8D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x2F\xAA\x9\xCC\xEF\xA8\x5F\x37\xEB\x72\x0\x63\x53\xFB\x6B\x70\x44\x46\xBA\x2E\x3D\xD7\x38\xEE\x85\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x7\xAA\xC7\x9C\xEF\xBB\x9F\x34\xBD\x72\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\x44\xAA\x4C\xA4\xEF\x39\x5F\x3A\xE7\x72\x0\x43\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x3\xAA\x9E\xED\xCF\xF3\x7B\xA5\x27\x72\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x40\xAA\x15\xD5\xCF\x71\xBB\xAB\x7D\x72\x0\x41\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x68\xAA\xDB\x85\xCF\x62\x7B\xA8\x2B\x72\x0\x71\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2B\xAA\x50\xBD\xCF\xE0\xBB\xA6\x71\x72\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC9\xAA\xA5\x9C\xEC\x7A\x9F\x3D\x7F\x72\x0\x63\x53\xFB\x6B\x70\x40\x46\xBA\xE\x3D\xD5\x38\xEE\x85\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8A\xAA\x3C\xED\xC8\xB1\x7B\xA3\x25\x72\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA2\xAA\xF2\xBD\xC8\xA2\xBB\xA0\x73\x72\x0\x51\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD2\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x8B\xFC\x16\x90\x6E\xBE\xBB\x75\x55\xDB\xA7\xAE\xC1\x55\x7\xFA\xAF\xE0\x0\x0\xCE\x9\x7E\x47\x71\x14\xB3\x12\xB2\x32\x8A\xBB\x3B\x0\x6C\xAF\xF9\x4F\xF0\x54\xD4\xBA\x8E\x3D\xD2\x71\xEE\x9B\x1\x5\x72\xAF\xEF\x3F\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA6\xAA\xAB\xCC\xE8\xEA\x5F\x31\xE9\x72\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE5\xAA\x32\xBD\xCC\x21\xBB\xAF\xB3\x72\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCD\xAA\xFC\xED\xCC\x32\x7B\xAC\xE5\x72\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xEA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x8E\xAA\x77\xD5\xCC\xB0\xBB\xA2\xBF\x72\x0\x71\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x26\xAA\x99\xE4\xEE\xCA\xDF\x19\x77\x72\x0\x53\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD7\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x65\xAA\x12\xDC\xEE\x48\x1F\x17\x2D\x72\x0\x43\x53\xFA\xEB\x70\x48\x46\xBA\x4E\x3D\xD5\x38\xEE\x95\xDB\x5\x72\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x4D\xAA\xCE\xC5\xCA\x12\xFB\x84\x7B\x72\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE\xAA\x45\xFD\xCA\x90\x3B\x8A\x21\x72\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x49\xAA\x85\xFD\xCE\x13\x3B\x85\xE1\x72\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA\xAA\xE\xC5\xCE\x91\xFB\x8B\xBB\x72\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x22\xAA\xD2\xDC\xEA\xCB\x1F\x18\xED\x72\x0\x43\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD5\x38\xEE\x8D\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x54\x49\x49\xC5\x95\x2D\x8A\xC3\x65\x39\x4A\x80\x4F\x23\xFA\xAC\x70\x47\xC8\xBA\xC9\x25\xD7\x49\x2E\xBD\x39\x5\x43\x6F\xEB\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x83\xAA\xAC\xC5\xC9\xD3\xFB\x8D\xB9\x72\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC0\xAA\x35\xB4\xED\x18\x1F\x13\xE3\x72\x0\x53\x53\xFB\x6B\x70\x48\x46\xBA\xE\x3D\xD5\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE8\xAA\xE9\xAD\xC9\x42\xFB\x80\xB5\x72\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x83\x49\xEC\xAD\x2A\x4A\x3\x6D\x61\x4A\x80\x6F\x23\xFA\x2C\x70\x4F\xC8\xBA\xA9\x25\xD6\x49\x2E\xA5\x39\x5\xC3\x6F\xE9\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEC\xAA\xA2\x95\xCD\x43\x3B\x81\x2F\x72\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x56\x87\x49\xA7\x95\x2E\x4B\xC3\x6C\xFB\x4A\x80\x7F\x23\xFB\xAC\x70\x43\xC8\xBA\xE9\x25\xD4\x49\x2E\xB5\x39\x5\x83\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x87\xAA\xE7\xFD\xCD\xD2\x3B\x8C\x23\x72\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC4\xAA\x6C\xC5\xCD\x50\xFB\x82\x79\x72\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x9\xAA\xB5\xA5\xCB\xD3\x3B\xBB\xF3\x72\x0\x51\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\x4A\xAA\x2C\xD4\xEF\x18\xDF\x25\xA9\x72\x0\x53\x53\xFA\xEB\x70\x48\x46\xBA\xE\x3D\xD6\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x62\xAA\xF0\xCD\xCB\x42\x3B\xB6\xFF\x72\x0\x71\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x51\x9\x49\xF5\xCD\x28\x4A\xC3\x5B\x2B\x4A\x80\x6F\x23\xFB\xAC\x70\x4F\xC8\xBA\xA9\x25\xD5\x49\x2E\xA5\x39\x5\x3\x6F\xED\x4E\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x66\xAA\xA9\xBC\xEB\xA\xDF\x27\x65\x72\x0\x73\x53\xFA\x6B\x70\x44\x46\xBA\x4E\x3D\xD5\x38\xEE\x85\xDB\x5\x32\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x25\xAA\x30\xCD\xCF\xC1\x3B\xB9\x3F\x72\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xD\xAA\xFE\x9D\xCF\xD2\xFB\xBA\x69\x72\x0\x41\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD2\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\x4E\xAA\x67\xEC\xEB\x19\x1F\x24\x33\x72\x0\x43\x53\xFB\x6B\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x8D\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB3\xAC\xAA\x80\x84\xEC\xCA\x1F\x2F\x3D\x72\x0\x53\x53\xFA\xEB\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xEF\xAA\xB\xBC\xEC\x48\xDF\x21\x67\x72\x0\x43\x53\xFB\x6B\x70\x48\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC7\xAA\xC5\xEC\xEC\x5B\x1F\x22\x31\x72\x0\x73\x53\xFA\x6B\x70\x48\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x84\xAA\x4E\xD4\xEC\xD9\xDF\x2C\x6B\x72\x0\x63\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x95\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xC3\xAA\x9C\x9D\xCC\x13\xFB\xB3\xAB\x72\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x80\xAA\x17\xA5\xCC\x91\x3B\xBD\xF1\x72\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA8\xAA\xD9\xF5\xCC\x82\xFB\xBE\xA7\x72\x0\x51\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD2\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xEB\xAA\x52\xCD\xCC\x0\x3B\xB0\xFD\x72\x0\x41\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x43\xAA\xAE\xB5\xCA\x33\x7B\x9B\x35\x72\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x0\xAA\x25\x8D\xCA\xB1\xBB\x95\x6F\x72\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x28\xAA\xEB\xDD\xCA\xA2\x7B\x96\x39\x72\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x6B\xAA\x60\xE5\xCA\x20\xBB\x98\x63\x72\x0\x41\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x2C\xAA\xA0\xE5\xCE\xA3\xBB\x97\xA3\x72\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x6F\xAA\x39\x94\xEA\x68\x5F\x9\xF9\x72\x0\x43\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD5\x38\xEE\x9D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x47\xAA\xE5\x8D\xCE\x32\xBB\x9A\xAF\x72\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x4\xAA\x6E\xB5\xCE\xB0\x7B\x94\xF5\x72\x0\x71\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE6\xAA\x89\xDD\xC9\x63\x7B\x9F\xFB\x72\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xA5\xAA\x2\xE5\xC9\xE1\xBB\x91\xA1\x72\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x8D\xAA\xCC\xB5\xC9\xF2\x7B\x92\xF7\x72\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xCE\xAA\x47\x8D\xC9\x70\xBB\x9C\xAD\x72\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x89\xAA\x87\x8D\xCD\xF3\xBB\x93\x6D\x72\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xCA\xAA\xC\xB5\xCD\x71\x7B\x9D\x37\x72\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xE2\xAA\xC2\xE5\xCD\x62\xBB\x9E\x61\x72\x0\x71\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA1\xAA\x49\xDD\xCD\xE0\x7B\x90\x3B\x72\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE\xAA\xA2\xE5\xCB\x23\xBB\xAA\x35\x73\x0\x51\x1B\xFA\x22\x50\x50\x46\xBA\xAA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4D\xAA\x29\xDD\xCB\xA1\x7B\xA4\x6F\x73\x0\x41\x1B\xFB\xA2\x50\x54\x46\xBA\xCA\xAD\xD1\xAA\xAE\xAD\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x65\xAA\xE7\x8D\xCB\xB2\xBB\xA7\x39\x73\x0\x71\x1B\xFA\xA2\x50\x54\x46\xBA\xCA\xAD\xD0\xAA\xAE\xA5\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x26\xAA\x6C\xB5\xCB\x30\x7B\xA9\x63\x73\x0\x61\x1B\xFB\x22\x50\x50\x46\xBA\xAA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x61\xAA\xAC\xB5\xCF\xB3\x7B\xA6\xA3\x73\x0\x61\x1B\xFB\x22\x50\x58\x46\xBA\x8A\xAD\xD2\xAA\xAE\xB5\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x22\xAA\x35\xC4\xEB\x78\x9F\x38\xF9\x73\x0\x63\x53\xFA\xEB\x70\x4C\x46\xBA\x4E\x3D\xD4\x38\xEE\x9D\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xA\xAA\xE9\xDD\xCF\x22\x7B\xAB\xAF\x73\x0\x41\x1B\xFB\xA2\x50\x5C\x46\xBA\xEA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x49\xAA\x62\xE5\xCF\xA0\xBB\xA5\xF5\x73\x0\x51\x1B\xFA\x22\x50\x58\x46\xBA\x8A\xAD\xD3\xAA\xAE\xBD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xAB\xAA\x97\xC4\xEC\x3A\x9F\x3E\xFB\x73\x0\x53\x53\xFB\xEB\x70\x40\x46\xBA\x4E\x3D\xD7\x38\xEE\x8D\xDB\x5\xF2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xE8\xAA\xE\xB5\xC8\xF1\x7B\xA0\xA1\x73\x0\x51\x1B\xFA\x22\x50\x54\x46\xBA\x8A\xAD\xD1\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\xC0\xAA\xC0\xE5\xC8\xE2\xBB\xA3\xF7\x73\x0\x61\x1B\xFB\x22\x50\x54\x46\xBA\x8A\xAD\xD0\xAA\xAE\xAD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x83\xAA\x4B\xDD\xC8\x60\x7B\xAD\xAD\x73\x0\x71\x1B\xFA\xA2\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\xC4\xAA\x99\x94\xE8\xAA\x5F\x32\x6D\x73\x0\x63\x53\xFA\xEB\x70\x48\x46\xBA\x6E\x3D\xD6\x38\xEE\x9D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x87\xAA\x0\xE5\xCC\x61\xBB\xAC\x37\x73\x0\x61\x1B\xFB\x22\x50\x5C\x46\xBA\xAA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xAF\xAA\xCE\xB5\xCC\x72\x7B\xAF\x61\x73\x0\x51\x1B\xFA\x22\x50\x5C\x46\xBA\xAA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEC\xAA\x45\x8D\xCC\xF0\xBB\xA1\x3B\x73\x0\x41\x1B\xFB\xA2\x50\x58\x46\xBA\xCA\xAD\xD3\xAA\xAE\xB5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x44\xAA\xB9\xF5\xCA\xC3\xFB\x8A\xF3\x73\x0\x71\x1B\xFB\xA2\x50\x5C\x46\xBA\xCA\xAD\xD1\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\xFB\xFC\x16\xD0\x6E\x9C\xBB\x74\xB5\xDB\xA8\xAE\xC1\x51\x7\xFA\xAF\xE0\x1E\x0\xE6\xFF\x99\xAD\x0\xA7\x98\x9E\xEB\x91\x21\xFC\x26\x0\x4B\xB3\xF9\x77\x10\x52\xEC\xBA\x7F\xFD\xD1\x0\x2E\xA8\x8F\x5\x11\x2F\xEA\x23\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x2F\xAA\xFC\x9D\xCA\x52\xFB\x87\xFF\x73\x0\x51\x1B\xFB\x22\x50\x58\x46\xBA\xAA\xAD\xD2\xAA\xAE\xB5\xDB\x5\xBB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x6C\xAA\x77\xA5\xCA\xD0\x3B\x89\xA5\x73\x0\x41\x1B\xFA\xA2\x50\x5C\x46\xBA\xCA\xAD\xD0\xAA\xAE\xBD\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x2B\xAA\xA5\xEC\xEA\x1A\x1F\x16\x65\x73\x0\x53\x53\xFA\xEB\x70\x44\x46\xBA\x4E\x3D\xD4\x38\xEE\x85\xDB\x5\xB2\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x68\xAA\x2E\xD4\xEA\x98\xDF\x18\x3F\x73\x0\x43\x53\xFB\x6B\x70\x40\x46\xBA\x2E\x3D\xD6\x38\xEE\x8D\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x40\xAA\xF2\xCD\xCE\xC2\x3B\x8B\x69\x73\x0\x61\x1B\xFA\x22\x50\x50\x46\xBA\x8A\xAD\xD3\xAA\xAE\xA5\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x3\xAA\x79\xF5\xCE\x40\xFB\x85\x33\x73\x0\x71\x1B\xFB\xA2\x50\x54\x46\xBA\xEA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB5\xE1\xAA\x8C\xD4\xED\xDA\xDF\x1E\x3D\x73\x0\x73\x53\xFA\x6B\x70\x4C\x46\xBA\x2E\x3D\xD5\x38\xEE\x9D\xDB\x5\x32\xAF\xEC\x52\x0",
+ "\xFE\x4B\xFC\x14\x90\x6E\x90\xBB\x75\x25\xDB\xA3\xAE\xC1\x75\x7\xFA\xAF\xE0\x7\x0\xFB\xCD\x55\x8A\x49\x9B\x9D\x2A\x9B\x3\x63\xE9\x4B\x80\x7F\x23\xFA\x2C\x70\x4B\xC8\xBA\x89\x25\xD7\x49\x2E\xAD\x39\x5\x43\x6F\xEF\x4E\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8A\xAA\xDB\xF5\xC9\x2\xFB\x83\x31\x73\x0\x41\x1B\xFA\xA2\x50\x58\x46\xBA\xEA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\xC9\xAA\x42\x84\xED\xC9\x1F\x1D\x6B\x73\x0\x43\x53\xFB\x6B\x70\x4C\x46\xBA\x2E\x3D\xD4\x38\xEE\x95\xDB\x5\x32\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x8E\xAA\x90\xCD\xCD\x3\x3B\x82\xAB\x73\x0\x51\x1B\xFB\x22\x50\x54\x46\xBA\xAA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\xCD\xAA\x9\xBC\xE9\xC8\xDF\x1C\xF1\x73\x0\x53\x53\xFA\xEB\x70\x40\x46\xBA\x6E\x3D\xD6\x38\xEE\x85\xDB\x5\x72\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE5\xAA\xD5\xA5\xCD\x92\x3B\x8F\xA7\x73\x0\x71\x1B\xFB\xA2\x50\x50\x46\xBA\xCA\xAD\xD3\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA6\xAA\x5E\x9D\xCD\x10\xFB\x81\xFD\x73\x0\x61\x1B\xFA\x22\x50\x54\x46\xBA\xAA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB1\x6B\xAA\x95\xB4\xEF\xDA\x1F\x28\x77\x73\x0\x73\x53\xFB\xEB\x70\x4C\x46\xBA\x2E\x3D\xD6\x38\xEE\x9D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x28\xAA\xC\xC5\xCB\x11\xFB\xB6\x2D\x73\x0\x71\x1B\xFA\x22\x50\x58\x46\xBA\xEA\xAD\xD0\xAA\xAE\xB5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x0\xAA\xC2\x95\xCB\x2\x3B\xB5\x7B\x73\x0\x41\x1B\xFB\x22\x50\x58\x46\xBA\xEA\xAD\xD1\xAA\xAE\xBD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x43\xAA\x49\xAD\xCB\x80\xFB\xBB\x21\x73\x0\x51\x1B\xFA\xA2\x50\x5C\x46\xBA\x8A\xAD\xD3\xAA\xAE\xB5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\x4\xAA\x89\xAD\xCF\x3\xFB\xB4\xE1\x73\x0\x51\x1B\xFA\xA2\x50\x54\x46\xBA\xAA\xAD\xD3\xAA\xAE\xAD\xDB\x5\xFB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\x47\xAA\x2\x95\xCF\x81\x3B\xBA\xBB\x73\x0\x41\x1B\xFB\x22\x50\x50\x46\xBA\xCA\xAD\xD1\xAA\xAE\xA5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x6F\xAA\xCC\xC5\xCF\x92\xFB\xB9\xED\x73\x0\x71\x1B\xFA\x22\x50\x50\x46\xBA\xCA\xAD\xD0\xAA\xAE\xAD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x2C\xAA\x47\xFD\xCF\x10\x3B\xB7\xB7\x73\x0\x61\x1B\xFB\xA2\x50\x54\x46\xBA\xAA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xFB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\xCE\xAA\xA0\x95\xC8\xC3\x3B\xBC\xB9\x73\x0\x71\x1B\xFA\x22\x50\x5C\x46\xBA\xCA\xAD\xD2\xAA\xAE\xB5\xDB\x5\x7B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x27\x8D\xAA\x2B\xAD\xC8\x41\xFB\xB2\xE3\x73\x0\x61\x1B\xFB\xA2\x50\x58\x46\xBA\xAA\xAD\xD0\xAA\xAE\xBD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xA5\xAA\xE5\xFD\xC8\x52\x3B\xB1\xB5\x73\x0\x51\x1B\xFA\xA2\x50\x58\x46\xBA\xAA\xAD\xD1\xAA\xAE\xB5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xE6\xAA\x6E\xC5\xC8\xD0\xFB\xBF\xEF\x73\x0\x41\x1B\xFB\x22\x50\x5C\x46\xBA\xCA\xAD\xD3\xAA\xAE\xBD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xA1\xAA\xAE\xC5\xCC\x53\xFB\xB0\x2F\x73\x0\x41\x1B\xFB\x22\x50\x54\x46\xBA\xEA\xAD\xD3\xAA\xAE\xA5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xE2\xAA\x25\xFD\xCC\xD1\x3B\xBE\x75\x73\x0\x51\x1B\xFA\xA2\x50\x50\x46\xBA\x8A\xAD\xD1\xAA\xAE\xAD\xDB\x5\x3B\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\xCA\xAA\xEB\xAD\xCC\xC2\xFB\xBD\x23\x73\x0\x61\x1B\xFB\xA2\x50\x50\x46\xBA\x8A\xAD\xD0\xAA\xAE\xA5\xDB\x5\x3B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\x89\xAA\x60\x95\xCC\x40\x3B\xB3\x79\x73\x0\x71\x1B\xFA\x22\x50\x54\x46\xBA\xEA\xAD\xD2\xAA\xAE\xAD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x21\xAA\x9C\xED\xCA\x73\x7B\x98\xB1\x73\x0\x41\x1B\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD0\xAA\xAE\xAD\xDB\x5\x7B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\x62\xAA\x5\x9C\xEE\xB8\x9F\x6\xEB\x73\x0\x43\x53\xFB\xEB\x70\x44\x46\xBA\x2E\x3D\xD6\x38\xEE\x85\xDB\x5\x32\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\x4A\xAA\xD9\x85\xCA\xE2\x7B\x95\xBD\x73\x0\x61\x1B\xFA\xA2\x50\x54\x46\xBA\x8A\xAD\xD3\xAA\xAE\xAD\xDB\x5\x7B\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\x9\xAA\x52\xBD\xCA\x60\xBB\x9B\xE7\x73\x0\x71\x1B\xFB\x22\x50\x50\x46\xBA\xEA\xAD\xD1\xAA\xAE\xA5\xDB\x5\x7B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB0\x4E\xAA\x80\xF4\xEA\xAA\x9F\x4\x27\x73\x0\x63\x53\xFB\x6B\x70\x48\x46\xBA\x6E\x3D\xD5\x38\xEE\x9D\xDB\x5\x72\xAF\xEE\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x26\xD\xAA\x19\x85\xCE\x61\x7B\x9A\x7D\x73\x0\x61\x1B\xFA\xA2\x50\x5C\x46\xBA\xAA\xAD\xD3\xAA\xAE\xB5\xDB\x5\x3B\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x22\x25\xAA\xD7\xD5\xCE\x72\xBB\x99\x2B\x73\x0\x51\x1B\xFB\xA2\x50\x5C\x46\xBA\xAA\xAD\xD2\xAA\xAE\xBD\xDB\x5\x3B\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB4\x66\xAA\x4E\xA4\xEA\xB9\x5F\x7\x71\x73\x0\x53\x53\xFA\x6B\x70\x48\x46\xBA\x6E\x3D\xD4\x38\xEE\x95\xDB\x5\x72\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x84\xAA\xBB\x85\xC9\x23\x7B\x9C\x7F\x73\x0\x51\x1B\xFB\xA2\x50\x50\x46\xBA\xAA\xAD\xD0\xAA\xAE\xA5\xDB\x5\xBB\x8F\xED\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB7\xC7\xAA\x22\xF4\xED\xE8\x9F\x2\x25\x73\x0\x53\x53\xFA\x6B\x70\x44\x46\xBA\x6E\x3D\xD6\x38\xEE\x8D\xDB\x5\xF2\xAF\xE8\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x23\xEF\xAA\xFE\xED\xC9\xB2\x7B\x91\x73\x73\x0\x71\x1B\xFB\x22\x50\x54\x46\xBA\xCA\xAD\xD3\xAA\xAE\xA5\xDB\x5\xBB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x25\xAC\xAA\x75\xD5\xC9\x30\xBB\x9F\x29\x73\x0\x61\x1B\xFA\xA2\x50\x50\x46\xBA\xAA\xAD\xD1\xAA\xAE\xAD\xDB\x5\xBB\x8F\xEB\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x20\xEB\xAA\xB5\xD5\xCD\xB3\xBB\x90\xE9\x73\x0\x61\x1B\xFA\xA2\x50\x58\x46\xBA\x8A\xAD\xD1\xAA\xAE\xB5\xDB\x5\xFB\x8F\xE9\x76\x1",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB6\xA8\xAA\x2C\xA4\xE9\x78\x5F\xE\xB3\x73\x0\x63\x53\xFB\x6B\x70\x4C\x46\xBA\x4E\x3D\xD7\x38\xEE\x9D\xDB\x5\xB2\xAF\xEC\x52\x0",
+ "\xFE\x2B\xFC\x15\x50\x6E\xAC\xBB\x74\x15\xDB\xAF\xAE\xC1\x71\x7\xFA\xAF\xE0\x10\x0\xD3\x3B\xB2\x80\xAA\xE2\xF4\xE9\x6B\x9F\xD\xE5\x73\x0\x53\x53\xFA\x6B\x70\x4C\x46\xBA\x4E\x3D\xD6\x38\xEE\x95\xDB\x5\xB2\xAF\xEA\x52\x0",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x24\xC3\xAA\x7B\x85\xCD\xA0\x7B\x93\xBF\x73\x0\x51\x1B\xFB\xA2\x50\x58\x46\xBA\x8A\xAD\xD0\xAA\xAE\xBD\xDB\x5\xFB\x8F\xEF\x76\x1",
+ "\xFE\x2B\xFC\x10\x50\x6E\xA8\xBB\x74\x15\xDB\xA5\xAE\xC1\x39\x7\xFA\xAF\xE0\x14\x0\xEF\xAE\x21\x25\xAA\x94\xB5\xCB\x30\x3B\xAA\x6B\x72\x80\x41\x13\xFA\x22\x50\x50\x46\xBA\xEA\xAD\xD2\xAA\xAE\xA5\xDB\x5\xBB\x8F\xE9\x76\x1",
+};
diff --git a/gfx/layers/d3d11/BlendShaderConstants.h b/gfx/layers/d3d11/BlendShaderConstants.h
new file mode 100644
index 000000000..d89240c18
--- /dev/null
+++ b/gfx/layers/d3d11/BlendShaderConstants.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+#define MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+
+// These constants are shared between CompositorD3D11 and the blend pixel shader.
+#define PS_LAYER_RGB 0
+#define PS_LAYER_RGBA 1
+#define PS_LAYER_YCBCR 2
+#define PS_LAYER_COLOR 3
+
+// These must be in the same order as the Mask enum.
+#define PS_MASK_NONE 0
+#define PS_MASK 1
+
+// These must be in the same order as CompositionOp.
+#define PS_BLEND_MULTIPLY 0
+#define PS_BLEND_SCREEN 1
+#define PS_BLEND_OVERLAY 2
+#define PS_BLEND_DARKEN 3
+#define PS_BLEND_LIGHTEN 4
+#define PS_BLEND_COLOR_DODGE 5
+#define PS_BLEND_COLOR_BURN 6
+#define PS_BLEND_HARD_LIGHT 7
+#define PS_BLEND_SOFT_LIGHT 8
+#define PS_BLEND_DIFFERENCE 9
+#define PS_BLEND_EXCLUSION 10
+#define PS_BLEND_HUE 11
+#define PS_BLEND_SATURATION 12
+#define PS_BLEND_COLOR 13
+#define PS_BLEND_LUMINOSITY 14
+
+#if defined(__cplusplus)
+namespace mozilla {
+namespace layers {
+
+static inline int
+BlendOpToShaderConstant(gfx::CompositionOp aOp) {
+ return int(aOp) - int(gfx::CompositionOp::OP_MULTIPLY);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+// Sanity checks.
+namespace {
+static inline void BlendShaderConstantAsserts() {
+ static_assert(PS_MASK_NONE == int(mozilla::layers::MaskType::MaskNone), "shader constant is out of sync");
+ static_assert(PS_MASK == int(mozilla::layers::MaskType::Mask), "shader constant is out of sync");
+ static_assert(int(mozilla::gfx::CompositionOp::OP_LUMINOSITY) - int(mozilla::gfx::CompositionOp::OP_MULTIPLY) == 14,
+ "shader constants are out of sync");
+}
+} // anonymous namespace
+#endif
+
+#endif // MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
diff --git a/gfx/layers/d3d11/BlendingHelpers.hlslh b/gfx/layers/d3d11/BlendingHelpers.hlslh
new file mode 100644
index 000000000..57d27b23b
--- /dev/null
+++ b/gfx/layers/d3d11/BlendingHelpers.hlslh
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Helper functions.
+float hardlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest * (2.0 * src);
+ } else {
+ // Note: we substitute (2*src-1) into the screen formula below.
+ return 2.0 * dest + 2.0 * src - 1.0 - 2.0 * dest * src;
+ }
+}
+
+float dodge(float dest, float src) {
+ if (dest == 0.0) {
+ return 0.0;
+ } else if (src == 1.0) {
+ return 1.0;
+ } else {
+ return min(1.0, dest / (1.0 - src));
+ }
+}
+
+float burn(float dest, float src) {
+ if (dest == 1.0) {
+ return 1.0;
+ } else if (src == 0.0) {
+ return 0.0;
+ } else {
+ return 1.0 - min(1.0, (1.0 - dest) / src);
+ }
+}
+
+float darken(float dest) {
+ if (dest <= 0.25) {
+ return ((16.0 * dest - 12.0) * dest + 4.0) * dest;
+ } else {
+ return sqrt(dest);
+ }
+}
+
+float softlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);
+ } else {
+ return dest + (2.0 * src - 1.0) * (darken(dest) - dest);
+ }
+}
+
+float Lum(float3 c) {
+ return dot(float3(0.3, 0.59, 0.11), c);
+}
+
+float3 ClipColor(float3 c) {
+ float L = Lum(c);
+ float n = min(min(c.r, c.g), c.b);
+ float x = max(max(c.r, c.g), c.b);
+ if (n < 0.0) {
+ c = L + (((c - L) * L) / (L - n));
+ }
+ if (x > 1.0) {
+ c = L + (((c - L) * (1.0 - L)) / (x - L));
+ }
+ return c;
+}
+
+float3 SetLum(float3 c, float L) {
+ float d = L - Lum(c);
+ return ClipColor(float3(
+ c.r + d,
+ c.g + d,
+ c.b + d));
+}
+
+float Sat(float3 c) {
+ return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);
+}
+
+// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+float3 SetSatInner(float3 c, float s) {
+ if (c.b > c.r) {
+ c.g = (((c.g - c.r) * s) / (c.b - c.r));
+ c.b = s;
+ } else {
+ c.gb = float2(0.0, 0.0);
+ }
+ return float3(0.0, c.g, c.b);
+}
+
+float3 SetSat(float3 c, float s) {
+ if (c.r <= c.g) {
+ if (c.g <= c.b) {
+ c.rgb = SetSatInner(c.rgb, s);
+ } else if (c.r <= c.b) {
+ c.rbg = SetSatInner(c.rbg, s);
+ } else {
+ c.brg = SetSatInner(c.brg, s);
+ }
+ } else if (c.r <= c.b) {
+ c.grb = SetSatInner(c.grb, s);
+ } else if (c.g <= c.b) {
+ c.gbr = SetSatInner(c.gbr, s);
+ } else {
+ c.bgr = SetSatInner(c.bgr, s);
+ }
+ return c;
+}
+
+float3 BlendMultiply(float3 dest, float3 src) {
+ return dest * src;
+}
+
+float3 BlendScreen(float3 dest, float3 src) {
+ return dest + src - (dest * src);
+}
+
+float3 BlendOverlay(float3 dest, float3 src) {
+ return float3(
+ hardlight(src.r, dest.r),
+ hardlight(src.g, dest.g),
+ hardlight(src.b, dest.b));
+}
+
+float3 BlendDarken(float3 dest, float3 src) {
+ return min(dest, src);
+}
+
+float3 BlendLighten(float3 dest, float3 src) {
+ return max(dest, src);
+}
+
+float3 BlendColorDodge(float3 dest, float3 src) {
+ return float3(
+ dodge(dest.r, src.r),
+ dodge(dest.g, src.g),
+ dodge(dest.b, src.b));
+}
+
+float3 BlendColorBurn(float3 dest, float3 src) {
+ return float3(
+ burn(dest.r, src.r),
+ burn(dest.g, src.g),
+ burn(dest.b, src.b));
+}
+
+float3 BlendHardLight(float3 dest, float3 src) {
+ return float3(
+ hardlight(dest.r, src.r),
+ hardlight(dest.g, src.g),
+ hardlight(dest.b, src.b));
+}
+
+float3 BlendSoftLight(float3 dest, float3 src) {
+ return float3(
+ softlight(dest.r, src.r),
+ softlight(dest.g, src.g),
+ softlight(dest.b, src.b));
+}
+
+float3 BlendDifference(float3 dest, float3 src) {
+ return abs(dest - src);
+}
+
+float3 BlendExclusion(float3 dest, float3 src) {
+ return dest + src - 2.0 * dest * src;
+}
+
+float3 BlendHue(float3 dest, float3 src) {
+ return SetLum(SetSat(src, Sat(dest)), Lum(dest));
+}
+
+float3 BlendSaturation(float3 dest, float3 src) {
+ return SetLum(SetSat(dest, Sat(src)), Lum(dest));
+}
+
+float3 BlendColor(float3 dest, float3 src) {
+ return SetLum(src, Lum(dest));
+}
+
+float3 BlendLuminosity(float3 dest, float3 src) {
+ return SetLum(dest, Lum(src));
+}
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp
new file mode 100644
index 000000000..540d39b33
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -0,0 +1,1586 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorD3D11.h"
+
+#include "TextureD3D11.h"
+#include "CompositorD3D11Shaders.h"
+
+#include "gfxWindowsPlatform.h"
+#include "nsIWidget.h"
+#include "mozilla/gfx/D3D11Checks.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/ContentHost.h"
+#include "mozilla/layers/Effects.h"
+#include "nsWindowsHelpers.h"
+#include "gfxPrefs.h"
+#include "gfxConfig.h"
+#include "gfxCrashReporterUtils.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/StackArray.h"
+#include "mozilla/Services.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/Telemetry.h"
+#include "BlendShaderConstants.h"
+
+#include "D3D11ShareHandleImage.h"
+#include "D3D9SurfaceImage.h"
+
+#include <dxgi1_2.h>
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static bool CanUsePartialPresents(ID3D11Device* aDevice);
+
+struct Vertex
+{
+ float position[2];
+};
+
+// {1E4D7BEB-D8EC-4A0B-BF0A-63E6DE129425}
+static const GUID sDeviceAttachmentsD3D11 =
+{ 0x1e4d7beb, 0xd8ec, 0x4a0b, { 0xbf, 0xa, 0x63, 0xe6, 0xde, 0x12, 0x94, 0x25 } };
+// {88041664-C835-4AA8-ACB8-7EC832357ED8}
+static const GUID sLayerManagerCount =
+{ 0x88041664, 0xc835, 0x4aa8, { 0xac, 0xb8, 0x7e, 0xc8, 0x32, 0x35, 0x7e, 0xd8 } };
+
+const FLOAT sBlendFactor[] = { 0, 0, 0, 0 };
+
+namespace TexSlot {
+ static const int RGB = 0;
+ static const int Y = 1;
+ static const int Cb = 2;
+ static const int Cr = 3;
+ static const int RGBWhite = 4;
+ static const int Mask = 5;
+ static const int Backdrop = 6;
+}
+
+struct DeviceAttachmentsD3D11
+{
+ DeviceAttachmentsD3D11(ID3D11Device* device)
+ : mSyncHandle(0),
+ mDevice(device),
+ mInitOkay(true)
+ {}
+
+ bool CreateShaders();
+ bool InitBlendShaders();
+ bool InitSyncObject();
+
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11VertexShader>>
+ VertexShaderArray;
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11PixelShader>>
+ PixelShaderArray;
+
+ RefPtr<ID3D11InputLayout> mInputLayout;
+ RefPtr<ID3D11Buffer> mVertexBuffer;
+
+ VertexShaderArray mVSQuadShader;
+ VertexShaderArray mVSQuadBlendShader;
+ PixelShaderArray mSolidColorShader;
+ PixelShaderArray mRGBAShader;
+ PixelShaderArray mRGBShader;
+ PixelShaderArray mYCbCrShader;
+ PixelShaderArray mComponentAlphaShader;
+ PixelShaderArray mBlendShader;
+ RefPtr<ID3D11Buffer> mPSConstantBuffer;
+ RefPtr<ID3D11Buffer> mVSConstantBuffer;
+ RefPtr<ID3D11RasterizerState> mRasterizerState;
+ RefPtr<ID3D11SamplerState> mLinearSamplerState;
+ RefPtr<ID3D11SamplerState> mPointSamplerState;
+ RefPtr<ID3D11BlendState> mPremulBlendState;
+ RefPtr<ID3D11BlendState> mNonPremulBlendState;
+ RefPtr<ID3D11BlendState> mComponentBlendState;
+ RefPtr<ID3D11BlendState> mDisabledBlendState;
+ RefPtr<IDXGIResource> mSyncTexture;
+ HANDLE mSyncHandle;
+
+private:
+ void InitVertexShader(const ShaderBytes& aShader, VertexShaderArray& aArray, MaskType aMaskType) {
+ InitVertexShader(aShader, getter_AddRefs(aArray[aMaskType]));
+ }
+ void InitPixelShader(const ShaderBytes& aShader, PixelShaderArray& aArray, MaskType aMaskType) {
+ InitPixelShader(aShader, getter_AddRefs(aArray[aMaskType]));
+ }
+ void InitVertexShader(const ShaderBytes& aShader, ID3D11VertexShader** aOut) {
+ if (!mInitOkay) {
+ return;
+ }
+ if (Failed(mDevice->CreateVertexShader(aShader.mData, aShader.mLength, nullptr, aOut), "create vs")) {
+ mInitOkay = false;
+ }
+ }
+ void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut) {
+ if (!mInitOkay) {
+ return;
+ }
+ if (Failed(mDevice->CreatePixelShader(aShader.mData, aShader.mLength, nullptr, aOut), "create ps")) {
+ mInitOkay = false;
+ }
+ }
+
+ bool Failed(HRESULT hr, const char* aContext) {
+ if (SUCCEEDED(hr))
+ return false;
+
+ gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr);
+ return true;
+ }
+
+ // Only used during initialization.
+ RefPtr<ID3D11Device> mDevice;
+ bool mInitOkay;
+};
+
+CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mAttachments(nullptr)
+ , mHwnd(nullptr)
+ , mDisableSequenceForNextFrame(false)
+ , mAllowPartialPresents(false)
+ , mVerifyBuffersFailed(false)
+{
+}
+
+CompositorD3D11::~CompositorD3D11()
+{
+ if (mDevice) {
+ int referenceCount = 0;
+ UINT size = sizeof(referenceCount);
+ HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+ NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device.");
+ referenceCount--;
+ mDevice->SetPrivateData(sLayerManagerCount,
+ sizeof(referenceCount),
+ &referenceCount);
+
+ if (!referenceCount) {
+ DeviceAttachmentsD3D11 *attachments;
+ size = sizeof(attachments);
+ mDevice->GetPrivateData(sDeviceAttachmentsD3D11, &size, &attachments);
+ // No LayerManagers left for this device. Clear out interfaces stored
+ // which hold a reference to the device.
+ mDevice->SetPrivateData(sDeviceAttachmentsD3D11, 0, nullptr);
+
+ delete attachments;
+ }
+ }
+}
+
+bool
+CompositorD3D11::Initialize(nsCString* const out_failureReason)
+{
+ ScopedGfxFeatureReporter reporter("D3D11 Layers");
+
+ MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
+
+ HRESULT hr;
+
+ mDevice = DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!mDevice) {
+ gfxCriticalNote << "[D3D11] failed to get compositor device.";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE";
+ return false;
+ }
+
+ mDevice->GetImmediateContext(getter_AddRefs(mContext));
+ if (!mContext) {
+ gfxCriticalNote << "[D3D11] failed to get immediate context";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT";
+ return false;
+ }
+
+ mFeatureLevel = mDevice->GetFeatureLevel();
+
+ mHwnd = mWidget->AsWindows()->GetHwnd();
+
+ memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
+
+ int referenceCount = 0;
+ UINT size = sizeof(referenceCount);
+ // If this isn't there yet it'll fail, count will remain 0, which is correct.
+ mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+ referenceCount++;
+ mDevice->SetPrivateData(sLayerManagerCount,
+ sizeof(referenceCount),
+ &referenceCount);
+
+ size = sizeof(DeviceAttachmentsD3D11*);
+ if (FAILED(mDevice->GetPrivateData(sDeviceAttachmentsD3D11,
+ &size,
+ &mAttachments))) {
+ mAttachments = new DeviceAttachmentsD3D11(mDevice);
+ mDevice->SetPrivateData(sDeviceAttachmentsD3D11,
+ sizeof(mAttachments),
+ &mAttachments);
+
+ D3D11_INPUT_ELEMENT_DESC layout[] =
+ {
+ { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+ };
+
+ hr = mDevice->CreateInputLayout(layout,
+ sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
+ LayerQuadVS,
+ sizeof(LayerQuadVS),
+ getter_AddRefs(mAttachments->mInputLayout));
+
+ if (Failed(hr, "CreateInputLayout")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_INPUT_LAYOUT";
+ return false;
+ }
+
+ Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} };
+ CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
+ D3D11_SUBRESOURCE_DATA data;
+ data.pSysMem = (void*)vertices;
+
+ hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mAttachments->mVertexBuffer));
+
+ if (Failed(hr, "create vertex buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_VERTEX_BUFFER";
+ return false;
+ }
+
+ if (!mAttachments->CreateShaders()) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_CREATE_SHADERS";
+ return false;
+ }
+
+ CD3D11_BUFFER_DESC cBufferDesc(sizeof(VertexShaderConstants),
+ D3D11_BIND_CONSTANT_BUFFER,
+ D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE);
+
+ hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVSConstantBuffer));
+ if (Failed(hr, "create vs buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_VS_BUFFER";
+ return false;
+ }
+
+ cBufferDesc.ByteWidth = sizeof(PixelShaderConstants);
+ hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mPSConstantBuffer));
+ if (Failed(hr, "create ps buffer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_PS_BUFFER";
+ return false;
+ }
+
+ CD3D11_RASTERIZER_DESC rastDesc(D3D11_DEFAULT);
+ rastDesc.CullMode = D3D11_CULL_NONE;
+ rastDesc.ScissorEnable = TRUE;
+
+ hr = mDevice->CreateRasterizerState(&rastDesc, getter_AddRefs(mAttachments->mRasterizerState));
+ if (Failed(hr, "create rasterizer")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_RASTERIZER";
+ return false;
+ }
+
+ CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
+ hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mLinearSamplerState));
+ if (Failed(hr, "create linear sampler")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_LINEAR_SAMPLER";
+ return false;
+ }
+
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+ hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mPointSamplerState));
+ if (Failed(hr, "create point sampler")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_POINT_SAMPLER";
+ return false;
+ }
+
+ CD3D11_BLEND_DESC blendDesc(D3D11_DEFAULT);
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendPremul = {
+ TRUE,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendPremul;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mPremulBlendState));
+ if (Failed(hr, "create pm blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_PM_BLENDER";
+ return false;
+ }
+
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendNonPremul = {
+ TRUE,
+ D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendNonPremul;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mNonPremulBlendState));
+ if (Failed(hr, "create npm blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NPM_BLENDER";
+ return false;
+ }
+
+ if (gfxPrefs::ComponentAlphaEnabled()) {
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendComponent = {
+ TRUE,
+ D3D11_BLEND_ONE,
+ D3D11_BLEND_INV_SRC1_COLOR,
+ D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE,
+ D3D11_BLEND_INV_SRC_ALPHA,
+ D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendComponent;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mComponentBlendState));
+ if (Failed(hr, "create component blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_COMP_BLENDER";
+ return false;
+ }
+ }
+
+ D3D11_RENDER_TARGET_BLEND_DESC rtBlendDisabled = {
+ FALSE,
+ D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
+ D3D11_COLOR_WRITE_ENABLE_ALL
+ };
+ blendDesc.RenderTarget[0] = rtBlendDisabled;
+ hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mDisabledBlendState));
+ if (Failed(hr, "create null blender")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NULL_BLENDER";
+ return false;
+ }
+
+ if (!mAttachments->InitSyncObject()) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_OBJ_SYNC";
+ return false;
+ }
+ }
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> dxgiAdapter;
+
+ mDevice->QueryInterface(dxgiDevice.StartAssignment());
+ dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
+
+ {
+ RefPtr<IDXGIFactory> dxgiFactory;
+ dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));
+
+ DXGI_SWAP_CHAIN_DESC swapDesc;
+ ::ZeroMemory(&swapDesc, sizeof(swapDesc));
+ swapDesc.BufferDesc.Width = 0;
+ swapDesc.BufferDesc.Height = 0;
+ swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapDesc.BufferDesc.RefreshRate.Numerator = 60;
+ swapDesc.BufferDesc.RefreshRate.Denominator = 1;
+ swapDesc.SampleDesc.Count = 1;
+ swapDesc.SampleDesc.Quality = 0;
+ swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapDesc.BufferCount = 1;
+ swapDesc.OutputWindow = mHwnd;
+ swapDesc.Windowed = TRUE;
+ swapDesc.Flags = 0;
+ swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+
+
+ /**
+ * Create a swap chain, this swap chain will contain the backbuffer for
+ * the window we draw to. The front buffer is the full screen front
+ * buffer.
+ */
+ hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain));
+ if (Failed(hr, "create swap chain")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN";
+ return false;
+ }
+
+ // We need this because we don't want DXGI to respond to Alt+Enter.
+ dxgiFactory->MakeWindowAssociation(swapDesc.OutputWindow,
+ DXGI_MWA_NO_WINDOW_CHANGES);
+ }
+
+ if (!mWidget->InitCompositor(this)) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR";
+ return false;
+ }
+
+ mAllowPartialPresents = CanUsePartialPresents(mDevice);
+
+ reporter.SetSuccessful();
+ return true;
+}
+
+static bool
+CanUsePartialPresents(ID3D11Device* aDevice)
+{
+ if (gfxPrefs::PartialPresent() > 0) {
+ return true;
+ }
+ if (gfxPrefs::PartialPresent() < 0) {
+ return false;
+ }
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (!D3D11Checks::GetDxgiDesc(aDevice, &desc)) {
+ return false;
+ }
+
+ // We have to disable partial presents on NVIDIA (bug 1189940).
+ if (desc.VendorId == 0x10de) {
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorD3D11::CreateDataTextureSource(TextureFlags aFlags)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN,
+ this, aFlags);
+ return result.forget();
+}
+
+TextureFactoryIdentifier
+CompositorD3D11::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident;
+ ident.mMaxTextureSize = GetMaxTextureSize();
+ ident.mParentProcessType = XRE_GetProcessType();
+ ident.mParentBackend = LayersBackend::LAYERS_D3D11;
+ ident.mSyncHandle = mAttachments->mSyncHandle;
+ return ident;
+}
+
+bool
+CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize)
+{
+ int32_t maxTextureSize = GetMaxTextureSize();
+
+ if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
+ return false;
+ }
+
+ return true;
+}
+
+int32_t
+CompositorD3D11::GetMaxTextureSize() const
+{
+ return GetMaxTextureSizeForFeatureLevel(mFeatureLevel);
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
+ SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr);
+ return nullptr;
+ }
+
+ RefPtr<CompositingRenderTargetD3D11> rt = new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
+ rt->SetSize(IntSize(aRect.width, aRect.height));
+
+ if (aInit == INIT_MODE_CLEAR) {
+ FLOAT clear[] = { 0, 0, 0, 0 };
+ mContext->ClearRenderTargetView(rt->mRTView, clear);
+ }
+
+ return rt.forget();
+}
+
+RefPtr<ID3D11Texture2D>
+CompositorD3D11::CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ aRect.width, aRect.height, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed in CreateRenderTargetFromSource " << hexa(hr);
+ HandleError(hr);
+ return nullptr;
+ }
+
+ if (aSource) {
+ const CompositingRenderTargetD3D11* sourceD3D11 =
+ static_cast<const CompositingRenderTargetD3D11*>(aSource);
+
+ const IntSize& srcSize = sourceD3D11->GetSize();
+ MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0,
+ "render targets should have nonnegative sizes");
+
+ IntRect srcRect(IntPoint(), srcSize);
+ IntRect copyRect(aSourcePoint, aRect.Size());
+ if (!srcRect.Contains(copyRect)) {
+ NS_WARNING("Could not copy the whole copy rect from the render target");
+ }
+
+ copyRect = copyRect.Intersect(srcRect);
+
+ if (!copyRect.IsEmpty()) {
+ D3D11_BOX copyBox;
+ copyBox.front = 0;
+ copyBox.back = 1;
+ copyBox.left = copyRect.x;
+ copyBox.top = copyRect.y;
+ copyBox.right = copyRect.XMost();
+ copyBox.bottom = copyRect.YMost();
+
+ mContext->CopySubresourceRegion(texture, 0,
+ 0, 0, 0,
+ sourceD3D11->GetD3D11Texture(), 0,
+ &copyBox);
+ }
+ }
+
+ return texture;
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint &aSourcePoint)
+{
+ RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, aSource, aSourcePoint);
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<CompositingRenderTargetD3D11> rt =
+ new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
+ rt->SetSize(aRect.Size());
+
+ return rt.forget();
+}
+
+bool
+CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect,
+ RefPtr<ID3D11Texture2D>* aOutTexture,
+ RefPtr<ID3D11ShaderResourceView>* aOutView)
+{
+ RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, mCurrentRT, aRect.TopLeft());
+ if (!texture) {
+ return false;
+ }
+
+ CD3D11_SHADER_RESOURCE_VIEW_DESC desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM);
+
+ RefPtr<ID3D11ShaderResourceView> srv;
+ HRESULT hr = mDevice->CreateShaderResourceView(texture, &desc, getter_AddRefs(srv));
+ if (FAILED(hr) || !srv) {
+ return false;
+ }
+
+ *aOutTexture = texture.forget();
+ *aOutView = srv.forget();
+ return true;
+}
+
+void
+CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget);
+ CompositingRenderTargetD3D11* newRT =
+ static_cast<CompositingRenderTargetD3D11*>(aRenderTarget);
+ if (mCurrentRT != newRT) {
+ mCurrentRT = newRT;
+ mCurrentRT->BindRenderTarget(mContext);
+ }
+
+ if (newRT->HasComplexProjection()) {
+ gfx::Matrix4x4 projection;
+ bool depthEnable;
+ float zNear, zFar;
+ newRT->GetProjection(projection, depthEnable, zNear, zFar);
+ PrepareViewport(newRT->GetSize(), projection, zNear, zFar);
+ } else {
+ PrepareViewport(newRT->GetSize());
+ }
+}
+
+ID3D11PixelShader*
+CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType)
+{
+ switch (aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ return mAttachments->mSolidColorShader[aMaskType];
+ case EffectTypes::RENDER_TARGET:
+ return mAttachments->mRGBAShader[aMaskType];
+ case EffectTypes::RGB: {
+ SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
+ return (format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::R8G8B8A8)
+ ? mAttachments->mRGBAShader[aMaskType]
+ : mAttachments->mRGBShader[aMaskType];
+ }
+ case EffectTypes::YCBCR:
+ return mAttachments->mYCbCrShader[aMaskType];
+ case EffectTypes::COMPONENT_ALPHA:
+ return mAttachments->mComponentAlphaShader[aMaskType];
+ default:
+ NS_WARNING("No shader to load");
+ return nullptr;
+ }
+}
+
+void
+CompositorD3D11::ClearRect(const gfx::Rect& aRect)
+{
+ mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF);
+
+ Matrix4x4 identity;
+ memcpy(&mVSConstants.layerTransform, &identity._11, 64);
+
+ mVSConstants.layerQuad = aRect;
+ mVSConstants.renderTargetOffset[0] = 0;
+ mVSConstants.renderTargetOffset[1] = 0;
+ mPSConstants.layerOpacity[0] = 1.0f;
+
+ D3D11_RECT scissor;
+ scissor.left = aRect.x;
+ scissor.right = aRect.XMost();
+ scissor.top = aRect.y;
+ scissor.bottom = aRect.YMost();
+ mContext->RSSetScissorRects(1, &scissor);
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], nullptr, 0);
+
+ mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], nullptr, 0);
+ mPSConstants.layerColor[0] = 0;
+ mPSConstants.layerColor[1] = 0;
+ mPSConstants.layerColor[2] = 0;
+ mPSConstants.layerColor[3] = 0;
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ return;
+ }
+
+ mContext->Draw(4, 0);
+
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+}
+
+static inline bool
+EffectHasPremultipliedAlpha(Effect* aEffect)
+{
+ if (aEffect->mType == EffectTypes::RGB) {
+ return static_cast<TexturedEffect*>(aEffect)->mPremultiplied;
+ }
+ return true;
+}
+
+static inline int
+EffectToBlendLayerType(Effect* aEffect)
+{
+ switch (aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ return PS_LAYER_COLOR;
+ case EffectTypes::RGB: {
+ gfx::SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
+ return (format == gfx::SurfaceFormat::B8G8R8A8 || format == gfx::SurfaceFormat::R8G8B8A8)
+ ? PS_LAYER_RGBA
+ : PS_LAYER_RGB;
+ }
+ case EffectTypes::RENDER_TARGET:
+ return PS_LAYER_RGBA;
+ case EffectTypes::YCBCR:
+ return PS_LAYER_YCBCR;
+ default:
+ MOZ_ASSERT_UNREACHABLE("blending not supported for this layer type");
+ return 0;
+ }
+}
+
+void
+CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ if (mCurrentClip.IsEmpty()) {
+ return;
+ }
+
+ MOZ_ASSERT(mCurrentRT, "No render target");
+
+ memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
+ IntPoint origin = mCurrentRT->GetOrigin();
+ mVSConstants.renderTargetOffset[0] = origin.x;
+ mVSConstants.renderTargetOffset[1] = origin.y;
+
+ mPSConstants.layerOpacity[0] = aOpacity;
+
+ bool restoreBlendMode = false;
+
+ MaskType maskType = MaskType::MaskNone;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ maskType = MaskType::Mask;
+
+ EffectMask* maskEffect =
+ static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();
+
+ if (!source) {
+ NS_WARNING("Missing texture source!");
+ return;
+ }
+
+ ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
+ mContext->PSSetShaderResources(TexSlot::Mask, 1, &srView);
+
+ const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskEffect->mSize));
+
+ mVSConstants.maskQuad = maskTransform.As2D().TransformBounds(bounds);
+ }
+
+ D3D11_RECT scissor;
+
+ IntRect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
+ if (mCurrentRT == mDefaultRT) {
+ clipRect = clipRect.Intersect(mCurrentClip);
+ }
+
+ if (clipRect.IsEmpty()) {
+ return;
+ }
+
+ scissor.left = clipRect.x;
+ scissor.right = clipRect.XMost();
+ scissor.top = clipRect.y;
+ scissor.bottom = clipRect.YMost();
+
+ RefPtr<ID3D11VertexShader> vertexShader = mAttachments->mVSQuadShader[maskType];
+ RefPtr<ID3D11PixelShader> pixelShader = GetPSForEffect(aEffectChain.mPrimaryEffect, maskType);
+
+ RefPtr<ID3D11Texture2D> mixBlendBackdrop;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+
+ // If the blend operation needs to read from the backdrop, copy the
+ // current render target into a new texture and bind it now.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ gfx::Matrix4x4 backdropTransform;
+ gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &backdropTransform);
+
+ RefPtr<ID3D11ShaderResourceView> srv;
+ if (CopyBackdrop(rect, &mixBlendBackdrop, &srv) &&
+ mAttachments->InitBlendShaders())
+ {
+ vertexShader = mAttachments->mVSQuadBlendShader[maskType];
+ pixelShader = mAttachments->mBlendShader[MaskType::MaskNone];
+
+ ID3D11ShaderResourceView* srView = srv.get();
+ mContext->PSSetShaderResources(TexSlot::Backdrop, 1, &srView);
+
+ memcpy(&mVSConstants.backdropTransform, &backdropTransform._11, 64);
+
+ mPSConstants.blendConfig[0] = EffectToBlendLayerType(aEffectChain.mPrimaryEffect);
+ mPSConstants.blendConfig[1] = int(maskType);
+ mPSConstants.blendConfig[2] = BlendOpToShaderConstant(blendMode);
+ mPSConstants.blendConfig[3] = EffectHasPremultipliedAlpha(aEffectChain.mPrimaryEffect);
+ }
+ }
+ }
+
+ mContext->RSSetScissorRects(1, &scissor);
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->VSSetShader(vertexShader, nullptr, 0);
+ mContext->PSSetShader(pixelShader, nullptr, 0);
+
+ const Rect* pTexCoordRect = nullptr;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ Color color =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
+ mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
+ mPSConstants.layerColor[1] = color.g * color.a * aOpacity;
+ mPSConstants.layerColor[2] = color.b * color.a * aOpacity;
+ mPSConstants.layerColor[3] = color.a * aOpacity;
+ }
+ break;
+ case EffectTypes::RGB:
+ case EffectTypes::RENDER_TARGET:
+ {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ pTexCoordRect = &texturedEffect->mTextureCoords;
+
+ TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
+
+ if (!source) {
+ NS_WARNING("Missing texture source!");
+ return;
+ }
+
+ ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
+ mContext->PSSetShaderResources(TexSlot::RGB, 1, &srView);
+
+ if (!texturedEffect->mPremultiplied) {
+ mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+ restoreBlendMode = true;
+ }
+
+ SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
+ }
+ break;
+ case EffectTypes::YCBCR: {
+ EffectYCbCr* ycbcrEffect =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+
+ SetSamplerForSamplingFilter(SamplingFilter::LINEAR);
+
+ pTexCoordRect = &ycbcrEffect->mTextureCoords;
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSource* source = ycbcrEffect->mTexture;
+
+ if (!source) {
+ NS_WARNING("No texture to composite");
+ return;
+ }
+
+ if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || !source->GetSubSource(Cr)) {
+ // This can happen if we failed to upload the textures, most likely
+ // because of unsupported dimensions (we don't tile YCbCr textures).
+ return;
+ }
+
+ float* yuvToRgb = gfxUtils::Get4x3YuvColorMatrix(ycbcrEffect->mYUVColorSpace);
+ memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, sizeof(mPSConstants.yuvColorMatrix));
+
+ TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11();
+ TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
+ TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
+
+ ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(),
+ sourceCb->GetShaderResourceView(),
+ sourceCr->GetShaderResourceView() };
+ mContext->PSSetShaderResources(TexSlot::Y, 3, srViews);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ MOZ_ASSERT(mAttachments->mComponentBlendState);
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+
+ TextureSourceD3D11* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D11();
+ TextureSourceD3D11* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D11();
+
+ if (!sourceOnWhite || !sourceOnBlack) {
+ NS_WARNING("Missing texture source(s)!");
+ return;
+ }
+
+ SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);
+
+ pTexCoordRect = &effectComponentAlpha->mTextureCoords;
+
+ ID3D11ShaderResourceView* srViews[2] = { sourceOnBlack->GetShaderResourceView(),
+ sourceOnWhite->GetShaderResourceView() };
+ mContext->PSSetShaderResources(TexSlot::RGB, 1, &srViews[0]);
+ mContext->PSSetShaderResources(TexSlot::RGBWhite, 1, &srViews[1]);
+
+ mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
+ restoreBlendMode = true;
+ }
+ break;
+ default:
+ NS_WARNING("Unknown shader type");
+ return;
+ }
+
+ if (pTexCoordRect) {
+ Rect layerRects[4];
+ Rect textureRects[4];
+ size_t rects = DecomposeIntoNoRepeatRects(aRect,
+ *pTexCoordRect,
+ &layerRects,
+ &textureRects);
+ for (size_t i = 0; i < rects; i++) {
+ mVSConstants.layerQuad = layerRects[i];
+ mVSConstants.textureCoords = textureRects[i];
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ break;
+ }
+ mContext->Draw(4, 0);
+ }
+ } else {
+ mVSConstants.layerQuad = aRect;
+
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ } else {
+ mContext->Draw(4, 0);
+ }
+ }
+
+ if (restoreBlendMode) {
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
+ }
+}
+
+void
+CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect* aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect* aClipRectOut,
+ IntRect* aRenderBoundsOut)
+{
+ // Don't composite if we are minimised. Other than for the sake of efficency,
+ // this is important because resizing our buffers when mimised will fail and
+ // cause a crash when we're restored.
+ NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
+ if (::IsIconic(mHwnd)) {
+ // We are not going to render, and not going to call EndFrame so we have to
+ // read-unlock our textures to prevent them from accumulating.
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed.";
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+
+ // If we are in the GPU process then the main process doesn't
+ // know that a device reset has happened and needs to be informed
+ if (XRE_IsGPUProcess()) {
+ GPUParent::GetSingleton()->NotifyDeviceReset();
+ }
+
+ return;
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+
+ // Failed to create a render target or the view.
+ if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
+ mSize.width <= 0 || mSize.height <= 0) {
+ ReadUnlockTextures();
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ IntRect intRect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize());
+ // Sometimes the invalid region is larger than we want to draw.
+ nsIntRegion invalidRegionSafe;
+
+ if (mSize != oldSize) {
+ invalidRegionSafe = intRect;
+ } else {
+ invalidRegionSafe.And(aInvalidRegion, intRect);
+ }
+
+ IntRect invalidRect = invalidRegionSafe.GetBounds();
+
+ IntRect clipRect = invalidRect;
+ if (aClipRectIn) {
+ clipRect.IntersectRect(clipRect, IntRect(aClipRectIn->x, aClipRectIn->y, aClipRectIn->width, aClipRectIn->height));
+ }
+
+ if (clipRect.IsEmpty()) {
+ *aRenderBoundsOut = IntRect();
+ return;
+ }
+
+ mContext->IASetInputLayout(mAttachments->mInputLayout);
+
+ ID3D11Buffer* buffer = mAttachments->mVertexBuffer;
+ UINT size = sizeof(Vertex);
+ UINT offset = 0;
+ mContext->IASetVertexBuffers(0, 1, &buffer, &size, &offset);
+
+ mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
+ mInvalidRegion = invalidRegionSafe;
+
+ if (aClipRectOut) {
+ *aClipRectOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+
+ mCurrentClip = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+
+ mContext->RSSetState(mAttachments->mRasterizerState);
+
+ SetRenderTarget(mDefaultRT);
+
+ // ClearRect will set the correct blend state for us.
+ ClearRect(Rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height));
+
+ if (mAttachments->mSyncTexture) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ mAttachments->mSyncTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+ MOZ_ASSERT(mutex);
+ {
+ HRESULT hr;
+ AutoTextureLock lock(mutex, hr, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ hr = mDevice->GetDeviceRemovedReason();
+ if (hr == S_OK) {
+ // There is no driver-removed event. Crash with this timeout.
+ MOZ_CRASH("GFX: D3D11 normal status timeout");
+ }
+
+ // Since the timeout is related to the driver-removed, clear the
+ // render-bounding size to skip this frame.
+ gfxCriticalNote << "GFX: D3D11 timeout with device-removed:" << gfx::hexa(hr);
+ *aRenderBoundsOut = IntRect();
+ return;
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 abandoned sync";
+ }
+ }
+ }
+}
+
+void
+CompositorD3D11::EndFrame()
+{
+ if (!mDefaultRT) {
+ Compositor::EndFrame();
+ return;
+ }
+
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
+ Compositor::EndFrame();
+ mCurrentRT = nullptr;
+ return;
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+ EnsureSize();
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ Compositor::EndFrame();
+ return;
+ }
+
+ RefPtr<ID3D11Query> query;
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+ mDevice->CreateQuery(&desc, getter_AddRefs(query));
+ if (query) {
+ mContext->End(query);
+ }
+
+ UINT presentInterval = 0;
+
+ bool isWARP = DeviceManagerDx::Get()->IsWARP();
+ if (isWARP) {
+ // When we're using WARP we cannot present immediately as it causes us
+ // to tear when rendering. When not using WARP it appears the DWM takes
+ // care of tearing for us.
+ presentInterval = 1;
+ }
+
+ if (oldSize == mSize) {
+ RefPtr<IDXGISwapChain1> chain;
+ HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain));
+
+ if (SUCCEEDED(hr) && chain && mAllowPartialPresents) {
+ DXGI_PRESENT_PARAMETERS params;
+ PodZero(&params);
+ params.DirtyRectsCount = mInvalidRegion.GetNumRects();
+ StackArray<RECT, 4> rects(params.DirtyRectsCount);
+
+ uint32_t i = 0;
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ rects[i].left = r.x;
+ rects[i].top = r.y;
+ rects[i].bottom = r.YMost();
+ rects[i].right = r.XMost();
+ i++;
+ }
+
+ params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
+ chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
+ } else {
+ hr = mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
+ HandleError(hr);
+ }
+ }
+ mDisableSequenceForNextFrame = false;
+ if (mTarget) {
+ PaintToTarget();
+ }
+ }
+
+ // Block until the previous frame's work has been completed.
+ if (mQuery) {
+ TimeStamp start = TimeStamp::Now();
+ BOOL result;
+ while (mContext->GetData(mQuery, &result, sizeof(BOOL), 0) != S_OK) {
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ break;
+ }
+ if ((TimeStamp::Now() - start) > TimeDuration::FromSeconds(2)) {
+ break;
+ }
+ Sleep(0);
+ }
+ }
+ // Store the query for this frame so we can flush it next time.
+ mQuery = query;
+
+ Compositor::EndFrame();
+
+ mCurrentRT = nullptr;
+}
+
+void
+CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
+{
+ // This view matrix translates coordinates from 0..width and 0..height to
+ // -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate)
+ Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+
+ Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
+ projection._33 = 0.0f;
+
+ PrepareViewport(aSize, projection, 0.0f, 1.0f);
+}
+
+void
+CompositorD3D11::ForcePresent()
+{
+ LayoutDeviceIntSize size = mWidget->GetClientSize();
+
+ DXGI_SWAP_CHAIN_DESC desc;
+ mSwapChain->GetDesc(&desc);
+
+ if (desc.BufferDesc.Width == size.width && desc.BufferDesc.Height == size.height) {
+ mSwapChain->Present(0, 0);
+ }
+}
+
+void
+CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
+ const gfx::Matrix4x4& aProjection,
+ float aZNear, float aZFar)
+{
+ D3D11_VIEWPORT viewport;
+ viewport.MaxDepth = aZFar;
+ viewport.MinDepth = aZNear;
+ viewport.Width = aSize.width;
+ viewport.Height = aSize.height;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+
+ mContext->RSSetViewports(1, &viewport);
+
+ memcpy(&mVSConstants.projection, &aProjection._11, sizeof(mVSConstants.projection));
+}
+
+void
+CompositorD3D11::EnsureSize()
+{
+ mSize = mWidget->GetClientSize();
+}
+
+bool
+CompositorD3D11::VerifyBufferSize()
+{
+ DXGI_SWAP_CHAIN_DESC swapDesc;
+ HRESULT hr;
+
+ hr = mSwapChain->GetDesc(&swapDesc);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+
+ if (((swapDesc.BufferDesc.Width == mSize.width &&
+ swapDesc.BufferDesc.Height == mSize.height) ||
+ mSize.width <= 0 || mSize.height <= 0) &&
+ !mVerifyBuffersFailed) {
+ return true;
+ }
+
+ ID3D11RenderTargetView* view = nullptr;
+ mContext->OMSetRenderTargets(1, &view, nullptr);
+
+ if (mDefaultRT) {
+ RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView;
+ RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV;
+
+ // Make sure the texture, which belongs to the swapchain, is destroyed
+ // before resizing the swapchain.
+ if (mCurrentRT == mDefaultRT) {
+ mCurrentRT = nullptr;
+ }
+ MOZ_ASSERT(mDefaultRT->hasOneRef());
+ mDefaultRT = nullptr;
+
+ RefPtr<ID3D11Resource> resource;
+ rtView->GetResource(getter_AddRefs(resource));
+
+ ULONG newRefCnt = rtView.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "mRTView not destroyed on final release! RefCnt: " << newRefCnt;
+ }
+
+ if (srView) {
+ newRefCnt = srView.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "mSRV not destroyed on final release! RefCnt: " << newRefCnt;
+ }
+ }
+
+ newRefCnt = resource.forget().take()->Release();
+
+ if (newRefCnt > 0) {
+ gfxCriticalError() << "Unexpecting lingering references to backbuffer! RefCnt: " << newRefCnt;
+ }
+ }
+
+ hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ 0);
+
+ mVerifyBuffersFailed = FAILED(hr);
+ if (mVerifyBuffersFailed) {
+ gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " << mSize;
+ HandleError(hr);
+ }
+
+ return !mVerifyBuffersFailed;
+}
+
+bool
+CompositorD3D11::UpdateRenderTarget()
+{
+ EnsureSize();
+ if (!VerifyBufferSize()) {
+ gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " << mSize;
+ return false;
+ }
+
+ if (mDefaultRT) {
+ return true;
+ }
+
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", " << (int)mVerifyBuffersFailed;
+ return false;
+ }
+
+ HRESULT hr;
+
+ RefPtr<ID3D11Texture2D> backBuf;
+
+ hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ // This happens on some GPUs/drivers when there's a TDR.
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalError() << "GetBuffer returned invalid call! " << mSize << ", " << (int)mVerifyBuffersFailed;
+ return false;
+ }
+ }
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+
+ mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
+ mDefaultRT->SetSize(mSize.ToUnknownSize());
+
+ return true;
+}
+
+bool
+DeviceAttachmentsD3D11::InitSyncObject()
+{
+ // Sync object is not supported on WARP.
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ // It's okay to do this on Windows 8. But for now we'll just bail
+ // whenever we're using WARP.
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 1, 1, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE |
+ D3D11_BIND_RENDER_TARGET);
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (Failed(hr, "create sync texture")) {
+ return false;
+ }
+
+ hr = texture->QueryInterface((IDXGIResource**)getter_AddRefs(mSyncTexture));
+ if (Failed(hr, "QI sync texture")) {
+ return false;
+ }
+
+ hr = mSyncTexture->GetSharedHandle(&mSyncHandle);
+ if (FAILED(hr) || !mSyncHandle) {
+ gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: "
+ << hexa(hr);
+ NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> void {
+ Accumulate(Telemetry::D3D11_SYNC_HANDLE_FAILURE, 1);
+ }));
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DeviceAttachmentsD3D11::InitBlendShaders()
+{
+ if (!mVSQuadBlendShader[MaskType::MaskNone]) {
+ InitVertexShader(sLayerQuadBlendVS, mVSQuadBlendShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadBlendMaskVS, mVSQuadBlendShader, MaskType::Mask);
+ }
+ if (!mBlendShader[MaskType::MaskNone]) {
+ InitPixelShader(sBlendShader, mBlendShader, MaskType::MaskNone);
+ }
+ return mInitOkay;
+}
+
+bool
+DeviceAttachmentsD3D11::CreateShaders()
+{
+ InitVertexShader(sLayerQuadVS, mVSQuadShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadMaskVS, mVSQuadShader, MaskType::Mask);
+
+ InitPixelShader(sSolidColorShader, mSolidColorShader, MaskType::MaskNone);
+ InitPixelShader(sSolidColorShaderMask, mSolidColorShader, MaskType::Mask);
+ InitPixelShader(sRGBShader, mRGBShader, MaskType::MaskNone);
+ InitPixelShader(sRGBShaderMask, mRGBShader, MaskType::Mask);
+ InitPixelShader(sRGBAShader, mRGBAShader, MaskType::MaskNone);
+ InitPixelShader(sRGBAShaderMask, mRGBAShader, MaskType::Mask);
+ InitPixelShader(sYCbCrShader, mYCbCrShader, MaskType::MaskNone);
+ InitPixelShader(sYCbCrShaderMask, mYCbCrShader, MaskType::Mask);
+ if (gfxPrefs::ComponentAlphaEnabled()) {
+ InitPixelShader(sComponentAlphaShader, mComponentAlphaShader, MaskType::MaskNone);
+ InitPixelShader(sComponentAlphaShaderMask, mComponentAlphaShader, MaskType::Mask);
+ }
+
+ return mInitOkay;
+}
+
+bool
+CompositorD3D11::UpdateConstantBuffers()
+{
+ HRESULT hr;
+ D3D11_MAPPED_SUBRESOURCE resource;
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr) || !resource.pData) {
+ gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+ *(VertexShaderConstants*)resource.pData = mVSConstants;
+ mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
+ resource.pData = nullptr;
+
+ hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr) || !resource.pData) {
+ gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ HandleError(hr);
+ return false;
+ }
+ *(PixelShaderConstants*)resource.pData = mPSConstants;
+ mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
+
+ ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
+
+ mContext->VSSetConstantBuffers(0, 1, &buffer);
+
+ buffer = mAttachments->mPSConstantBuffer;
+ mContext->PSSetConstantBuffers(0, 1, &buffer);
+ return true;
+}
+
+void
+CompositorD3D11::SetSamplerForSamplingFilter(SamplingFilter aSamplingFilter)
+{
+ ID3D11SamplerState *sampler;
+ switch (aSamplingFilter) {
+ case SamplingFilter::POINT:
+ sampler = mAttachments->mPointSamplerState;
+ break;
+ case SamplingFilter::LINEAR:
+ default:
+ sampler = mAttachments->mLinearSamplerState;
+ break;
+ }
+
+ mContext->PSSetSamplers(0, 1, &sampler);
+}
+
+void
+CompositorD3D11::PaintToTarget()
+{
+ RefPtr<ID3D11Texture2D> backBuf;
+ HRESULT hr;
+
+ hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 1";
+ HandleError(hr);
+ return;
+ }
+
+ D3D11_TEXTURE2D_DESC bbDesc;
+ backBuf->GetDesc(&bbDesc);
+
+ CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
+ softDesc.MipLevels = 1;
+ softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ softDesc.Usage = D3D11_USAGE_STAGING;
+ softDesc.BindFlags = 0;
+
+ RefPtr<ID3D11Texture2D> readTexture;
+
+ hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 2";
+ HandleError(hr);
+ return;
+ }
+ mContext->CopyResource(readTexture, backBuf);
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
+ if (FAILED(hr)) {
+ gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 3";
+ HandleError(hr);
+ return;
+ }
+ RefPtr<DataSourceSurface> sourceSurface =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)map.pData,
+ map.RowPitch,
+ IntSize(bbDesc.Width, bbDesc.Height),
+ SurfaceFormat::B8G8R8A8);
+ mTarget->CopySurface(sourceSurface,
+ IntRect(0, 0, bbDesc.Width, bbDesc.Height),
+ IntPoint(-mTargetBounds.x, -mTargetBounds.y));
+
+ mTarget->Flush();
+ mContext->Unmap(readTexture, 0);
+}
+
+bool
+CompositorD3D11::Failed(HRESULT hr, const char* aContext)
+{
+ if (SUCCEEDED(hr))
+ return false;
+
+ gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
+ return true;
+}
+
+void
+CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
+{
+ if (SUCCEEDED(hr)) {
+ return;
+ }
+
+ if (aSeverity == Critical) {
+ MOZ_CRASH("GFX: Unrecoverable D3D11 error");
+ }
+
+ if (mDevice && DeviceManagerDx::Get()->GetCompositorDevice() != mDevice) {
+ gfxCriticalNote << "Out of sync D3D11 devices in HandleError, " << (int)mVerifyBuffersFailed;
+ }
+
+ HRESULT hrOnReset = S_OK;
+ bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
+
+ if (deviceRemoved && mDevice) {
+ hrOnReset = mDevice->GetDeviceRemovedReason();
+ } else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) {
+ hrOnReset = mDevice->GetDeviceRemovedReason();
+ if (hrOnReset != S_OK) {
+ deviceRemoved = true;
+ }
+ }
+
+ // Device reset may not be an error on our side, but can mess things up so
+ // it's useful to see it in the reports.
+ gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
+ << (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
+ : "[CompositorD3D11] error code: ")
+ << hexa(hr) << ", " << hexa(hrOnReset) << ", " << (int)mVerifyBuffersFailed;
+
+ // Crash if we are making invalid calls outside of device removal
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ gfxDevCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved : LogReason::D3D11InvalidCall) << "Invalid D3D11 api call";
+ }
+
+ if (aSeverity == Recoverable) {
+ NS_WARNING("Encountered a recoverable D3D11 error");
+ }
+}
+
+}
+}
diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h
new file mode 100644
index 000000000..7a1a5cc7d
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITORD3D11_H
+#define MOZILLA_GFX_COMPOSITORD3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "gfx2DGlue.h"
+#include "mozilla/layers/Compositor.h"
+#include "TextureD3D11.h"
+#include <d3d11.h>
+
+class nsWidget;
+
+namespace mozilla {
+namespace layers {
+
+#define LOGD3D11(param)
+
+struct VertexShaderConstants
+{
+ float layerTransform[4][4];
+ float projection[4][4];
+ float renderTargetOffset[4];
+ gfx::Rect textureCoords;
+ gfx::Rect layerQuad;
+ gfx::Rect maskQuad;
+ float backdropTransform[4][4];
+};
+
+struct PixelShaderConstants
+{
+ float layerColor[4];
+ float layerOpacity[4];
+ int blendConfig[4];
+ float yuvColorMatrix[3][4];
+};
+
+struct DeviceAttachmentsD3D11;
+
+class CompositorD3D11 : public Compositor
+{
+public:
+ CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+ ~CompositorD3D11();
+
+ virtual CompositorD3D11* AsCompositorD3D11() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual TextureFactoryIdentifier
+ GetTextureFactoryIdentifier() override;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override;
+ virtual int32_t GetMaxTextureSize() const final;
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override {}
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect,
+ SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget* aSurface) override;
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mCurrentRT;
+ }
+
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
+
+ /**
+ * Declare an offset to use when rendering layers. This will be ignored when
+ * rendering to a target instead of the screen.
+ */
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override
+ {
+ if (aOffset.x || aOffset.y) {
+ NS_RUNTIMEABORT("SetScreenRenderOffset not supported by CompositorD3D11.");
+ }
+ // If the offset is 0, 0 that's okay.
+ }
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void DrawQuad(const gfx::Rect &aRect,
+ const gfx::IntRect &aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ /**
+ * Start a new frame. If aClipRectIn is null, sets *aClipRectOut to the
+ * screen dimensions.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+
+ /**
+ * Flush the current frame to the screen.
+ */
+ virtual void EndFrame() override;
+
+ /**
+ * Post rendering stuff if the rendering is outside of this Compositor
+ * e.g., by Composer2D
+ */
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override {}
+
+ /**
+ * Setup the viewport and projection matrix for rendering
+ * to a window of the given dimensions.
+ */
+ virtual void PrepareViewport(const gfx::IntSize& aSize);
+ virtual void PrepareViewport(const gfx::IntSize& aSize, const gfx::Matrix4x4& aProjection,
+ float aZNear, float aZFar);
+
+ virtual bool SupportsPartialTextureUpdate() override { return true; }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Direct3D 11"; }
+#endif
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_D3D11;
+ }
+
+ virtual void ForcePresent();
+
+ ID3D11Device* GetDevice() { return mDevice; }
+
+ ID3D11DeviceContext* GetDC() { return mContext; }
+
+private:
+ enum Severity {
+ Recoverable,
+ DebugAssert,
+ Critical,
+ };
+
+ void HandleError(HRESULT hr, Severity aSeverity = DebugAssert);
+
+ // Same as Failed(), except the severity is critical (with no abort) and
+ // a string prefix must be provided.
+ bool Failed(HRESULT hr, const char* aContext);
+
+ // ensure mSize is up to date with respect to mWidget
+ void EnsureSize();
+ bool VerifyBufferSize();
+ bool UpdateRenderTarget();
+ bool UpdateConstantBuffers();
+ void SetSamplerForSamplingFilter(gfx::SamplingFilter aSamplingFilter);
+ ID3D11PixelShader* GetPSForEffect(Effect *aEffect, MaskType aMaskType);
+ void PaintToTarget();
+ RefPtr<ID3D11Texture2D> CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint);
+ bool CopyBackdrop(const gfx::IntRect& aRect,
+ RefPtr<ID3D11Texture2D>* aOutTexture,
+ RefPtr<ID3D11ShaderResourceView>* aOutView);
+
+ RefPtr<ID3D11DeviceContext> mContext;
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<IDXGISwapChain> mSwapChain;
+ RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
+ RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
+
+ RefPtr<ID3D11Query> mQuery;
+
+ DeviceAttachmentsD3D11* mAttachments;
+
+ LayoutDeviceIntSize mSize;
+
+ HWND mHwnd;
+
+ D3D_FEATURE_LEVEL mFeatureLevel;
+
+ VertexShaderConstants mVSConstants;
+ PixelShaderConstants mPSConstants;
+ bool mDisableSequenceForNextFrame;
+ bool mAllowPartialPresents;
+
+ gfx::IntRect mInvalidRect;
+ // This is the clip rect applied to the default DrawTarget (i.e. the window)
+ gfx::IntRect mCurrentClip;
+ nsIntRegion mInvalidRegion;
+
+ bool mVerifyBuffersFailed;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/d3d11/CompositorD3D11.hlsl b/gfx/layers/d3d11/CompositorD3D11.hlsl
new file mode 100644
index 000000000..21175704b
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.hlsl
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BlendingHelpers.hlslh"
+#include "BlendShaderConstants.h"
+
+typedef float4 rect;
+
+float4x4 mLayerTransform : register(vs, c0);
+float4x4 mProjection : register(vs, c4);
+float4 vRenderTargetOffset : register(vs, c8);
+rect vTextureCoords : register(vs, c9);
+rect vLayerQuad : register(vs, c10);
+rect vMaskQuad : register(vs, c11);
+float4x4 mBackdropTransform : register(vs, c12);
+
+float4 fLayerColor : register(ps, c0);
+float fLayerOpacity : register(ps, c1);
+
+// x = layer type
+// y = mask type
+// z = blend op
+// w = is premultiplied
+uint4 iBlendConfig : register(ps, c2);
+
+row_major float3x3 mYuvColorMatrix : register(ps, c3);
+
+sampler sSampler : register(ps, s0);
+
+// The mix-blend mega shader uses all variables, so we have to make sure they
+// are assigned fixed slots.
+Texture2D tRGB : register(ps, t0);
+Texture2D tY : register(ps, t1);
+Texture2D tCb : register(ps, t2);
+Texture2D tCr : register(ps, t3);
+Texture2D tRGBWhite : register(ps, t4);
+Texture2D tMask : register(ps, t5);
+Texture2D tBackdrop : register(ps, t6);
+
+struct VS_INPUT {
+ float2 vPosition : POSITION;
+};
+
+struct VS_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+};
+
+struct VS_MASK_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+};
+
+// Combined struct for the mix-blend compatible vertex shaders.
+struct VS_BLEND_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+ float2 vBackdropCoords : TEXCOORD2;
+};
+
+struct PS_OUTPUT {
+ float4 vSrc;
+ float4 vAlpha;
+};
+
+float2 TexCoords(const float2 aPosition)
+{
+ float2 result;
+ const float2 size = vTextureCoords.zw;
+ result.x = vTextureCoords.x + aPosition.x * size.x;
+ result.y = vTextureCoords.y + aPosition.y * size.y;
+
+ return result;
+}
+
+SamplerState LayerTextureSamplerLinear
+{
+ Filter = MIN_MAG_MIP_LINEAR;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+float4 TransformedPosition(float2 aInPosition)
+{
+ // the current vertex's position on the quad
+ // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform
+ float4 position = float4(0, 0, 0, 1);
+
+ // We use 4 component floats to uniquely describe a rectangle, by the structure
+ // of x, y, width, height. This allows us to easily generate the 4 corners
+ // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+ // stream source for our LayerQuad vertex shader. We do this by doing:
+ // Xout = x + Xin * width
+ // Yout = y + Yin * height
+ float2 size = vLayerQuad.zw;
+ position.x = vLayerQuad.x + aInPosition.x * size.x;
+ position.y = vLayerQuad.y + aInPosition.y * size.y;
+
+ position = mul(mLayerTransform, position);
+
+ return position;
+}
+
+float4 VertexPosition(float4 aTransformedPosition)
+{
+ float4 result;
+ result.w = aTransformedPosition.w;
+ result.xyz = aTransformedPosition.xyz / aTransformedPosition.w;
+ result -= vRenderTargetOffset;
+ result.xyz *= result.w;
+
+ result = mul(mProjection, result);
+
+ return result;
+}
+
+float2 BackdropPosition(float4 aPosition)
+{
+ // Move the position from clip space (-1,1) into 0..1 space.
+ float2 pos;
+ pos.x = (aPosition.x + 1.0) / 2.0;
+ pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;
+
+ return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
+}
+
+VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT outp;
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ outp.vPosition = VertexPosition(position);
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+
+ return outp;
+}
+
+VS_MASK_OUTPUT LayerQuadMaskVS(const VS_INPUT aVertex)
+{
+ VS_MASK_OUTPUT outp;
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ outp.vPosition = VertexPosition(position);
+
+ // calculate the position on the mask texture
+ outp.vMaskCoords.x = (position.x - vMaskQuad.x) / vMaskQuad.z;
+ outp.vMaskCoords.y = (position.y - vMaskQuad.y) / vMaskQuad.w;
+ // We use the w coord to do non-perspective correct interpolation:
+ // the quad might be transformed in 3D, in which case it will have some
+ // perspective. The graphics card will do perspective-correct interpolation
+ // of the texture, but our mask is already transformed and so we require
+ // linear interpolation. Therefore, we must correct the interpolation
+ // ourselves, we do this by multiplying all coords by w here, and dividing by
+ // w in the pixel shader (post-interpolation), we pass w in outp.vMaskCoords.z.
+ // See http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness
+ outp.vMaskCoords.z = 1;
+ outp.vMaskCoords *= position.w;
+
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+
+ return outp;
+}
+
+float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return result * mask;
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+float4 CalculateYCbCrColor(const float2 aTexCoords)
+{
+ float3 yuv;
+ float4 color;
+
+ yuv.x = tY.Sample(sSampler, aTexCoords).r - 0.06275;
+ yuv.y = tCb.Sample(sSampler, aTexCoords).r - 0.50196;
+ yuv.z = tCr.Sample(sSampler, aTexCoords).r - 0.50196;
+
+ color.rgb = mul(mYuvColorMatrix, yuv);
+ color.a = 1.0f;
+
+ return color;
+}
+
+float4 YCbCrShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+PS_OUTPUT ComponentAlphaShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ result.vSrc *= fLayerOpacity * mask;
+ result.vAlpha *= fLayerOpacity * mask;
+
+ return result;
+}
+
+float4 SolidColorShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return fLayerColor * mask;
+}
+
+/*
+ * Un-masked versions
+ *************************************************************
+ */
+float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+}
+
+float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+ return result;
+}
+
+float4 YCbCrShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity;
+}
+
+PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+ result.vSrc *= fLayerOpacity;
+ result.vAlpha *= fLayerOpacity;
+ return result;
+}
+
+float4 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return fLayerColor;
+}
+
+// Mix-blend compatible vertex shaders.
+VS_BLEND_OUTPUT LayerQuadBlendVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT v = LayerQuadVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = float3(0, 0, 0);
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+VS_BLEND_OUTPUT LayerQuadBlendMaskVS(const VS_INPUT aVertex)
+{
+ VS_MASK_OUTPUT v = LayerQuadMaskVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = v.vMaskCoords;
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+// The layer type and mask type are specified as constants. We use these to
+// call the correct pixel shader to determine the source color for blending.
+// Unfortunately this also requires some boilerplate to convert VS_BLEND_OUTPUT
+// to a compatible pixel shader input.
+float4 ComputeBlendSourceColor(const VS_BLEND_OUTPUT aVertex)
+{
+ if (iBlendConfig.y == PS_MASK_NONE) {
+ VS_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShader(tmp);
+ }
+ return SolidColorShader(tmp);
+ } else if (iBlendConfig.y == PS_MASK) {
+ VS_MASK_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ tmp.vMaskCoords = aVertex.vMaskCoords;
+
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShaderMask(tmp);
+ }
+ return SolidColorShaderMask(tmp);
+ }
+
+ return float4(0.0, 0.0, 0.0, 1.0);
+}
+
+float3 ChooseBlendFunc(float3 dest, float3 src)
+{
+ [flatten] switch (iBlendConfig.z) {
+ case PS_BLEND_MULTIPLY:
+ return BlendMultiply(dest, src);
+ case PS_BLEND_SCREEN:
+ return BlendScreen(dest, src);
+ case PS_BLEND_OVERLAY:
+ return BlendOverlay(dest, src);
+ case PS_BLEND_DARKEN:
+ return BlendDarken(dest, src);
+ case PS_BLEND_LIGHTEN:
+ return BlendLighten(dest, src);
+ case PS_BLEND_COLOR_DODGE:
+ return BlendColorDodge(dest, src);
+ case PS_BLEND_COLOR_BURN:
+ return BlendColorBurn(dest, src);
+ case PS_BLEND_HARD_LIGHT:
+ return BlendHardLight(dest, src);
+ case PS_BLEND_SOFT_LIGHT:
+ return BlendSoftLight(dest, src);
+ case PS_BLEND_DIFFERENCE:
+ return BlendDifference(dest, src);
+ case PS_BLEND_EXCLUSION:
+ return BlendExclusion(dest, src);
+ case PS_BLEND_HUE:
+ return BlendHue(dest, src);
+ case PS_BLEND_SATURATION:
+ return BlendSaturation(dest, src);
+ case PS_BLEND_COLOR:
+ return BlendColor(dest, src);
+ case PS_BLEND_LUMINOSITY:
+ return BlendLuminosity(dest, src);
+ default:
+ return float3(0, 0, 0);
+ }
+}
+
+float4 BlendShader(const VS_BLEND_OUTPUT aVertex) : SV_Target
+{
+ float4 backdrop = tBackdrop.Sample(sSampler, aVertex.vBackdropCoords.xy);
+ float4 source = ComputeBlendSourceColor(aVertex);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ backdrop.rgb /= backdrop.a;
+ if (iBlendConfig.w) {
+ source.rgb /= source.a;
+ }
+
+ float4 result;
+ result.rgb = ChooseBlendFunc(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
diff --git a/gfx/layers/d3d11/CompositorD3D11Shaders.h b/gfx/layers/d3d11/CompositorD3D11Shaders.h
new file mode 100755
index 000000000..084dc667a
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11Shaders.h
@@ -0,0 +1,9434 @@
+struct ShaderBytes { const void* mData; size_t mLength; };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64 [unused]
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 8 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ dcl_texcoord v0
+ mad oT0.xy, v0, c9.zwzw, c9
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c2
+ mad r0, c1, r0.x, r1
+ add r0, r0, c3
+ rcp r1.x, r0.w
+ mul r0.xyz, r0, r1.x
+ add r0, r0, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 15 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[11], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_temps 2
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r0.xyz, r0.xyzx, r0.wwww
+add r0.xyzw, r0.xyzw, -cb0[8].xyzw
+mul r0.xyz, r0.wwww, r0.xyzx
+mul r1.xyzw, r0.yyyy, cb0[5].xyzw
+mad r1.xyzw, cb0[4].xyzw, r0.xxxx, r1.xyzw
+mad r1.xyzw, cb0[6].xyzw, r0.zzzz, r1.xyzw
+mad o0.xyzw, cb0[7].xyzw, r0.wwww, r1.xyzw
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+ret
+// Approximately 13 instruction slots used
+#endif
+
+const BYTE LayerQuadVS[] =
+{
+ 68, 88, 66, 67, 250, 65,
+ 94, 205, 254, 155, 52, 90,
+ 43, 147, 203, 201, 141, 74,
+ 80, 143, 1, 0, 0, 0,
+ 68, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 152, 1, 0, 0, 160, 3,
+ 0, 0, 28, 4, 0, 0,
+ 184, 6, 0, 0, 236, 6,
+ 0, 0, 65, 111, 110, 57,
+ 88, 1, 0, 0, 88, 1,
+ 0, 0, 0, 2, 254, 255,
+ 24, 1, 0, 0, 64, 0,
+ 0, 0, 2, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 0, 0, 36, 0,
+ 1, 0, 60, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 8, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 10, 0, 238, 160,
+ 10, 0, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 1, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 0, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 8, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 255, 128, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 85, 128, 5, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 4, 0, 228, 160,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 15, 128, 6, 0,
+ 228, 160, 0, 0, 170, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 7, 0, 228, 160, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 3, 192, 0, 0, 255, 128,
+ 0, 0, 228, 160, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 12, 192, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 0, 2,
+ 0, 0, 64, 0, 1, 0,
+ 128, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 166, 10, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 13, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 12, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 148, 2, 0, 0, 1, 0,
+ 0, 0, 72, 0, 0, 0,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 254, 255,
+ 0, 1, 0, 0, 108, 2,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 36, 71,
+ 108, 111, 98, 97, 108, 115,
+ 0, 171, 171, 171, 60, 0,
+ 0, 0, 11, 0, 0, 0,
+ 96, 0, 0, 0, 96, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 104, 1,
+ 0, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 2, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 136, 1,
+ 0, 0, 64, 0, 0, 0,
+ 64, 0, 0, 0, 2, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 148, 1,
+ 0, 0, 128, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 168, 1, 0, 0,
+ 0, 0, 0, 0, 184, 1,
+ 0, 0, 144, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 216, 1,
+ 0, 0, 160, 0, 0, 0,
+ 16, 0, 0, 0, 2, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 227, 1,
+ 0, 0, 176, 0, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 200, 1, 0, 0,
+ 0, 0, 0, 0, 237, 1,
+ 0, 0, 192, 0, 0, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 120, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 1, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 168, 1, 0, 0,
+ 0, 0, 0, 0, 12, 2,
+ 0, 0, 16, 1, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 28, 2, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 32, 1, 0, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 60, 2, 0, 0,
+ 0, 0, 0, 0, 76, 2,
+ 0, 0, 48, 1, 0, 0,
+ 44, 0, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 84, 101, 120, 116, 117,
+ 114, 101, 67, 111, 111, 114,
+ 100, 115, 0, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 76, 97, 121,
+ 101, 114, 81, 117, 97, 100,
+ 0, 118, 77, 97, 115, 107,
+ 81, 117, 97, 100, 0, 109,
+ 66, 97, 99, 107, 100, 114,
+ 111, 112, 84, 114, 97, 110,
+ 115, 102, 111, 114, 109, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 49,
+ 48, 46, 49, 0, 73, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3,
+ 0, 0, 80, 79, 83, 73,
+ 84, 73, 79, 78, 0, 171,
+ 171, 171, 79, 83, 71, 78,
+ 80, 0, 0, 0, 2, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 12, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171
+};
+ShaderBytes sLayerQuadVS = { LayerQuadVS, sizeof(LayerQuadVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ mov oC0, c0
+
+// approximately 1 instruction slot used
+ps_4_0
+dcl_constantbuffer CB0[1], immediateIndexed
+dcl_output o0.xyzw
+mov o0.xyzw, cb0[0].xyzw
+ret
+// Approximately 2 instruction slots used
+#endif
+
+const BYTE SolidColorShader[] =
+{
+ 68, 88, 66, 67, 181, 3,
+ 20, 0, 202, 78, 164, 59,
+ 210, 171, 118, 253, 118, 104,
+ 133, 184, 1, 0, 0, 0,
+ 112, 4, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 132, 0, 0, 0, 204, 0,
+ 0, 0, 72, 1, 0, 0,
+ 228, 3, 0, 0, 60, 4,
+ 0, 0, 65, 111, 110, 57,
+ 68, 0, 0, 0, 68, 0,
+ 0, 0, 0, 2, 255, 255,
+ 20, 0, 0, 0, 48, 0,
+ 0, 0, 1, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 48, 0, 0, 0, 36, 0,
+ 0, 0, 48, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 160, 255, 255, 0, 0,
+ 83, 72, 68, 82, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 16, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 6,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 116, 1,
+ 0, 0, 0, 0, 0, 0,
+ 132, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 148, 1,
+ 0, 0, 0, 0, 0, 0,
+ 164, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 180, 1,
+ 0, 0, 0, 0, 0, 0,
+ 196, 1, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 212, 1,
+ 0, 0, 0, 0, 0, 0,
+ 228, 1, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 116, 1,
+ 0, 0, 0, 0, 0, 0,
+ 36, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 68, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 79, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 52, 2,
+ 0, 0, 0, 0, 0, 0,
+ 89, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 244, 1,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sSolidColorShader = { SolidColorShader, sizeof(SolidColorShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mul r0.xyz, r0, c0.x
+ mov r0.w, c0.x
+ mov oC0, r0
+
+// approximately 4 instruction slots used (1 texture, 3 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 1
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul o0.xyz, r0.xyzx, cb0[1].xxxx
+mov o0.w, cb0[1].x
+ret
+// Approximately 4 instruction slots used
+#endif
+
+const BYTE RGBShader[] =
+{
+ 68, 88, 66, 67, 181, 57,
+ 113, 191, 104, 206, 206, 65,
+ 235, 158, 87, 241, 179, 224,
+ 69, 235, 1, 0, 0, 0,
+ 120, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 204, 0, 0, 0, 136, 1,
+ 0, 0, 4, 2, 0, 0,
+ 236, 4, 0, 0, 68, 5,
+ 0, 0, 65, 111, 110, 57,
+ 140, 0, 0, 0, 140, 0,
+ 0, 0, 0, 2, 255, 255,
+ 88, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 180, 0,
+ 0, 0, 64, 0, 0, 0,
+ 45, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 114, 32, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 54, 0,
+ 0, 6, 130, 32, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 224, 2,
+ 0, 0, 1, 0, 0, 0,
+ 148, 0, 0, 0, 3, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 184, 2, 0, 0,
+ 124, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 133, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 138, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 138, 0, 0, 0, 11, 0,
+ 0, 0, 172, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 208, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
+ 0, 0, 0, 0, 0, 0,
+ 240, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 0, 0, 0, 0,
+ 48, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 80, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 112, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 144, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 155, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 165, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sRGBShader = { RGBShader, sizeof(RGBShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 3 instruction slots used (1 texture, 2 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 1
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul o0.xyzw, r0.xyzw, cb0[1].xxxx
+ret
+// Approximately 3 instruction slots used
+#endif
+
+const BYTE RGBAShader[] =
+{
+ 68, 88, 66, 67, 0, 64,
+ 93, 222, 73, 216, 128, 20,
+ 42, 69, 82, 179, 209, 122,
+ 136, 190, 1, 0, 0, 0,
+ 84, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 192, 0, 0, 0, 100, 1,
+ 0, 0, 224, 1, 0, 0,
+ 200, 4, 0, 0, 32, 5,
+ 0, 0, 65, 111, 110, 57,
+ 128, 0, 0, 0, 128, 0,
+ 0, 0, 0, 2, 255, 255,
+ 76, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 156, 0,
+ 0, 0, 64, 0, 0, 0,
+ 39, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 1, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 224, 2,
+ 0, 0, 1, 0, 0, 0,
+ 148, 0, 0, 0, 3, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 184, 2, 0, 0,
+ 124, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 133, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 138, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 138, 0, 0, 0, 11, 0,
+ 0, 0, 172, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 208, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 224, 1,
+ 0, 0, 0, 0, 0, 0,
+ 240, 1, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 16, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 32, 2,
+ 0, 0, 0, 0, 0, 0,
+ 48, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 80, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 0, 0, 0, 0,
+ 112, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 144, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 155, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 128, 2,
+ 0, 0, 0, 0, 0, 0,
+ 165, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sRGBAShader = { RGBAShader, sizeof(RGBAShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tRGBWhite texture float4 2d t4 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+// SV_Target 1 xyzw 1 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t4
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t0, s0
+ texld r1, t0, s1
+ add r1, r0, -r1
+ add r1, r1, c1.x
+ mov r0.w, r1.y
+ mul r1, r1, c0.x
+ mov oC1, r1
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 9 instruction slots used (2 texture, 7 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t4
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_output o1.xyzw
+dcl_temps 2
+sample r0.xyzw, v1.xyxx, t4.xyzw, s0
+sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+add r0.xyzw, -r0.xyzw, r1.xyzw
+add r0.xyzw, r0.xyzw, l(1.000000, 1.000000, 1.000000, 1.000000)
+mov r1.w, r0.y
+mul o1.xyzw, r0.xyzw, cb0[1].xxxx
+mul o0.xyzw, r1.xyzw, cb0[1].xxxx
+ret
+// Approximately 8 instruction slots used
+#endif
+
+const BYTE ComponentAlphaShader[] =
+{
+ 68, 88, 66, 67, 168, 127,
+ 203, 56, 125, 182, 211, 23,
+ 166, 215, 189, 218, 181, 48,
+ 227, 73, 1, 0, 0, 0,
+ 212, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 64, 1, 0, 0, 160, 2,
+ 0, 0, 28, 3, 0, 0,
+ 48, 6, 0, 0, 136, 6,
+ 0, 0, 65, 111, 110, 57,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 2, 255, 255,
+ 200, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 4, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 81, 0,
+ 0, 5, 1, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 2, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 0, 228, 129, 2, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 85, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 0, 0, 0, 160,
+ 1, 0, 0, 2, 1, 8,
+ 15, 128, 1, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 0, 0, 0, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 88, 1, 0, 0, 64, 0,
+ 0, 0, 86, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 0, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 4, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 1, 0, 0, 0,
+ 104, 0, 0, 2, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 4, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 54, 0, 0, 5,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 56, 0, 0, 8, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 8, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 12, 3, 0, 0,
+ 1, 0, 0, 0, 192, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 228, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 180, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 82, 71, 66,
+ 87, 104, 105, 116, 101, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 180, 0, 0, 0, 11, 0,
+ 0, 0, 216, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 0, 0, 0, 0,
+ 252, 1, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 12, 2,
+ 0, 0, 0, 0, 0, 0,
+ 28, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 60, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 76, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 1,
+ 0, 0, 0, 0, 0, 0,
+ 156, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 188, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 199, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 209, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 80, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sComponentAlphaShader = { ComponentAlphaShader, sizeof(ComponentAlphaShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+// c1 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t1
+// s1 s0 t2
+// s2 s0 t3
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ mov r0.w, c4.z
+ texld r1, t0, s1
+ texld r2, t0, s0
+ add r2.x, r2.x, c4.x
+ add r2.y, r1.x, c4.y
+ texld r1, t0, s2
+ add r2.z, r1.x, c4.y
+ dp3 r0.x, c1, r2
+ dp3 r0.y, c2, r2
+ dp3 r0.z, c3, r2
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 12 instruction slots used (3 texture, 9 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_input_ps linear v1.xy
+dcl_output o0.xyzw
+dcl_temps 3
+mov r0.w, l(1.000000)
+sample r1.xyzw, v1.xyxx, t1.xyzw, s0
+add r1.x, r1.x, l(-0.062750)
+sample r2.xyzw, v1.xyxx, t2.xyzw, s0
+add r1.y, r2.x, l(-0.501960)
+sample r2.xyzw, v1.xyxx, t3.xyzw, s0
+add r1.z, r2.x, l(-0.501960)
+dp3 r0.x, cb0[3].xyzx, r1.xyzx
+dp3 r0.y, cb0[4].xyzx, r1.xyzx
+dp3 r0.z, cb0[5].xyzx, r1.xyzx
+mul o0.xyzw, r0.xyzw, cb0[1].xxxx
+ret
+// Approximately 12 instruction slots used
+#endif
+
+const BYTE YCbCrShader[] =
+{
+ 68, 88, 66, 67, 56, 199,
+ 91, 5, 215, 233, 204, 14,
+ 193, 166, 163, 11, 246, 123,
+ 165, 88, 1, 0, 0, 0,
+ 156, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 144, 1, 0, 0, 100, 3,
+ 0, 0, 224, 3, 0, 0,
+ 16, 7, 0, 0, 104, 7,
+ 0, 0, 65, 111, 110, 57,
+ 80, 1, 0, 0, 80, 1,
+ 0, 0, 0, 2, 255, 255,
+ 8, 1, 0, 0, 72, 0,
+ 0, 0, 2, 0, 48, 0,
+ 0, 0, 72, 0, 0, 0,
+ 72, 0, 3, 0, 36, 0,
+ 0, 0, 72, 0, 1, 0,
+ 0, 0, 2, 0, 1, 0,
+ 3, 0, 2, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 4, 0, 15, 160, 18, 131,
+ 128, 189, 115, 128, 0, 191,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 4, 0,
+ 170, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 1, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 1, 128,
+ 2, 0, 0, 128, 4, 0,
+ 0, 160, 2, 0, 0, 3,
+ 2, 0, 2, 128, 1, 0,
+ 0, 128, 4, 0, 85, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 2, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 4, 128,
+ 1, 0, 0, 128, 4, 0,
+ 85, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 228, 160, 2, 0, 228, 128,
+ 8, 0, 0, 3, 0, 0,
+ 2, 128, 2, 0, 228, 160,
+ 2, 0, 228, 128, 8, 0,
+ 0, 3, 0, 0, 4, 128,
+ 3, 0, 228, 160, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 0, 0, 0, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 204, 1, 0, 0,
+ 64, 0, 0, 0, 115, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 6, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 2, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 3, 0,
+ 0, 0, 85, 85, 0, 0,
+ 98, 16, 0, 3, 50, 16,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 104, 0, 0, 2, 3, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 1, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 18, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 1, 64, 0, 0, 18, 131,
+ 128, 189, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 2, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 34, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 115, 128, 0, 191,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 3, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 66, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 16, 0,
+ 0, 8, 18, 0, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 16, 0, 0, 8, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 8,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 12, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 40, 3, 0, 0, 1, 0,
+ 0, 0, 220, 0, 0, 0,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 0, 3,
+ 0, 0, 188, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 197, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 200, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 2, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 204, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 208, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 89, 0,
+ 116, 67, 98, 0, 116, 67,
+ 114, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 208, 0, 0, 0,
+ 11, 0, 0, 0, 244, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 252, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 164, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 216, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 227, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 0, 0,
+ 0, 0, 237, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 80, 0, 0, 0, 2, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sYCbCrShader = { YCbCrShader, sizeof(YCbCrShader) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64 [unused]
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 9 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c12, 1, 0, 0, 0
+ dcl_texcoord v0
+ mov r0.z, c12.x
+ rcp r0.w, c11.z
+ mad r1.xy, v0, c10.zwzw, c10
+ mul r2, r1.y, c2
+ mad r1, c1, r1.x, r2
+ add r1, r1, c3
+ add r2.xy, r1, -c11
+ mul r0.x, r0.w, r2.x
+ rcp r0.w, c11.w
+ mul r0.y, r0.w, r2.y
+ mul oT1.xyz, r0, r1.w
+ mad oT0.xy, v0, c9.zwzw, c9
+ rcp r0.x, r1.w
+ mul r1.xyz, r0.x, r1
+ add r0, r1, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 22 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[12], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o2.xyz
+dcl_temps 4
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r1.xyz, r0.xyzx, r0.wwww
+mov r1.w, r0.w
+add r2.xyzw, r1.xyzw, -cb0[8].xyzw
+mul r1.xyz, r2.wwww, r2.xyzx
+mul r3.xyzw, r1.yyyy, cb0[5].xyzw
+mad r3.xyzw, cb0[4].xyzw, r1.xxxx, r3.xyzw
+mad r3.xyzw, cb0[6].xyzw, r1.zzzz, r3.xyzw
+mad o0.xyzw, cb0[7].xyzw, r2.wwww, r3.xyzw
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+add r0.xy, r0.xyxx, -cb0[11].xyxx
+div r0.xy, r0.xyxx, cb0[11].zwzz
+mov r0.z, l(1.000000)
+mul o2.xyz, r1.wwww, r0.xyzx
+ret
+// Approximately 18 instruction slots used
+#endif
+
+const BYTE LayerQuadMaskVS[] =
+{
+ 68, 88, 66, 67, 47, 28,
+ 196, 228, 98, 79, 27, 152,
+ 192, 25, 215, 128, 59, 234,
+ 245, 240, 1, 0, 0, 0,
+ 108, 8, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 20, 2, 0, 0, 176, 4,
+ 0, 0, 44, 5, 0, 0,
+ 200, 7, 0, 0, 252, 7,
+ 0, 0, 65, 111, 110, 57,
+ 212, 1, 0, 0, 212, 1,
+ 0, 0, 0, 2, 254, 255,
+ 148, 1, 0, 0, 64, 0,
+ 0, 0, 2, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 0, 0, 36, 0,
+ 1, 0, 60, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 9, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 12, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 12, 0, 0, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 11, 0, 170, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 0, 0, 228, 144,
+ 10, 0, 238, 160, 10, 0,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 15, 128, 1, 0,
+ 85, 128, 2, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 1, 0, 228, 160,
+ 1, 0, 0, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 2, 0, 0, 3, 2, 0,
+ 3, 128, 1, 0, 228, 128,
+ 11, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 11, 0,
+ 255, 160, 5, 0, 0, 3,
+ 0, 0, 2, 128, 0, 0,
+ 255, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 224, 0, 0, 228, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 9, 0,
+ 238, 160, 9, 0, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 1, 128, 1, 0, 255, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 128, 8, 0,
+ 228, 161, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 255, 128, 0, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 85, 128,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 1, 0, 15, 128,
+ 4, 0, 228, 160, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 6, 0, 228, 160,
+ 0, 0, 170, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 7, 0,
+ 228, 160, 0, 0, 255, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 3, 192,
+ 0, 0, 255, 128, 0, 0,
+ 228, 160, 0, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 12, 192, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 148, 2, 0, 0,
+ 64, 0, 1, 0, 165, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 12, 0, 0, 0,
+ 95, 0, 0, 3, 50, 16,
+ 16, 0, 0, 0, 0, 0,
+ 103, 0, 0, 4, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 50, 32, 16, 0,
+ 1, 0, 0, 0, 101, 0,
+ 0, 3, 114, 32, 16, 0,
+ 2, 0, 0, 0, 104, 0,
+ 0, 2, 4, 0, 0, 0,
+ 50, 0, 0, 11, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 10, 0,
+ 0, 0, 70, 128, 32, 0,
+ 0, 0, 0, 0, 10, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 0, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 15,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 2, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 86, 5, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 6, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 166, 10, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 50, 0, 0, 10, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 11, 0, 0, 0,
+ 14, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 18, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 104, 0, 0, 0,
+ 3, 0, 0, 0, 8, 0,
+ 0, 0, 80, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 92, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 92, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadMaskVS = { LayerQuadMaskVS, sizeof(LayerQuadMaskVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t1.xyz
+ dcl_2d s0
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s0
+ mul r0, r0.x, c0
+ mov oC0, r0
+
+// approximately 5 instruction slots used (1 texture, 4 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[1], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 1
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+mul o0.xyzw, r0.xxxx, cb0[0].xyzw
+ret
+// Approximately 4 instruction slots used
+#endif
+
+const BYTE SolidColorShaderMask[] =
+{
+ 68, 88, 66, 67, 11, 0,
+ 43, 127, 123, 42, 253, 228,
+ 4, 220, 7, 130, 11, 94,
+ 213, 177, 1, 0, 0, 0,
+ 164, 5, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 220, 0, 0, 0, 156, 1,
+ 0, 0, 24, 2, 0, 0,
+ 0, 5, 0, 0, 112, 5,
+ 0, 0, 65, 111, 110, 57,
+ 156, 0, 0, 0, 156, 0,
+ 0, 0, 0, 2, 255, 255,
+ 104, 0, 0, 0, 52, 0,
+ 0, 0, 1, 0, 40, 0,
+ 0, 0, 52, 0, 0, 0,
+ 52, 0, 1, 0, 36, 0,
+ 0, 0, 52, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 170, 176,
+ 5, 0, 0, 3, 0, 0,
+ 3, 128, 0, 0, 255, 128,
+ 1, 0, 228, 176, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 184, 0, 0, 0,
+ 64, 0, 0, 0, 46, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 5, 0, 0, 0,
+ 85, 85, 0, 0, 98, 16,
+ 0, 3, 114, 16, 16, 0,
+ 2, 0, 0, 0, 101, 0,
+ 0, 3, 242, 32, 16, 0,
+ 0, 0, 0, 0, 104, 0,
+ 0, 2, 1, 0, 0, 0,
+ 14, 0, 0, 7, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 83, 84,
+ 65, 84, 116, 0, 0, 0,
+ 4, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 68, 69, 70,
+ 224, 2, 0, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 4, 255, 255,
+ 0, 1, 0, 0, 184, 2,
+ 0, 0, 124, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 133, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 139, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 77, 97, 115, 107, 0, 36,
+ 71, 108, 111, 98, 97, 108,
+ 115, 0, 139, 0, 0, 0,
+ 11, 0, 0, 0, 172, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 180, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 192, 1, 0, 0, 0, 0,
+ 0, 0, 208, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 224, 1, 0, 0, 0, 0,
+ 0, 0, 240, 1, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 16, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 0, 0,
+ 0, 0, 48, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 80, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 92, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 192, 1, 0, 0, 0, 0,
+ 0, 0, 112, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 144, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 155, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 0, 0,
+ 0, 0, 165, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sSolidColorShaderMask = { SolidColorShaderMask, sizeof(SolidColorShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1.xyz, r1, c0.x
+ mov r1.w, c0.x
+ mul r0, r0.x, r1
+ mov oC0, r0
+
+// approximately 8 instruction slots used (2 texture, 6 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 2
+sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+mul r0.xyz, r0.xyzx, cb0[1].xxxx
+div r1.xy, v2.xyxx, v2.zzzz
+sample r1.xyzw, r1.xyxx, t5.xyzw, s0
+mov r0.w, cb0[1].x
+mul o0.xyzw, r0.xyzw, r1.xxxx
+ret
+// Approximately 7 instruction slots used
+#endif
+
+const BYTE RGBShaderMask[] =
+{
+ 68, 88, 66, 67, 89, 221,
+ 15, 22, 232, 140, 114, 122,
+ 200, 15, 217, 125, 153, 18,
+ 224, 0, 1, 0, 0, 0,
+ 136, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 36, 1, 0, 0, 88, 2,
+ 0, 0, 212, 2, 0, 0,
+ 228, 5, 0, 0, 84, 6,
+ 0, 0, 65, 111, 110, 57,
+ 228, 0, 0, 0, 228, 0,
+ 0, 0, 0, 2, 255, 255,
+ 172, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 5, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 7, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 5, 0,
+ 0, 3, 1, 0, 7, 128,
+ 1, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 1, 0, 8, 128, 0, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 44, 1, 0, 0,
+ 64, 0, 0, 0, 75, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 14, 0, 0, 7, 50, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 6, 130, 0, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 7, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 8, 3, 0, 0,
+ 1, 0, 0, 0, 188, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 224, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 176, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 176, 0, 0, 0,
+ 11, 0, 0, 0, 212, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 248, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 132, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 195, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 205, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sRGBShaderMask = { RGBShaderMask, sizeof(RGBShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mul r1, r1, c0.x
+ mul r0, r0.x, r1
+ mov oC0, r0
+
+// approximately 7 instruction slots used (2 texture, 5 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 2
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+mul r1.xyzw, r1.xyzw, cb0[1].xxxx
+mul o0.xyzw, r0.xxxx, r1.xyzw
+ret
+// Approximately 6 instruction slots used
+#endif
+
+const BYTE RGBAShaderMask[] =
+{
+ 68, 88, 66, 67, 195, 236,
+ 129, 118, 244, 48, 247, 117,
+ 155, 208, 5, 31, 9, 224,
+ 75, 19, 1, 0, 0, 0,
+ 100, 6, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 24, 1, 0, 0, 52, 2,
+ 0, 0, 176, 2, 0, 0,
+ 192, 5, 0, 0, 48, 6,
+ 0, 0, 65, 111, 110, 57,
+ 216, 0, 0, 0, 216, 0,
+ 0, 0, 0, 2, 255, 255,
+ 160, 0, 0, 0, 56, 0,
+ 0, 0, 1, 0, 44, 0,
+ 0, 0, 56, 0, 0, 0,
+ 56, 0, 2, 0, 36, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 5, 0, 1, 0,
+ 0, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 2, 255, 255, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 7, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 0, 0, 3, 128,
+ 0, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 0, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0, 83, 72,
+ 68, 82, 20, 1, 0, 0,
+ 64, 0, 0, 0, 69, 0,
+ 0, 0, 89, 0, 0, 4,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 90, 0, 0, 3, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 0, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 6, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 8, 3, 0, 0,
+ 1, 0, 0, 0, 188, 0,
+ 0, 0, 4, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 224, 2, 0, 0, 156, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 165, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 170, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 176, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 82, 71,
+ 66, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 171, 171, 176, 0, 0, 0,
+ 11, 0, 0, 0, 212, 0,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 220, 1, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 248, 1, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 8, 2, 0, 0, 0, 0,
+ 0, 0, 24, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 40, 2, 0, 0, 0, 0,
+ 0, 0, 56, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 0, 0, 0, 0,
+ 72, 2, 0, 0, 0, 0,
+ 0, 0, 88, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 120, 2, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 132, 2, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 232, 1, 0, 0, 0, 0,
+ 0, 0, 152, 2, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 184, 2, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 195, 2, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 168, 2, 0, 0, 0, 0,
+ 0, 0, 205, 2, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 104, 0, 0, 0, 3, 0,
+ 0, 0, 8, 0, 0, 0,
+ 80, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 92, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 92, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sRGBAShaderMask = { RGBAShaderMask, sizeof(RGBAShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+// c1 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t1
+// s1 s0 t2
+// s2 s0 t3
+// s3 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ dcl_2d s3
+ mov r0.w, c4.z
+ texld r1, t0, s1
+ texld r2, t0, s0
+ add r2.x, r2.x, c4.x
+ add r2.y, r1.x, c4.y
+ rcp r2.w, t1.z
+ mul r1.xy, r2.w, t1
+ texld r3, t0, s2
+ texld r1, r1, s3
+ add r2.z, r3.x, c4.y
+ dp3 r0.x, c1, r2
+ dp3 r0.y, c2, r2
+ dp3 r0.z, c3, r2
+ mul r0, r0, c0.x
+ mul r0, r1.x, r0
+ mov oC0, r0
+
+// approximately 16 instruction slots used (4 texture, 12 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 3
+mov r0.w, l(1.000000)
+sample r1.xyzw, v1.xyxx, t1.xyzw, s0
+add r1.x, r1.x, l(-0.062750)
+sample r2.xyzw, v1.xyxx, t2.xyzw, s0
+add r1.y, r2.x, l(-0.501960)
+sample r2.xyzw, v1.xyxx, t3.xyzw, s0
+add r1.z, r2.x, l(-0.501960)
+dp3 r0.x, cb0[3].xyzx, r1.xyzx
+dp3 r0.y, cb0[4].xyzx, r1.xyzx
+dp3 r0.z, cb0[5].xyzx, r1.xyzx
+mul r0.xyzw, r0.xyzw, cb0[1].xxxx
+div r1.xy, v2.xyxx, v2.zzzz
+sample r1.xyzw, r1.xyxx, t5.xyzw, s0
+mul o0.xyzw, r0.xyzw, r1.xxxx
+ret
+// Approximately 15 instruction slots used
+#endif
+
+const BYTE YCbCrShaderMask[] =
+{
+ 68, 88, 66, 67, 239, 174,
+ 189, 163, 31, 16, 244, 108,
+ 86, 227, 23, 8, 28, 147,
+ 43, 62, 1, 0, 0, 0,
+ 168, 8, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 232, 1, 0, 0, 52, 4,
+ 0, 0, 176, 4, 0, 0,
+ 4, 8, 0, 0, 116, 8,
+ 0, 0, 65, 111, 110, 57,
+ 168, 1, 0, 0, 168, 1,
+ 0, 0, 0, 2, 255, 255,
+ 92, 1, 0, 0, 76, 0,
+ 0, 0, 2, 0, 52, 0,
+ 0, 0, 76, 0, 0, 0,
+ 76, 0, 4, 0, 36, 0,
+ 0, 0, 76, 0, 1, 0,
+ 0, 0, 2, 0, 1, 0,
+ 3, 0, 2, 0, 5, 0,
+ 3, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 1, 2, 255, 255,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 18, 131, 128, 189,
+ 115, 128, 0, 191, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 3, 8, 15, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 170, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 2, 0, 0, 3,
+ 2, 0, 1, 128, 2, 0,
+ 0, 128, 4, 0, 0, 160,
+ 2, 0, 0, 3, 2, 0,
+ 2, 128, 1, 0, 0, 128,
+ 4, 0, 85, 160, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 1, 0, 170, 176, 5, 0,
+ 0, 3, 1, 0, 3, 128,
+ 2, 0, 255, 128, 1, 0,
+ 228, 176, 66, 0, 0, 3,
+ 3, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 3, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 4, 128,
+ 3, 0, 0, 128, 4, 0,
+ 85, 160, 8, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 228, 160, 2, 0, 228, 128,
+ 8, 0, 0, 3, 0, 0,
+ 2, 128, 2, 0, 228, 160,
+ 2, 0, 228, 128, 8, 0,
+ 0, 3, 0, 0, 4, 128,
+ 3, 0, 228, 160, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 1, 0, 0, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 68, 2, 0, 0, 64, 0,
+ 0, 0, 145, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 90, 0,
+ 0, 3, 0, 96, 16, 0,
+ 0, 0, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 1, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 2, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 3, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 1, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 18, 0,
+ 16, 0, 1, 0, 0, 0,
+ 10, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 18, 131, 128, 189, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 2, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 34, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 69, 0, 0, 9,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 3, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 66, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 115, 128, 0, 191,
+ 16, 0, 0, 8, 18, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 16, 0, 0, 8,
+ 34, 0, 16, 0, 0, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 16, 0,
+ 0, 8, 66, 0, 16, 0,
+ 0, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 14, 0, 0, 7,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 70, 16, 16, 0,
+ 2, 0, 0, 0, 166, 26,
+ 16, 0, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 0, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 5, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 15, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 76, 3,
+ 0, 0, 1, 0, 0, 0,
+ 0, 1, 0, 0, 6, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 36, 3, 0, 0,
+ 220, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 229, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 232, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 236, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 3, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 240, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 5, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 246, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 83, 97, 109, 112, 108,
+ 101, 114, 0, 116, 89, 0,
+ 116, 67, 98, 0, 116, 67,
+ 114, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 246, 0, 0, 0, 11, 0,
+ 0, 0, 24, 1, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 60, 2, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 76, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 108, 2,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 2, 0, 0, 0, 140, 2,
+ 0, 0, 0, 0, 0, 0,
+ 156, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 188, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 200, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 44, 2,
+ 0, 0, 0, 0, 0, 0,
+ 220, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 7, 3, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 236, 2,
+ 0, 0, 0, 0, 0, 0,
+ 17, 3, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 172, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 7, 7, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 44, 0, 0, 0,
+ 1, 0, 0, 0, 8, 0,
+ 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sYCbCrShaderMask = { YCbCrShaderMask, sizeof(YCbCrShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44 [unused]
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tRGBWhite texture float4 2d t4 1
+// tMask texture float4 2d t5 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+// SV_Target 1 xyzw 1 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 1 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t4
+// s2 s0 t5
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s2
+ mul r0.x, r0.x, c0.x
+ texld r1, t0, s0
+ texld r2, t0, s1
+ add r2, r1, -r2
+ add r2, r2, c1.x
+ mov r1.w, r2.y
+ mul r2, r0.x, r2
+ mul r0, r0.x, r1
+ mov oC0, r0
+ mov oC1, r2
+
+// approximately 13 instruction slots used (3 texture, 10 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[2], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t4
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_output o1.xyzw
+dcl_temps 3
+div r0.xy, v2.xyxx, v2.zzzz
+sample r0.xyzw, r0.xyxx, t5.xyzw, s0
+mul r0.x, r0.x, cb0[1].x
+sample r1.xyzw, v1.xyxx, t4.xyzw, s0
+sample r2.xyzw, v1.xyxx, t0.xyzw, s0
+add r1.xyzw, -r1.xyzw, r2.xyzw
+add r1.xyzw, r1.xyzw, l(1.000000, 1.000000, 1.000000, 1.000000)
+mov r2.w, r1.y
+mul o1.xyzw, r0.xxxx, r1.xyzw
+mul o0.xyzw, r0.xxxx, r2.xyzw
+ret
+// Approximately 11 instruction slots used
+#endif
+
+const BYTE ComponentAlphaShaderMask[] =
+{
+ 68, 88, 66, 67, 53, 1,
+ 100, 182, 2, 181, 247, 136,
+ 91, 215, 208, 183, 243, 6,
+ 78, 16, 1, 0, 0, 0,
+ 220, 7, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 152, 1, 0, 0, 108, 3,
+ 0, 0, 232, 3, 0, 0,
+ 32, 7, 0, 0, 144, 7,
+ 0, 0, 65, 111, 110, 57,
+ 88, 1, 0, 0, 88, 1,
+ 0, 0, 0, 2, 255, 255,
+ 28, 1, 0, 0, 60, 0,
+ 0, 0, 1, 0, 48, 0,
+ 0, 0, 60, 0, 0, 0,
+ 60, 0, 3, 0, 36, 0,
+ 0, 0, 60, 0, 0, 0,
+ 0, 0, 4, 0, 1, 0,
+ 5, 0, 2, 0, 0, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 2, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 0, 0,
+ 0, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 66, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 2, 0,
+ 0, 3, 2, 0, 15, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 129, 2, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 128, 1, 0, 0, 160,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 2, 0,
+ 15, 128, 0, 0, 0, 128,
+ 2, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 8, 15, 128, 2, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 204, 1,
+ 0, 0, 64, 0, 0, 0,
+ 115, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 4, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 5, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 1, 0,
+ 0, 0, 104, 0, 0, 2,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 18, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 4, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 2, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 2, 0, 0, 0,
+ 0, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 2, 0, 0, 0, 26, 0,
+ 16, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 32,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 32, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 11, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 48, 3, 0, 0,
+ 1, 0, 0, 0, 228, 0,
+ 0, 0, 5, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 255, 255, 0, 1, 0, 0,
+ 8, 3, 0, 0, 188, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 197, 0, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 0, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 202, 0, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 4, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 212, 0,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 218, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 83, 97, 109,
+ 112, 108, 101, 114, 0, 116,
+ 82, 71, 66, 0, 116, 82,
+ 71, 66, 87, 104, 105, 116,
+ 101, 0, 116, 77, 97, 115,
+ 107, 0, 36, 71, 108, 111,
+ 98, 97, 108, 115, 0, 171,
+ 218, 0, 0, 0, 11, 0,
+ 0, 0, 252, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 2, 0, 0, 0, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 16, 0,
+ 0, 0, 4, 0, 0, 0,
+ 2, 0, 0, 0, 48, 2,
+ 0, 0, 0, 0, 0, 0,
+ 64, 2, 0, 0, 32, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 80, 2,
+ 0, 0, 0, 0, 0, 0,
+ 96, 2, 0, 0, 48, 0,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 112, 2,
+ 0, 0, 0, 0, 0, 0,
+ 128, 2, 0, 0, 96, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 160, 2, 0, 0, 160, 0,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 172, 2, 0, 0, 224, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 192, 2, 0, 0, 240, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 224, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 235, 2, 0, 0, 16, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 208, 2,
+ 0, 0, 0, 0, 0, 0,
+ 245, 2, 0, 0, 32, 1,
+ 0, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 144, 2,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 105, 66, 108, 101, 110, 100,
+ 67, 111, 110, 102, 105, 103,
+ 0, 171, 171, 171, 1, 0,
+ 19, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 89, 117, 118,
+ 67, 111, 108, 111, 114, 77,
+ 97, 116, 114, 105, 120, 0,
+ 2, 0, 3, 0, 3, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 82, 101, 110, 100, 101,
+ 114, 84, 97, 114, 103, 101,
+ 116, 79, 102, 102, 115, 101,
+ 116, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 104, 0,
+ 0, 0, 3, 0, 0, 0,
+ 8, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 92, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 3, 0, 0, 92, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 2, 0, 0, 0,
+ 7, 7, 0, 0, 83, 86,
+ 95, 80, 111, 115, 105, 116,
+ 105, 111, 110, 0, 84, 69,
+ 88, 67, 79, 79, 82, 68,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 68, 0, 0, 0,
+ 2, 0, 0, 0, 8, 0,
+ 0, 0, 56, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 56, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 15, 0,
+ 0, 0, 83, 86, 95, 84,
+ 97, 114, 103, 101, 116, 0,
+ 171, 171
+};
+ShaderBytes sComponentAlphaShaderMask = { ComponentAlphaShaderMask, sizeof(ComponentAlphaShaderMask) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 8 ( FLT, FLT, FLT, FLT)
+// c11 cb0 12 2 ( FLT, FLT, FLT, FLT)
+// c13 cb0 15 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c14, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c9.zwzw, c9
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c2
+ mad r0, c1, r0.x, r1
+ add r0, r0, c3
+ rcp r1.x, r0.w
+ mul r0.xyz, r0, r1.x
+ add r0, r0, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ add r1.xy, r0, c14.x
+ mad r1.y, r1.y, -c14.y, c14.x
+ mul r1.x, r1.x, c14.y
+ mul r1.yz, r1.y, c12.xyxw
+ mad r1.xy, c11.yxzw, r1.x, r1.yzzw
+ add oT0.zw, r1.xyxy, c13.xyyx
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+ mov oT1.xyz, c14.z
+
+// approximately 22 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[16], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o1.zw
+dcl_output o2.xyz
+dcl_temps 2
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r0.xyz, r0.xyzx, r0.wwww
+add r0.xyzw, r0.xyzw, -cb0[8].xyzw
+mul r0.xyz, r0.wwww, r0.xyzx
+mul r1.xyzw, r0.yyyy, cb0[5].xyzw
+mad r1.xyzw, cb0[4].xyzw, r0.xxxx, r1.xyzw
+mad r1.xyzw, cb0[6].xyzw, r0.zzzz, r1.xyzw
+mad r0.xyzw, cb0[7].xyzw, r0.wwww, r1.xyzw
+mov o0.xyzw, r0.xyzw
+add r0.xy, r0.xyxx, l(1.000000, 1.000000, 0.000000, 0.000000)
+mad r0.y, -r0.y, l(0.500000), l(1.000000)
+mul r0.x, r0.x, l(0.500000)
+mul r0.yz, r0.yyyy, cb0[13].xxyx
+mad r0.xy, cb0[12].xyxx, r0.xxxx, r0.yzyy
+add o1.zw, r0.xxxy, cb0[15].xxxy
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+mov o2.xyz, l(0,0,0,0)
+ret
+// Approximately 21 instruction slots used
+#endif
+
+const BYTE LayerQuadBlendVS[] =
+{
+ 68, 88, 66, 67, 36, 1,
+ 251, 17, 122, 90, 56, 20,
+ 13, 210, 38, 20, 162, 170,
+ 120, 203, 1, 0, 0, 0,
+ 56, 9, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 60, 2, 0, 0, 100, 5,
+ 0, 0, 224, 5, 0, 0,
+ 124, 8, 0, 0, 176, 8,
+ 0, 0, 65, 111, 110, 57,
+ 252, 1, 0, 0, 252, 1,
+ 0, 0, 0, 2, 254, 255,
+ 164, 1, 0, 0, 88, 0,
+ 0, 0, 4, 0, 36, 0,
+ 0, 0, 84, 0, 0, 0,
+ 84, 0, 0, 0, 36, 0,
+ 1, 0, 84, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 8, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 12, 0, 2, 0, 11, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 1, 0, 13, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 14, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 10, 0, 238, 160,
+ 10, 0, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 2, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 1, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 0, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 8, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 255, 128, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 85, 128, 5, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 4, 0, 228, 160,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 15, 128, 6, 0,
+ 228, 160, 0, 0, 170, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 7, 0, 228, 160, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 3, 128, 0, 0, 228, 128,
+ 14, 0, 0, 160, 4, 0,
+ 0, 4, 1, 0, 2, 128,
+ 1, 0, 85, 128, 14, 0,
+ 85, 161, 14, 0, 0, 160,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 1, 0, 0, 128,
+ 14, 0, 85, 160, 5, 0,
+ 0, 3, 1, 0, 6, 128,
+ 1, 0, 85, 128, 12, 0,
+ 196, 160, 4, 0, 0, 4,
+ 1, 0, 3, 128, 11, 0,
+ 225, 160, 1, 0, 0, 128,
+ 1, 0, 233, 128, 2, 0,
+ 0, 3, 0, 0, 12, 224,
+ 1, 0, 68, 128, 13, 0,
+ 20, 160, 4, 0, 0, 4,
+ 0, 0, 3, 192, 0, 0,
+ 255, 128, 0, 0, 228, 160,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 12, 192,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 7, 224,
+ 14, 0, 170, 160, 255, 255,
+ 0, 0, 83, 72, 68, 82,
+ 32, 3, 0, 0, 64, 0,
+ 1, 0, 200, 0, 0, 0,
+ 89, 0, 0, 4, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 16, 0, 0, 0, 95, 0,
+ 0, 3, 50, 16, 16, 0,
+ 0, 0, 0, 0, 103, 0,
+ 0, 4, 242, 32, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 50, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 101, 0, 0, 3,
+ 114, 32, 16, 0, 2, 0,
+ 0, 0, 104, 0, 0, 2,
+ 2, 0, 0, 0, 50, 0,
+ 0, 11, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 10, 0, 0, 0,
+ 56, 0, 0, 8, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 86, 5, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 0, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 1, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 10,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 0, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 50, 0, 0, 10, 34, 0,
+ 16, 0, 0, 0, 0, 0,
+ 26, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 18, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 56, 0, 0, 8,
+ 98, 0, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
+ 0, 0, 0, 0, 6, 129,
+ 32, 0, 0, 0, 0, 0,
+ 13, 0, 0, 0, 50, 0,
+ 0, 10, 50, 0, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 6, 0,
+ 16, 0, 0, 0, 0, 0,
+ 150, 5, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 6, 4, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 54, 0, 0, 8, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 21, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 18, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 128, 0, 0, 0,
+ 4, 0, 0, 0, 8, 0,
+ 0, 0, 104, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 116, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 116, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadBlendVS = { LayerQuadBlendVS, sizeof(LayerQuadBlendVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4x4 mLayerTransform; // Offset: 0 Size: 64
+// float4x4 mProjection; // Offset: 64 Size: 64
+// float4 vRenderTargetOffset; // Offset: 128 Size: 16
+// float4 vTextureCoords; // Offset: 144 Size: 16
+// float4 vLayerQuad; // Offset: 160 Size: 16
+// float4 vMaskQuad; // Offset: 176 Size: 16
+// float4x4 mBackdropTransform; // Offset: 192 Size: 64
+// float4 fLayerColor; // Offset: 256 Size: 16 [unused]
+// float fLayerOpacity; // Offset: 272 Size: 4 [unused]
+// uint4 iBlendConfig; // Offset: 288 Size: 16 [unused]
+// row_major float3x3 mYuvColorMatrix;// Offset: 304 Size: 44 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// POSITION 0 xy 0 NONE float xy
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float xyzw
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c3 cb0 3 11 ( FLT, FLT, FLT, FLT)
+// c14 cb0 15 1 ( FLT, FLT, FLT, FLT)
+//
+//
+// Runtime generated constant mappings:
+//
+// Target Reg Constant Description
+// ---------- --------------------------------------------------
+// c0 Vertex Shader position offset
+//
+//
+// Level9 shader bytecode:
+//
+ vs_2_x
+ def c15, 1, 0.5, 0, 0
+ dcl_texcoord v0
+ mov r0.z, c15.x
+ rcp r0.w, c11.z
+ mad r1.xy, v0, c10.zwzw, c10
+ mul r2, r1.y, c2
+ mad r1, c1, r1.x, r2
+ add r1, r1, c3
+ add r2.xy, r1, -c11
+ mul r0.x, r0.w, r2.x
+ rcp r0.w, c11.w
+ mul r0.y, r0.w, r2.y
+ mul oT1.xyz, r0, r1.w
+ mad oT0.xy, v0, c9.zwzw, c9
+ rcp r0.x, r1.w
+ mul r1.xyz, r0.x, r1
+ add r0, r1, -c8
+ mul r0.xyz, r0.w, r0
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mad r1, c6, r0.z, r1
+ mad r0, c7, r0.w, r1
+ add r1.xy, r0, c15.x
+ mad r1.y, r1.y, -c15.y, c15.x
+ mul r1.x, r1.x, c15.y
+ mul r1.yz, r1.y, c13.xyxw
+ mad r1.xy, c12.yxzw, r1.x, r1.yzzw
+ add oT0.zw, r1.xyxy, c14.xyyx
+ mad oPos.xy, r0.w, c0, r0
+ mov oPos.zw, r0
+
+// approximately 28 instruction slots used
+vs_4_0
+dcl_constantbuffer CB0[16], immediateIndexed
+dcl_input v0.xy
+dcl_output_siv o0.xyzw, position
+dcl_output o1.xy
+dcl_output o1.zw
+dcl_output o2.xyz
+dcl_temps 4
+mad r0.xy, v0.xyxx, cb0[10].zwzz, cb0[10].xyxx
+mul r1.xyzw, r0.yyyy, cb0[1].xyzw
+mad r0.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw
+add r0.xyzw, r0.xyzw, cb0[3].xyzw
+div r1.xyz, r0.xyzx, r0.wwww
+mov r1.w, r0.w
+add r2.xyzw, r1.xyzw, -cb0[8].xyzw
+mul r1.xyz, r2.wwww, r2.xyzx
+mul r3.xyzw, r1.yyyy, cb0[5].xyzw
+mad r3.xyzw, cb0[4].xyzw, r1.xxxx, r3.xyzw
+mad r3.xyzw, cb0[6].xyzw, r1.zzzz, r3.xyzw
+mad r2.xyzw, cb0[7].xyzw, r2.wwww, r3.xyzw
+mov o0.xyzw, r2.xyzw
+add r0.zw, r2.xxxy, l(0.000000, 0.000000, 1.000000, 1.000000)
+mad r0.w, -r0.w, l(0.500000), l(1.000000)
+mul r0.z, r0.z, l(0.500000)
+mul r1.xy, r0.wwww, cb0[13].xyxx
+mad r0.zw, cb0[12].xxxy, r0.zzzz, r1.xxxy
+add o1.zw, r0.zzzw, cb0[15].xxxy
+mad o1.xy, v0.xyxx, cb0[9].zwzz, cb0[9].xyxx
+add r0.xy, r0.xyxx, -cb0[11].xyxx
+div r0.xy, r0.xyxx, cb0[11].zwzz
+mov r0.z, l(1.000000)
+mul o2.xyz, r1.wwww, r0.xyzx
+ret
+// Approximately 25 instruction slots used
+#endif
+
+const BYTE LayerQuadBlendMaskVS[] =
+{
+ 68, 88, 66, 67, 206, 205,
+ 172, 45, 15, 157, 207, 85,
+ 247, 28, 223, 137, 10, 58,
+ 17, 237, 1, 0, 0, 0,
+ 236, 9, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 136, 2, 0, 0, 24, 6,
+ 0, 0, 148, 6, 0, 0,
+ 48, 9, 0, 0, 100, 9,
+ 0, 0, 65, 111, 110, 57,
+ 72, 2, 0, 0, 72, 2,
+ 0, 0, 0, 2, 254, 255,
+ 252, 1, 0, 0, 76, 0,
+ 0, 0, 3, 0, 36, 0,
+ 0, 0, 72, 0, 0, 0,
+ 72, 0, 0, 0, 36, 0,
+ 1, 0, 72, 0, 0, 0,
+ 0, 0, 2, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 11, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 1, 0, 14, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 254, 255,
+ 81, 0, 0, 5, 15, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 0, 0, 15, 144,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 15, 0, 0, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 11, 0, 170, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 0, 0, 228, 144,
+ 10, 0, 238, 160, 10, 0,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 15, 128, 1, 0,
+ 85, 128, 2, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 1, 0, 228, 160,
+ 1, 0, 0, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 3, 0, 228, 160,
+ 2, 0, 0, 3, 2, 0,
+ 3, 128, 1, 0, 228, 128,
+ 11, 0, 228, 161, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 2, 0,
+ 0, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 11, 0,
+ 255, 160, 5, 0, 0, 3,
+ 0, 0, 2, 128, 0, 0,
+ 255, 128, 2, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 224, 0, 0, 228, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 0, 0, 3, 224,
+ 0, 0, 228, 144, 9, 0,
+ 238, 160, 9, 0, 228, 160,
+ 6, 0, 0, 2, 0, 0,
+ 1, 128, 1, 0, 255, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 128, 8, 0,
+ 228, 161, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 255, 128, 0, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 85, 128,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 1, 0, 15, 128,
+ 4, 0, 228, 160, 0, 0,
+ 0, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 15, 128, 6, 0, 228, 160,
+ 0, 0, 170, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 7, 0,
+ 228, 160, 0, 0, 255, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 3, 128,
+ 0, 0, 228, 128, 15, 0,
+ 0, 160, 4, 0, 0, 4,
+ 1, 0, 2, 128, 1, 0,
+ 85, 128, 15, 0, 85, 161,
+ 15, 0, 0, 160, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 1, 0, 0, 128, 15, 0,
+ 85, 160, 5, 0, 0, 3,
+ 1, 0, 6, 128, 1, 0,
+ 85, 128, 13, 0, 196, 160,
+ 4, 0, 0, 4, 1, 0,
+ 3, 128, 12, 0, 225, 160,
+ 1, 0, 0, 128, 1, 0,
+ 233, 128, 2, 0, 0, 3,
+ 0, 0, 12, 224, 1, 0,
+ 68, 128, 14, 0, 20, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 192, 0, 0, 255, 128,
+ 0, 0, 228, 160, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 12, 192, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 136, 3,
+ 0, 0, 64, 0, 1, 0,
+ 226, 0, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 95, 0, 0, 3,
+ 50, 16, 16, 0, 0, 0,
+ 0, 0, 103, 0, 0, 4,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 50, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 194, 32,
+ 16, 0, 1, 0, 0, 0,
+ 101, 0, 0, 3, 114, 32,
+ 16, 0, 2, 0, 0, 0,
+ 104, 0, 0, 2, 4, 0,
+ 0, 0, 50, 0, 0, 11,
+ 50, 0, 16, 0, 0, 0,
+ 0, 0, 70, 16, 16, 0,
+ 0, 0, 0, 0, 230, 138,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 86, 5,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 242, 0, 16, 0,
+ 0, 0, 0, 0, 70, 14,
+ 16, 0, 0, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 130, 0, 16, 0,
+ 1, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 242, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 70, 142, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 8, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 3, 0, 0, 0, 86, 5,
+ 16, 0, 1, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 50, 0, 0, 10, 242, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 142, 32, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0,
+ 6, 0, 16, 0, 1, 0,
+ 0, 0, 70, 14, 16, 0,
+ 3, 0, 0, 0, 50, 0,
+ 0, 10, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 6, 0, 0, 0, 166, 10,
+ 16, 0, 1, 0, 0, 0,
+ 70, 14, 16, 0, 3, 0,
+ 0, 0, 50, 0, 0, 10,
+ 242, 0, 16, 0, 2, 0,
+ 0, 0, 70, 142, 32, 0,
+ 0, 0, 0, 0, 7, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 54, 0, 0, 5, 242, 32,
+ 16, 0, 0, 0, 0, 0,
+ 70, 14, 16, 0, 2, 0,
+ 0, 0, 0, 0, 0, 10,
+ 194, 0, 16, 0, 0, 0,
+ 0, 0, 6, 4, 16, 0,
+ 2, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 50, 0, 0, 10, 130, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 66, 0, 16, 0,
+ 0, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 63, 56, 0, 0, 8,
+ 50, 0, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 70, 128,
+ 32, 0, 0, 0, 0, 0,
+ 13, 0, 0, 0, 50, 0,
+ 0, 10, 194, 0, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 12, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 6, 4, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 194, 32, 16, 0, 1, 0,
+ 0, 0, 166, 14, 16, 0,
+ 0, 0, 0, 0, 6, 132,
+ 32, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 50, 0,
+ 0, 11, 50, 32, 16, 0,
+ 1, 0, 0, 0, 70, 16,
+ 16, 0, 0, 0, 0, 0,
+ 230, 138, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 70, 128, 32, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 9, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 70, 128, 32, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 11, 0, 0, 0,
+ 14, 0, 0, 8, 50, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 0, 16, 0, 0, 0,
+ 0, 0, 230, 138, 32, 0,
+ 0, 0, 0, 0, 11, 0,
+ 0, 0, 54, 0, 0, 5,
+ 66, 0, 16, 0, 0, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 2, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 62, 0, 0, 1,
+ 83, 84, 65, 84, 116, 0,
+ 0, 0, 25, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 21, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 82, 68,
+ 69, 70, 148, 2, 0, 0,
+ 1, 0, 0, 0, 72, 0,
+ 0, 0, 1, 0, 0, 0,
+ 28, 0, 0, 0, 0, 4,
+ 254, 255, 0, 1, 0, 0,
+ 108, 2, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 36, 71, 108, 111, 98, 97,
+ 108, 115, 0, 171, 171, 171,
+ 60, 0, 0, 0, 11, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 136, 1, 0, 0, 64, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 148, 1, 0, 0, 128, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 184, 1, 0, 0, 144, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 216, 1, 0, 0, 160, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 227, 1, 0, 0, 176, 0,
+ 0, 0, 16, 0, 0, 0,
+ 2, 0, 0, 0, 200, 1,
+ 0, 0, 0, 0, 0, 0,
+ 237, 1, 0, 0, 192, 0,
+ 0, 0, 64, 0, 0, 0,
+ 2, 0, 0, 0, 120, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 168, 1,
+ 0, 0, 0, 0, 0, 0,
+ 12, 2, 0, 0, 16, 1,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 28, 2,
+ 0, 0, 0, 0, 0, 0,
+ 44, 2, 0, 0, 32, 1,
+ 0, 0, 16, 0, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 0, 0, 0, 0,
+ 76, 2, 0, 0, 48, 1,
+ 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 92, 2,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 118, 77, 97,
+ 115, 107, 81, 117, 97, 100,
+ 0, 109, 66, 97, 99, 107,
+ 100, 114, 111, 112, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 105, 66,
+ 108, 101, 110, 100, 67, 111,
+ 110, 102, 105, 103, 0, 171,
+ 171, 171, 1, 0, 19, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 73, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 0, 80, 79,
+ 83, 73, 84, 73, 79, 78,
+ 0, 171, 171, 171, 79, 83,
+ 71, 78, 128, 0, 0, 0,
+ 4, 0, 0, 0, 8, 0,
+ 0, 0, 104, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 15, 0,
+ 0, 0, 116, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 3, 12,
+ 0, 0, 116, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 0, 0, 12, 3,
+ 0, 0, 116, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 3, 0, 0, 0,
+ 2, 0, 0, 0, 7, 8,
+ 0, 0, 83, 86, 95, 80,
+ 111, 115, 105, 116, 105, 111,
+ 110, 0, 84, 69, 88, 67,
+ 79, 79, 82, 68, 0, 171,
+ 171, 171
+};
+ShaderBytes sLayerQuadBlendMaskVS = { LayerQuadBlendMaskVS, sizeof(LayerQuadBlendMaskVS) };
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+//
+// Buffer Definitions:
+//
+// cbuffer $Globals
+// {
+//
+// float4 fLayerColor; // Offset: 0 Size: 16
+// float fLayerOpacity; // Offset: 16 Size: 4
+// uint4 iBlendConfig; // Offset: 32 Size: 16
+// row_major float3x3 mYuvColorMatrix;// Offset: 48 Size: 44
+// float4x4 mLayerTransform; // Offset: 96 Size: 64 [unused]
+// float4x4 mProjection; // Offset: 160 Size: 64 [unused]
+// float4 vRenderTargetOffset; // Offset: 224 Size: 16 [unused]
+// float4 vTextureCoords; // Offset: 240 Size: 16 [unused]
+// float4 vLayerQuad; // Offset: 256 Size: 16 [unused]
+// float4 vMaskQuad; // Offset: 272 Size: 16 [unused]
+// float4x4 mBackdropTransform; // Offset: 288 Size: 64 [unused]
+//
+// }
+//
+//
+// Resource Bindings:
+//
+// Name Type Format Dim HLSL Bind Count
+// ------------------------------ ---------- ------- ----------- -------------- ------
+// sSampler sampler NA NA s0 1
+// tRGB texture float4 2d t0 1
+// tY texture float4 2d t1 1
+// tCb texture float4 2d t2 1
+// tCr texture float4 2d t3 1
+// tMask texture float4 2d t5 1
+// tBackdrop texture float4 2d t6 1
+// $Globals cbuffer NA NA cb0 1
+//
+//
+//
+// Input signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Position 0 xyzw 0 POS float
+// TEXCOORD 0 xy 1 NONE float xy
+// TEXCOORD 2 zw 1 NONE float zw
+// TEXCOORD 1 xyz 2 NONE float xyz
+//
+//
+// Output signature:
+//
+// Name Index Mask Register SysValue Format Used
+// -------------------- ----- ------ -------- -------- ------- ------
+// SV_Target 0 xyzw 0 TARGET float xyzw
+//
+//
+// Constant buffer to DX9 shader constant mappings:
+//
+// Target Reg Buffer Start Reg # of Regs Data Conversion
+// ---------- ------- --------- --------- ----------------------
+// c0 cb0 0 2 ( FLT, FLT, FLT, FLT)
+// c2 cb0 2 1 (UINT,UINT,UINT,UINT)
+// c3 cb0 3 3 ( FLT, FLT, FLT, FLT)
+//
+//
+// Sampler/Resource to DX9 shader sampler mappings:
+//
+// Target Sampler Source Sampler Source Resource
+// -------------- --------------- ----------------
+// s0 s0 t0
+// s1 s0 t1
+// s2 s0 t2
+// s3 s0 t3
+// s4 s0 t5
+// s5 s0 t6
+//
+//
+// Level9 shader bytecode:
+//
+ ps_2_x
+ def c6, -1, -2, -0.0627499968, -0.50195998
+ def c7, -2, -3, -4, -5
+ def c8, -6, -7, -8, -9
+ def c9, 0.5, 1, 0.25, -2
+ def c10, 16, -12, -14, 0
+ def c11, -10, -11, -12, -13
+ def c12, 0.300000012, 0.589999974, 0.109999999, 0
+ def c13, -1, -0, 0, 1
+ dcl t0
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ dcl_2d s3
+ dcl_2d s4
+ dcl_2d s5
+ mov r0.x, c13.z
+ mov r1.x, c13.z
+ mov r2.z, c13.z
+ mov r3.w, -c6.x
+ texld r4, t0, s2
+ texld r5, t0, s1
+ add r5.x, r5.x, c6.z
+ add r5.y, r4.x, c6.w
+ rcp r0.w, t1.z
+ mul r4.xy, r0.w, t1
+ texld r6, t0, s3
+ texld r4, r4, s4
+ add r5.z, r6.x, c6.w
+ dp3 r3.x, c3, r5
+ dp3 r3.y, c4, r5
+ dp3 r3.z, c5, r5
+ mul r3, r3, c1.x
+ mul r5, r4.x, r3
+ mov r6.xy, t0.wzzw
+ texld r7, t0, s0
+ texld r6, r6, s5
+ mul r7, r7, c1.x
+ mul r8, r4.x, r7
+ mov r9.xy, c6
+ add r10, r9.xyxx, c2.xxyz
+ mul r10, r10, r10
+ cmp r5, -r10.x, r8, r5
+ cmp r3, -r10.x, r7, r3
+ mov r7.w, c1.x
+ mul r8, r4.x, r7
+ cmp r3, -c2.x, r7, r3
+ mul r4, r4.x, c0
+ cmp r5, -c2.x, r8, r5
+ cmp r7.xy, -r10.yzzw, c13.x, c13.y
+ cmp r0.w, -r10.x, c6.x, r7.x
+ cmp r1.w, -c2.y, r9.x, r7.y
+ cmp r0.w, -c2.x, r9.x, r0.w
+ cmp r4, r0.w, r4, r5
+ cmp r3, r0.w, c0, r3
+ cmp r3, -c2.y, r3, r4
+ cmp r3, r1.w, c13.zzzw, r3
+ rcp r0.w, r3.w
+ mul r4.xyz, r0.w, r3
+ cmp r4.xyz, -c2.w, r3, r4
+ add r5.xy, -r4.yzzw, r4
+ cmp r5.zw, r5.x, r4.xyxy, r4.xyyx
+ max r0.w, r5.z, r4.z
+ min r1.w, r4.z, r5.w
+ add r7.w, r0.w, -r1.w
+ rcp r0.w, r6.w
+ mul r8.xyz, r0.w, r6
+ mad r5.zw, r6.xyzy, r0.w, -r8.xyxz
+ mul r9.xy, r7.w, r5.zwzw
+ mad r11, r6.yxxz, r0.w, -r8.xzyy
+ rcp r1.w, r11.x
+ mul r7.y, r1.w, r9.x
+ cmp r1.yz, r11.z, c13.z, r7.xwyw
+ mul r12, r7.w, r11
+ rcp r1.w, r5.w
+ mul r7.x, r1.w, r12.y
+ cmp r2.xy, r11.w, c13.z, r7.xwzw
+ cmp r1.xyz, r5.z, r1, r2
+ rcp r1.w, r5.z
+ mul r7.z, r1.w, r12.x
+ cmp r0.yz, r11.y, c13.z, r7.xzww
+ cmp r0.xyz, r11.w, r0, r1
+ mov r1.y, c13.z
+ mov r2.y, c13.z
+ mov r10.z, c13.z
+ rcp r1.w, r11.z
+ mul r7.y, r1.w, r12.w
+ cmp r2.xz, r11.x, c13.z, r7.wyyw
+ rcp r1.w, r11.y
+ mul r7.x, r1.w, r9.y
+ cmp r10.xy, r5.z, c13.z, r7.wxzw
+ cmp r2.xyz, r11.w, r2, r10
+ rcp r1.w, r11.w
+ mul r7.z, r1.w, r12.z
+ cmp r1.xz, r5.w, c13.z, r7.zyww
+ cmp r1.xyz, r5.z, r1, r2
+ cmp r0.xyz, r11.x, r0, r1
+ cmp r1.xy, r11.z, r8, r8.yxzw
+ dp3 r4.w, c12, r0
+ dp3 r8.w, c12, r8
+ add r4.w, -r4.w, r8.w
+ add r0.xyz, r0, r4.w
+ add r4.w, -r0.y, r0.x
+ cmp r1.zw, r4.w, r0.xyyx, r0.xyxy
+ min r4.w, r0.z, r1.z
+ max r2.x, r1.w, r0.z
+ dp3 r1.z, c12, r0
+ add r1.w, -r4.w, r1.z
+ rcp r1.w, r1.w
+ add r2.yzw, r0.xxyz, -r1.z
+ mul r2.yzw, r1.z, r2
+ mad r2.yzw, r2, r1.w, r1.z
+ cmp r0.xyz, r4.w, r0, r2.yzww
+ add r2.yzw, -r1.z, r0.xxyz
+ add r1.w, -r1.z, -c6.x
+ mul r2.yzw, r1.w, r2
+ add r1.w, -r1.z, r2.x
+ add r4.w, -r2.x, -c6.x
+ rcp r1.w, r1.w
+ mad r2.xyz, r2.yzww, r1.w, r1.z
+ cmp r0.xyz, r4.w, r0, r2
+ mov r4.w, c2.z
+ add r1.z, r4.w, c10.z
+ mul r1.z, r1.z, r1.z
+ dp3 r1.w, c12, r4
+ add r2.x, -r8.w, r1.w
+ add r1.w, -r1.w, r8.w
+ add r2.yzw, r1.w, r4.xxyz
+ mad r7.xyz, r6, r0.w, r2.x
+ add r1.w, -r7.y, r7.x
+ cmp r5.zw, r1.w, r7.xyyx, r7.xyxy
+ min r1.w, r7.z, r5.z
+ max r2.x, r5.w, r7.z
+ dp3 r7.w, c12, r7
+ add r5.z, -r1.w, r7.w
+ rcp r5.z, r5.z
+ add r9.xyz, -r7.w, r7
+ mul r9.xyz, r7.w, r9
+ mad r9.xyz, r9, r5.z, r7.w
+ cmp r7.xyz, r1.w, r7, r9
+ add r9.xyz, -r7.w, r7
+ add r1.w, -r7.w, -c6.x
+ mul r9.xyz, r1.w, r9
+ add r1.w, r2.x, -r7.w
+ add r9.w, -r2.x, -c6.x
+ rcp r1.w, r1.w
+ mad r9.xyz, r9, r1.w, r7.w
+ cmp r7.xyz, r9.w, r7, r9
+ cmp r7.xyz, -r1.z, r7, c13.z
+ add r7.w, -r2.z, r2.y
+ cmp r1.zw, r7.w, r2.xyzy, r2.xyyz
+ min r7.w, r2.w, r1.z
+ max r5.z, r1.w, r2.w
+ dp3 r5.w, c12, r2.yzww
+ add r1.z, -r7.w, r5.w
+ rcp r1.z, r1.z
+ add r9.xyz, r2.yzww, -r5.w
+ mul r9.xyz, r5.w, r9
+ mad r9.xyz, r9, r1.z, r5.w
+ cmp r2.xyz, r7.w, r2.yzww, r9
+ add r9.xyz, -r5.w, r2
+ add r2.w, -r5.w, -c6.x
+ mul r9.xyz, r2.w, r9
+ add r2.w, -r5.w, r5.z
+ add r7.w, -r5.z, -c6.x
+ rcp r2.w, r2.w
+ mad r9.xyz, r9, r2.w, r5.w
+ cmp r2.xyz, r7.w, r2, r9
+ add r9, r4.w, c11
+ mul r9, r9, r9
+ cmp r2.xyz, -r9.w, r2, r7
+ cmp r0.xyz, -r9.z, r0, r2
+ add r2, -r4.xxzy, r4.yzxz
+ mov r7.y, c13.z
+ mov r10.y, c13.z
+ mov r11.z, c13.z
+ rcp r7.w, r2.z
+ max r11.w, r1.x, r8.z
+ min r5.z, r8.z, r1.y
+ add r1.w, -r5.z, r11.w
+ mul r5.zw, r1.w, r5.xyxy
+ mul r1.x, r7.w, r5.w
+ cmp r11.xy, r2.y, c13.z, r1.wxzw
+ rcp r5.w, r5.x
+ mul r12, r1.w, r2
+ mul r1.y, r5.w, r12.w
+ cmp r10.xz, r2.x, c13.z, r1.wyyw
+ cmp r10.xyz, r2.w, r10, r11
+ rcp r5.w, r2.w
+ mul r1.z, r5.w, r5.z
+ cmp r7.xz, r5.y, c13.z, r1.zyww
+ cmp r7.xyz, r2.y, r7, r10
+ mov r10.x, c13.z
+ mov r11.x, c13.z
+ mov r13.z, c13.z
+ rcp r7.w, r2.x
+ mul r1.y, r7.w, r12.y
+ cmp r11.yz, r5.x, c13.z, r1.xwyw
+ rcp r7.w, r5.y
+ mul r1.x, r7.w, r12.z
+ cmp r13.xy, r2.w, c13.z, r1.xwzw
+ cmp r5.xyz, r2.y, r11, r13
+ rcp r5.w, r2.y
+ mul r1.z, r5.w, r12.x
+ cmp r10.yz, r2.z, c13.z, r1.xzww
+ cmp r1.xyz, r2.w, r10, r5
+ cmp r1.xyz, r2.x, r1, r7
+ dp3 r1.w, c12, r1
+ add r1.w, -r1.w, r8.w
+ add r1.xyz, r1.w, r1
+ add r1.w, -r1.y, r1.x
+ cmp r2.xy, r1.w, r1.yxzw, r1
+ min r8.w, r1.z, r2.x
+ max r5.x, r2.y, r1.z
+ dp3 r1.w, c12, r1
+ add r2.x, -r8.w, r1.w
+ rcp r2.x, r2.x
+ add r2.yzw, -r1.w, r1.xxyz
+ mul r2.yzw, r1.w, r2
+ mad r2.xyz, r2.yzww, r2.x, r1.w
+ cmp r1.xyz, r8.w, r1, r2
+ add r2.xyz, -r1.w, r1
+ add r2.w, -r1.w, -c6.x
+ mul r2.xyz, r2.w, r2
+ add r2.w, -r1.w, r5.x
+ add r8.w, -r5.x, -c6.x
+ rcp r2.w, r2.w
+ mad r2.xyz, r2, r2.w, r1.w
+ cmp r1.xyz, r8.w, r1, r2
+ cmp r0.xyz, -r9.y, r1, r0
+ mad r1.xyz, r6, r0.w, r4
+ mul r2.xyz, r4, r8
+ mad r5.xyz, r2, c6.y, r1
+ mad r1.xyz, r8, -r4, r1
+ cmp r0.xyz, -r9.x, r5, r0
+ mad r5.xyz, r6, r0.w, -r4
+ abs r5.xyz, r5
+ add r7, r4.w, c8
+ mul r7, r7, r7
+ cmp r0.xyz, -r7.w, r5, r0
+ add r5.xy, -r4.yzzw, c9.x
+ mad r9.xyz, r4, -c9.w, -c9.y
+ mad r1.w, r6.z, -r0.w, c9.z
+ mad r10.xyz, r8, c10.x, c10.y
+ mad r10.xyz, r10, r8, -c7.z
+ mul r10.xyz, r8, r10
+ rsq r2.w, r8.z
+ rcp r2.w, r2.w
+ cmp r1.w, r1.w, r10.z, r2.w
+ mad r1.w, r6.z, -r0.w, r1.w
+ mad r1.w, r9.z, r1.w, r8.z
+ mad r11.xyz, r4, c6.y, -c6.x
+ mul r11.xyz, r8, r11
+ mad r12, r6.yzxy, -r0.w, c9.yyzz
+ mad r5.zw, r11.xyyz, -r12.xyxy, r8.xyyz
+ cmp r13.z, r5.y, r5.w, r1.w
+ rsq r1.w, r8.y
+ rcp r1.w, r1.w
+ cmp r1.w, r12.w, r10.y, r1.w
+ mad r1.w, r6.y, -r0.w, r1.w
+ mad r1.w, r9.y, r1.w, r8.y
+ cmp r13.y, r5.x, r5.z, r1.w
+ add r14, -r4.xyzx, c9.yyyx
+ rsq r1.w, r8.x
+ rcp r1.w, r1.w
+ cmp r1.w, r12.z, r10.x, r1.w
+ mad r1.w, r6.x, -r0.w, r1.w
+ mad r1.w, r9.x, r1.w, r8.x
+ mad r9, r6.xyzx, -r0.w, c9.xxxy
+ mad r6.xyz, r6, r0.w, c6.x
+ mul r6.xyz, r6, r6
+ mad r0.w, r11.x, -r9.w, r8.x
+ cmp r13.x, r14.w, r0.w, r1.w
+ cmp r0.xyz, -r7.z, r13, r0
+ add r10.xyz, r8, r8
+ mad r11.xyz, r4, -c6.y, r10
+ add r11.xyz, r11, c6.x
+ mad r13.xyz, r4, -r10, r11
+ mul r10.xyz, r4, r10
+ add r15.xyz, r4, r4
+ mul r16.xyz, r8, r15
+ mad r11.xyz, r15, -r8, r11
+ cmp r9.xyz, r9, r10, r11
+ cmp r5.yz, r5.xxyw, r16, r13
+ cmp r5.x, r14.w, r16.x, r13.x
+ cmp r0.xyz, -r7.y, r5, r0
+ rcp r0.w, r4.x
+ mad r0.w, r9.w, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ mul r5.xyz, r4, r4
+ cmp r0.w, -r5.x, c13.z, r1.w
+ cmp r10.x, -r6.x, -c6.x, r0.w
+ rcp r0.w, r4.y
+ mad r0.w, r12.x, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ cmp r0.w, -r5.y, c13.z, r1.w
+ cmp r10.y, -r6.y, -c6.x, r0.w
+ rcp r0.w, r4.z
+ mad r0.w, r12.y, -r0.w, -c6.x
+ max r1.w, r0.w, c13.z
+ cmp r0.w, -r5.z, c13.z, r1.w
+ cmp r10.z, -r6.z, -c6.x, r0.w
+ cmp r0.xyz, -r7.x, r10, r0
+ add r5, r4.w, c7
+ mul r5, r5, r5
+ add r6.xyz, r4, c6.x
+ mul r6.xyz, r6, r6
+ rcp r0.w, r14.x
+ mul r0.w, r0.w, r8.x
+ min r1.w, r0.w, -c6.x
+ cmp r0.w, -r6.x, -c6.x, r1.w
+ mul r7.xyz, r8, r8
+ cmp r10.x, -r7.x, c13.z, r0.w
+ rcp r0.w, r14.y
+ rcp r1.w, r14.z
+ mul r1.w, r1.w, r8.z
+ min r2.w, r1.w, -c6.x
+ cmp r1.w, -r6.z, -c6.x, r2.w
+ cmp r10.z, -r7.z, c13.z, r1.w
+ mul r0.w, r0.w, r8.y
+ min r1.w, r0.w, -c6.x
+ cmp r0.w, -r6.y, -c6.x, r1.w
+ cmp r10.y, -r7.y, c13.z, r0.w
+ cmp r0.xyz, -r5.w, r10, r0
+ max r6.xyz, r8, r4
+ min r7.xyz, r4, r8
+ cmp r0.xyz, -r5.z, r6, r0
+ cmp r0.xyz, -r5.y, r7, r0
+ cmp r0.xyz, -r5.x, r9, r0
+ cmp r0.xyz, -r10.w, r1, r0
+ cmp r0.xyz, -c2.z, r2, r0
+ lrp r1.xyz, r6.w, r0, r4
+ mul r1.w, r6.w, r6.w
+ mul r0.xyz, r3.w, r1
+ mul r1.x, r3.w, r3.w
+ mov r0.w, r3.w
+ cmp r0, -r1.x, c13.z, r0
+ cmp r0, -r1.w, r3, r0
+ mov oC0, r0
+
+// approximately 323 instruction slots used (6 texture, 317 arithmetic)
+ps_4_0
+dcl_constantbuffer CB0[6], immediateIndexed
+dcl_sampler s0, mode_default
+dcl_resource_texture2d (float,float,float,float) t0
+dcl_resource_texture2d (float,float,float,float) t1
+dcl_resource_texture2d (float,float,float,float) t2
+dcl_resource_texture2d (float,float,float,float) t3
+dcl_resource_texture2d (float,float,float,float) t5
+dcl_resource_texture2d (float,float,float,float) t6
+dcl_input_ps linear v1.xy
+dcl_input_ps linear v1.zw
+dcl_input_ps linear v2.xyz
+dcl_output o0.xyzw
+dcl_temps 22
+sample r0.xyzw, v1.zwzz, t6.xyzw, s0
+if_z cb0[2].y
+ if_z cb0[2].x
+ sample r1.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r1.xyz, r1.xyzx, cb0[1].xxxx
+ mov r1.w, cb0[1].x
+ mov r2.x, l(-1)
+ else
+ ieq r2.y, l(1), cb0[2].x
+ if_nz r2.y
+ sample r3.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r1.xyzw, r3.xyzw, cb0[1].xxxx
+ mov r2.x, l(-1)
+ else
+ ieq r2.x, l(2), cb0[2].x
+ if_nz r2.x
+ sample r3.xyzw, v1.xyxx, t1.xyzw, s0
+ add r3.x, r3.x, l(-0.062750)
+ sample r4.xyzw, v1.xyxx, t2.xyzw, s0
+ add r3.y, r4.x, l(-0.501960)
+ sample r4.xyzw, v1.xyxx, t3.xyzw, s0
+ add r3.z, r4.x, l(-0.501960)
+ dp3 r4.x, cb0[3].xyzx, r3.xyzx
+ dp3 r4.y, cb0[4].xyzx, r3.xyzx
+ dp3 r4.z, cb0[5].xyzx, r3.xyzx
+ mov r4.w, l(1.000000)
+ mul r1.xyzw, r4.xyzw, cb0[1].xxxx
+ endif
+ endif
+ endif
+ movc r1.xyzw, r2.xxxx, r1.xyzw, cb0[0].xyzw
+ mov r2.x, l(-1)
+else
+ ieq r2.x, l(1), cb0[2].y
+ if_nz r2.x
+ if_z cb0[2].x
+ sample r3.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r3.xyz, r3.xyzx, cb0[1].xxxx
+ div r2.yz, v2.xxyx, v2.zzzz
+ sample r4.xyzw, r2.yzyy, t5.xyzw, s0
+ mov r3.w, cb0[1].x
+ mul r1.xyzw, r3.xyzw, r4.xxxx
+ mov r2.y, l(-1)
+ else
+ ieq r2.z, l(1), cb0[2].x
+ if_nz r2.z
+ div r2.zw, v2.xxxy, v2.zzzz
+ sample r3.xyzw, r2.zwzz, t5.xyzw, s0
+ sample r4.xyzw, v1.xyxx, t0.xyzw, s0
+ mul r4.xyzw, r4.xyzw, cb0[1].xxxx
+ mul r1.xyzw, r3.xxxx, r4.xyzw
+ mov r2.y, l(-1)
+ else
+ ieq r2.y, l(2), cb0[2].x
+ if_nz r2.y
+ div r2.zw, v2.xxxy, v2.zzzz
+ sample r3.xyzw, r2.zwzz, t5.xyzw, s0
+ sample r4.xyzw, v1.xyxx, t1.xyzw, s0
+ add r4.x, r4.x, l(-0.062750)
+ sample r5.xyzw, v1.xyxx, t2.xyzw, s0
+ add r4.y, r5.x, l(-0.501960)
+ sample r5.xyzw, v1.xyxx, t3.xyzw, s0
+ add r4.z, r5.x, l(-0.501960)
+ dp3 r5.x, cb0[3].xyzx, r4.xyzx
+ dp3 r5.y, cb0[4].xyzx, r4.xyzx
+ dp3 r5.z, cb0[5].xyzx, r4.xyzx
+ mov r5.w, l(1.000000)
+ mul r4.xyzw, r5.xyzw, cb0[1].xxxx
+ mul r1.xyzw, r3.xxxx, r4.xyzw
+ endif
+ endif
+ endif
+ if_z r2.y
+ div r2.yz, v2.xxyx, v2.zzzz
+ sample r3.xyzw, r2.yzyy, t5.xyzw, s0
+ mul r1.xyzw, r3.xxxx, cb0[0].xyzw
+ endif
+ endif
+endif
+movc r1.xyzw, r2.xxxx, r1.xyzw, l(0,0,0,1.000000)
+eq r2.x, r0.w, l(0.000000)
+if_nz r2.x
+ mov o0.xyzw, r1.xyzw
+ ret
+endif
+eq r2.x, r1.w, l(0.000000)
+if_nz r2.x
+ mov o0.xyzw, l(0,0,0,0)
+ ret
+endif
+div r0.xyz, r0.xyzx, r0.wwww
+div r2.xyz, r1.xyzx, r1.wwww
+movc r1.xyz, cb0[2].wwww, r2.xyzx, r1.xyzx
+mul r2.xyz, r0.xyzx, r1.xyzx
+add r3.xyz, r0.xyzx, r1.xyzx
+mad r4.xyz, -r0.xyzx, r1.xyzx, r3.xyzx
+ge r5.xyzw, l(0.500000, 0.500000, 0.500000, 0.250000), r0.xyzx
+add r6.xyz, r0.xyzx, r0.xyzx
+mul r7.xyz, r1.xyzx, r6.xyzx
+add r8.xyz, r1.xyzx, r1.xyzx
+mad r9.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r6.xyzx
+add r9.xyz, r9.xyzx, l(-1.000000, -1.000000, -1.000000, 0.000000)
+mul r10.xyz, r0.xyzx, r8.xyzx
+mad r8.xyz, -r8.xyzx, r0.xyzx, r9.xyzx
+movc r5.xyz, r5.xyzx, r7.xyzx, r8.xyzx
+min r7.xyz, r0.xyzx, r1.xyzx
+ieq r8.xyzw, l(1, 2, 3, 4), cb0[2].zzzz
+max r11.xyz, r0.xyzx, r1.xyzx
+eq r12.xyzw, r0.xyzx, l(0.000000, 0.000000, 0.000000, 1.000000)
+eq r13.xyzw, r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+add r14.xyz, -r1.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+div r14.xyz, r0.xyzx, r14.xyzx
+min r14.xyz, r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+movc r13.xyz, r13.xyzx, l(1.000000,1.000000,1.000000,0), r14.xyzx
+movc r12.xyz, r12.xyzx, l(0,0,0,0), r13.xyzx
+add r13.xyz, -r0.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+div r14.xyz, r13.xyzx, r1.xyzx
+min r14.xyz, r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+add r14.xyz, -r14.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000)
+movc r2.w, r13.w, l(0), r14.x
+movc r15.x, r12.w, l(1.000000), r2.w
+eq r14.xw, r0.yyyz, l(1.000000, 0.000000, 0.000000, 1.000000)
+eq r16.xy, r1.yzyy, l(0.000000, 0.000000, 0.000000, 0.000000)
+movc r14.yz, r16.xxyx, l(0,0,0,0), r14.yyzy
+movc r15.yz, r14.xxwx, l(0,1.000000,1.000000,0), r14.yyzy
+ge r14.xyz, l(0.500000, 0.500000, 0.500000, 0.000000), r1.xyzx
+mad r6.xyz, -r1.xyzx, r6.xyzx, r9.xyzx
+movc r6.xyz, r14.xyzx, r10.xyzx, r6.xyzx
+ieq r9.xyzw, l(5, 6, 7, 8), cb0[2].zzzz
+mad r10.xyz, -r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(1.000000, 1.000000, 1.000000, 0.000000)
+mul r10.xyz, r0.xyzx, r10.xyzx
+mad r10.xyz, -r10.xyzx, r13.xyzx, r0.xyzx
+mad r13.xyz, r1.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), l(-1.000000, -1.000000, -1.000000, 0.000000)
+mad r16.xyz, r0.xyzx, l(16.000000, 16.000000, 16.000000, 0.000000), l(-12.000000, -12.000000, -12.000000, 0.000000)
+mad r16.xyz, r16.xyzx, r0.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000)
+mul r16.xyz, r0.xyzx, r16.xyzx
+sqrt r17.xyz, r0.xyzx
+movc r2.w, r5.w, r16.x, r17.x
+add r2.w, -r0.x, r2.w
+mad r2.w, r13.x, r2.w, r0.x
+movc r18.x, r14.x, r10.x, r2.w
+ge r10.xw, l(0.250000, 0.000000, 0.000000, 0.250000), r0.yyyz
+movc r10.xw, r10.xxxw, r16.yyyz, r17.yyyz
+add r10.xw, -r0.yyyz, r10.xxxw
+mad r10.xw, r13.yyyz, r10.xxxw, r0.yyyz
+movc r18.yz, r14.yyzy, r10.yyzy, r10.xxwx
+add r10.xyz, r0.xyzx, -r1.xyzx
+mad r3.xyz, -r2.xyzx, l(2.000000, 2.000000, 2.000000, 0.000000), r3.xyzx
+max r2.w, r0.y, r0.x
+max r2.w, r0.z, r2.w
+min r3.w, r0.y, r0.x
+min r3.w, r0.z, r3.w
+add r13.w, r2.w, -r3.w
+ge r2.w, r1.y, r1.x
+if_nz r2.w
+ lt r14.xyz, r1.xxzx, r1.zyyz
+ add r16.xyzw, -r1.xxzz, r1.yzxy
+ mul r17.xyz, r13.wwww, r16.xyzx
+ div r13.xyz, r17.xyzx, r16.yxwy
+ and r16.yz, r13.xxwx, r14.xxxx
+ ge r14.xw, r1.zzzz, r1.yyyx
+ and r17.yz, r13.wwyw, r14.yyyy
+ and r19.xy, r13.zwzz, r14.zzzz
+ mov r17.x, l(0)
+ mov r19.z, l(0)
+ movc r14.yzw, r14.wwww, r17.xxyz, r19.xxyz
+ mov r16.x, l(0)
+ movc r14.xyz, r14.xxxx, r16.xyzx, r14.yzwy
+else
+ lt r16.xyz, r1.yyzy, r1.zxxz
+ add r17.xyzw, -r1.yyzz, r1.xzyx
+ mul r19.xyz, r13.wwww, r17.xyzx
+ div r13.xyz, r19.xyzx, r17.yxwy
+ and r17.xz, r13.xxwx, r16.xxxx
+ ge r16.xw, r1.zzzz, r1.xxxy
+ and r19.xz, r13.wwyw, r16.yyyy
+ and r13.xy, r13.wzww, r16.zzzz
+ mov r19.y, l(0)
+ mov r13.z, l(0)
+ movc r13.xyz, r16.wwww, r19.xyzx, r13.xyzx
+ mov r17.y, l(0)
+ movc r14.xyz, r16.xxxx, r17.xyzx, r13.xyzx
+endif
+dp3 r2.w, l(0.300000, 0.590000, 0.110000, 0.000000), r0.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r14.xyzx
+add r3.w, r2.w, -r3.w
+add r13.xyz, r3.wwww, r14.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r13.xyzx
+min r4.w, r13.y, r13.x
+min r4.w, r13.z, r4.w
+max r5.w, r13.y, r13.x
+max r5.w, r13.z, r5.w
+lt r6.w, r4.w, l(0.000000)
+add r14.xyz, -r3.wwww, r13.xyzx
+mul r14.xyz, r3.wwww, r14.xyzx
+add r4.w, r3.w, -r4.w
+div r14.xyz, r14.xyzx, r4.wwww
+add r14.xyz, r3.wwww, r14.xyzx
+movc r13.xyz, r6.wwww, r14.xyzx, r13.xyzx
+lt r4.w, l(1.000000), r5.w
+add r14.xyz, -r3.wwww, r13.xyzx
+add r6.w, -r3.w, l(1.000000)
+mul r14.xyz, r6.wwww, r14.xyzx
+add r5.w, -r3.w, r5.w
+div r14.xyz, r14.xyzx, r5.wwww
+add r14.xyz, r3.wwww, r14.xyzx
+movc r13.xyz, r4.wwww, r14.xyzx, r13.xyzx
+ieq r14.xyzw, l(9, 10, 11, 12), cb0[2].zzzz
+max r3.w, r1.y, r1.x
+max r3.w, r1.z, r3.w
+min r4.w, r1.y, r1.x
+min r4.w, r1.z, r4.w
+add r16.w, r3.w, -r4.w
+ge r3.w, r0.y, r0.x
+if_nz r3.w
+ lt r17.xyz, r0.xxzx, r0.zyyz
+ add r19.xyzw, -r0.xxzz, r0.yzxy
+ mul r20.xyz, r16.wwww, r19.xyzx
+ div r16.xyz, r20.xyzx, r19.yxwy
+ and r19.yz, r16.xxwx, r17.xxxx
+ ge r17.xw, r0.zzzz, r0.yyyx
+ and r20.yz, r16.wwyw, r17.yyyy
+ and r21.xy, r16.zwzz, r17.zzzz
+ mov r20.x, l(0)
+ mov r21.z, l(0)
+ movc r17.yzw, r17.wwww, r20.xxyz, r21.xxyz
+ mov r19.x, l(0)
+ movc r17.xyz, r17.xxxx, r19.xyzx, r17.yzwy
+else
+ lt r19.xyz, r0.yyzy, r0.zxxz
+ add r20.xyzw, -r0.yyzz, r0.xzyx
+ mul r21.xyz, r16.wwww, r20.xyzx
+ div r16.xyz, r21.xyzx, r20.yxwy
+ and r20.xz, r16.xxwx, r19.xxxx
+ ge r19.xw, r0.zzzz, r0.xxxy
+ and r21.xz, r16.wwyw, r19.yyyy
+ and r16.xy, r16.wzww, r19.zzzz
+ mov r21.y, l(0)
+ mov r16.z, l(0)
+ movc r16.xyz, r19.wwww, r21.xyzx, r16.xyzx
+ mov r20.y, l(0)
+ movc r17.xyz, r19.xxxx, r20.xyzx, r16.xyzx
+endif
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r17.xyzx
+add r3.w, r2.w, -r3.w
+add r16.xyz, r3.wwww, r17.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r16.xyzx
+min r4.w, r16.y, r16.x
+min r4.w, r16.z, r4.w
+max r5.w, r16.y, r16.x
+max r5.w, r16.z, r5.w
+lt r6.w, r4.w, l(0.000000)
+add r17.xyz, -r3.wwww, r16.xyzx
+mul r17.xyz, r3.wwww, r17.xyzx
+add r4.w, r3.w, -r4.w
+div r17.xyz, r17.xyzx, r4.wwww
+add r17.xyz, r3.wwww, r17.xyzx
+movc r16.xyz, r6.wwww, r17.xyzx, r16.xyzx
+lt r4.w, l(1.000000), r5.w
+add r17.xyz, -r3.wwww, r16.xyzx
+add r6.w, -r3.w, l(1.000000)
+mul r17.xyz, r6.wwww, r17.xyzx
+add r5.w, -r3.w, r5.w
+div r17.xyz, r17.xyzx, r5.wwww
+add r17.xyz, r3.wwww, r17.xyzx
+movc r16.xyz, r4.wwww, r17.xyzx, r16.xyzx
+dp3 r3.w, l(0.300000, 0.590000, 0.110000, 0.000000), r1.xyzx
+add r4.w, r2.w, -r3.w
+add r17.xyz, r1.xyzx, r4.wwww
+dp3 r4.w, l(0.300000, 0.590000, 0.110000, 0.000000), r17.xyzx
+min r5.w, r17.y, r17.x
+min r5.w, r17.z, r5.w
+max r6.w, r17.y, r17.x
+max r6.w, r17.z, r6.w
+lt r7.w, r5.w, l(0.000000)
+add r19.xyz, -r4.wwww, r17.xyzx
+mul r19.xyz, r4.wwww, r19.xyzx
+add r5.w, r4.w, -r5.w
+div r19.xyz, r19.xyzx, r5.wwww
+add r19.xyz, r4.wwww, r19.xyzx
+movc r17.xyz, r7.wwww, r19.xyzx, r17.xyzx
+lt r5.w, l(1.000000), r6.w
+add r19.xyz, -r4.wwww, r17.xyzx
+add r7.w, -r4.w, l(1.000000)
+mul r19.xyz, r7.wwww, r19.xyzx
+add r6.w, -r4.w, r6.w
+div r19.xyz, r19.xyzx, r6.wwww
+add r19.xyz, r4.wwww, r19.xyzx
+movc r17.xyz, r5.wwww, r19.xyzx, r17.xyzx
+ieq r19.xy, l(13, 14, 0, 0), cb0[2].zzzz
+add r2.w, -r2.w, r3.w
+add r0.xyz, r0.xyzx, r2.wwww
+dp3 r2.w, l(0.300000, 0.590000, 0.110000, 0.000000), r0.xyzx
+min r3.w, r0.y, r0.x
+min r3.w, r0.z, r3.w
+max r4.w, r0.y, r0.x
+max r4.w, r0.z, r4.w
+lt r5.w, r3.w, l(0.000000)
+add r20.xyz, r0.xyzx, -r2.wwww
+mul r20.xyz, r2.wwww, r20.xyzx
+add r3.w, r2.w, -r3.w
+div r20.xyz, r20.xyzx, r3.wwww
+add r20.xyz, r2.wwww, r20.xyzx
+movc r0.xyz, r5.wwww, r20.xyzx, r0.xyzx
+lt r3.w, l(1.000000), r4.w
+add r20.xyz, -r2.wwww, r0.xyzx
+add r5.w, -r2.w, l(1.000000)
+mul r20.xyz, r5.wwww, r20.xyzx
+add r4.w, -r2.w, r4.w
+div r20.xyz, r20.xyzx, r4.wwww
+add r20.xyz, r2.wwww, r20.xyzx
+movc r0.xyz, r3.wwww, r20.xyzx, r0.xyzx
+and r0.xyz, r0.xyzx, r19.yyyy
+movc r0.xyz, r19.xxxx, r17.xyzx, r0.xyzx
+movc r0.xyz, r14.wwww, r16.xyzx, r0.xyzx
+movc r0.xyz, r14.zzzz, r13.xyzx, r0.xyzx
+movc r0.xyz, r14.yyyy, r3.xyzx, r0.xyzx
+movc r0.xyz, r14.xxxx, |r10.xyzx|, r0.xyzx
+movc r0.xyz, r9.wwww, r18.xyzx, r0.xyzx
+movc r0.xyz, r9.zzzz, r6.xyzx, r0.xyzx
+movc r0.xyz, r9.yyyy, r15.xyzx, r0.xyzx
+movc r0.xyz, r9.xxxx, r12.xyzx, r0.xyzx
+movc r0.xyz, r8.wwww, r11.xyzx, r0.xyzx
+movc r0.xyz, r8.zzzz, r7.xyzx, r0.xyzx
+movc r0.xyz, r8.yyyy, r5.xyzx, r0.xyzx
+movc r0.xyz, r8.xxxx, r4.xyzx, r0.xyzx
+movc r0.xyz, cb0[2].zzzz, r0.xyzx, r2.xyzx
+add r2.x, -r0.w, l(1.000000)
+mul r0.xyz, r0.xyzx, r0.wwww
+mad r0.xyz, r2.xxxx, r1.xyzx, r0.xyzx
+mul o0.xyz, r1.wwww, r0.xyzx
+mov o0.w, r1.w
+ret
+// Approximately 333 instruction slots used
+#endif
+
+const BYTE BlendShader[] =
+{
+ 68, 88, 66, 67, 28, 114,
+ 244, 41, 206, 5, 116, 244,
+ 79, 130, 118, 154, 72, 188,
+ 36, 32, 1, 0, 0, 0,
+ 172, 66, 0, 0, 6, 0,
+ 0, 0, 56, 0, 0, 0,
+ 16, 23, 0, 0, 208, 61,
+ 0, 0, 76, 62, 0, 0,
+ 240, 65, 0, 0, 120, 66,
+ 0, 0, 65, 111, 110, 57,
+ 208, 22, 0, 0, 208, 22,
+ 0, 0, 0, 2, 255, 255,
+ 112, 22, 0, 0, 96, 0,
+ 0, 0, 3, 0, 60, 0,
+ 0, 0, 96, 0, 0, 0,
+ 96, 0, 6, 0, 36, 0,
+ 0, 0, 96, 0, 0, 0,
+ 0, 0, 1, 0, 1, 0,
+ 2, 0, 2, 0, 3, 0,
+ 3, 0, 5, 0, 4, 0,
+ 6, 0, 5, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 1, 0, 2, 0,
+ 3, 3, 3, 3, 0, 0,
+ 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 1, 2,
+ 255, 255, 81, 0, 0, 5,
+ 6, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 192,
+ 18, 131, 128, 189, 115, 128,
+ 0, 191, 81, 0, 0, 5,
+ 7, 0, 15, 160, 0, 0,
+ 0, 192, 0, 0, 64, 192,
+ 0, 0, 128, 192, 0, 0,
+ 160, 192, 81, 0, 0, 5,
+ 8, 0, 15, 160, 0, 0,
+ 192, 192, 0, 0, 224, 192,
+ 0, 0, 0, 193, 0, 0,
+ 16, 193, 81, 0, 0, 5,
+ 9, 0, 15, 160, 0, 0,
+ 0, 63, 0, 0, 128, 63,
+ 0, 0, 128, 62, 0, 0,
+ 0, 192, 81, 0, 0, 5,
+ 10, 0, 15, 160, 0, 0,
+ 128, 65, 0, 0, 64, 193,
+ 0, 0, 96, 193, 0, 0,
+ 0, 0, 81, 0, 0, 5,
+ 11, 0, 15, 160, 0, 0,
+ 32, 193, 0, 0, 48, 193,
+ 0, 0, 64, 193, 0, 0,
+ 80, 193, 81, 0, 0, 5,
+ 12, 0, 15, 160, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 81, 0, 0, 5,
+ 13, 0, 15, 160, 0, 0,
+ 128, 191, 0, 0, 0, 128,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 3, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 4, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 5, 8,
+ 15, 160, 1, 0, 0, 2,
+ 0, 0, 1, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 1, 0, 1, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 2, 0, 4, 128, 13, 0,
+ 170, 160, 1, 0, 0, 2,
+ 3, 0, 8, 128, 6, 0,
+ 0, 161, 66, 0, 0, 3,
+ 4, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 66, 0, 0, 3, 5, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 2, 0,
+ 0, 3, 5, 0, 1, 128,
+ 5, 0, 0, 128, 6, 0,
+ 170, 160, 2, 0, 0, 3,
+ 5, 0, 2, 128, 4, 0,
+ 0, 128, 6, 0, 255, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 170, 176,
+ 5, 0, 0, 3, 4, 0,
+ 3, 128, 0, 0, 255, 128,
+ 1, 0, 228, 176, 66, 0,
+ 0, 3, 6, 0, 15, 128,
+ 0, 0, 228, 176, 3, 8,
+ 228, 160, 66, 0, 0, 3,
+ 4, 0, 15, 128, 4, 0,
+ 228, 128, 4, 8, 228, 160,
+ 2, 0, 0, 3, 5, 0,
+ 4, 128, 6, 0, 0, 128,
+ 6, 0, 255, 160, 8, 0,
+ 0, 3, 3, 0, 1, 128,
+ 3, 0, 228, 160, 5, 0,
+ 228, 128, 8, 0, 0, 3,
+ 3, 0, 2, 128, 4, 0,
+ 228, 160, 5, 0, 228, 128,
+ 8, 0, 0, 3, 3, 0,
+ 4, 128, 5, 0, 228, 160,
+ 5, 0, 228, 128, 5, 0,
+ 0, 3, 3, 0, 15, 128,
+ 3, 0, 228, 128, 1, 0,
+ 0, 160, 5, 0, 0, 3,
+ 5, 0, 15, 128, 4, 0,
+ 0, 128, 3, 0, 228, 128,
+ 1, 0, 0, 2, 6, 0,
+ 3, 128, 0, 0, 235, 176,
+ 66, 0, 0, 3, 7, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 6, 0, 15, 128,
+ 6, 0, 228, 128, 5, 8,
+ 228, 160, 5, 0, 0, 3,
+ 7, 0, 15, 128, 7, 0,
+ 228, 128, 1, 0, 0, 160,
+ 5, 0, 0, 3, 8, 0,
+ 15, 128, 4, 0, 0, 128,
+ 7, 0, 228, 128, 1, 0,
+ 0, 2, 9, 0, 3, 128,
+ 6, 0, 228, 160, 2, 0,
+ 0, 3, 10, 0, 15, 128,
+ 9, 0, 4, 128, 2, 0,
+ 144, 160, 5, 0, 0, 3,
+ 10, 0, 15, 128, 10, 0,
+ 228, 128, 10, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 15, 128, 10, 0, 0, 129,
+ 8, 0, 228, 128, 5, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 10, 0,
+ 0, 129, 7, 0, 228, 128,
+ 3, 0, 228, 128, 1, 0,
+ 0, 2, 7, 0, 8, 128,
+ 1, 0, 0, 160, 5, 0,
+ 0, 3, 8, 0, 15, 128,
+ 4, 0, 0, 128, 7, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 2, 0,
+ 0, 161, 7, 0, 228, 128,
+ 3, 0, 228, 128, 5, 0,
+ 0, 3, 4, 0, 15, 128,
+ 4, 0, 0, 128, 0, 0,
+ 228, 160, 88, 0, 0, 4,
+ 5, 0, 15, 128, 2, 0,
+ 0, 161, 8, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 7, 0, 3, 128,
+ 10, 0, 233, 129, 13, 0,
+ 0, 160, 13, 0, 85, 160,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 10, 0, 0, 129,
+ 6, 0, 0, 160, 7, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 8, 128, 2, 0,
+ 85, 161, 9, 0, 0, 128,
+ 7, 0, 85, 128, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 2, 0, 0, 161, 9, 0,
+ 0, 128, 0, 0, 255, 128,
+ 88, 0, 0, 4, 4, 0,
+ 15, 128, 0, 0, 255, 128,
+ 4, 0, 228, 128, 5, 0,
+ 228, 128, 88, 0, 0, 4,
+ 3, 0, 15, 128, 0, 0,
+ 255, 128, 0, 0, 228, 160,
+ 3, 0, 228, 128, 88, 0,
+ 0, 4, 3, 0, 15, 128,
+ 2, 0, 85, 161, 3, 0,
+ 228, 128, 4, 0, 228, 128,
+ 88, 0, 0, 4, 3, 0,
+ 15, 128, 1, 0, 255, 128,
+ 13, 0, 234, 160, 3, 0,
+ 228, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 3,
+ 4, 0, 7, 128, 0, 0,
+ 255, 128, 3, 0, 228, 128,
+ 88, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 255, 161,
+ 3, 0, 228, 128, 4, 0,
+ 228, 128, 2, 0, 0, 3,
+ 5, 0, 3, 128, 4, 0,
+ 233, 129, 4, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 12, 128, 5, 0, 0, 128,
+ 4, 0, 68, 128, 4, 0,
+ 20, 128, 11, 0, 0, 3,
+ 0, 0, 8, 128, 5, 0,
+ 170, 128, 4, 0, 170, 128,
+ 10, 0, 0, 3, 1, 0,
+ 8, 128, 4, 0, 170, 128,
+ 5, 0, 255, 128, 2, 0,
+ 0, 3, 7, 0, 8, 128,
+ 0, 0, 255, 128, 1, 0,
+ 255, 129, 6, 0, 0, 2,
+ 0, 0, 8, 128, 6, 0,
+ 255, 128, 5, 0, 0, 3,
+ 8, 0, 7, 128, 0, 0,
+ 255, 128, 6, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 12, 128, 6, 0, 100, 128,
+ 0, 0, 255, 128, 8, 0,
+ 132, 129, 5, 0, 0, 3,
+ 9, 0, 3, 128, 7, 0,
+ 255, 128, 5, 0, 238, 128,
+ 4, 0, 0, 4, 11, 0,
+ 15, 128, 6, 0, 129, 128,
+ 0, 0, 255, 128, 8, 0,
+ 88, 129, 6, 0, 0, 2,
+ 1, 0, 8, 128, 11, 0,
+ 0, 128, 5, 0, 0, 3,
+ 7, 0, 2, 128, 1, 0,
+ 255, 128, 9, 0, 0, 128,
+ 88, 0, 0, 4, 1, 0,
+ 6, 128, 11, 0, 170, 128,
+ 13, 0, 170, 160, 7, 0,
+ 220, 128, 5, 0, 0, 3,
+ 12, 0, 15, 128, 7, 0,
+ 255, 128, 11, 0, 228, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 5, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 1, 128, 1, 0, 255, 128,
+ 12, 0, 85, 128, 88, 0,
+ 0, 4, 2, 0, 3, 128,
+ 11, 0, 255, 128, 13, 0,
+ 170, 160, 7, 0, 236, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 5, 0, 170, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 6, 0, 0, 2,
+ 1, 0, 8, 128, 5, 0,
+ 170, 128, 5, 0, 0, 3,
+ 7, 0, 4, 128, 1, 0,
+ 255, 128, 12, 0, 0, 128,
+ 88, 0, 0, 4, 0, 0,
+ 6, 128, 11, 0, 85, 128,
+ 13, 0, 170, 160, 7, 0,
+ 248, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 11, 0,
+ 255, 128, 0, 0, 228, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 2, 128,
+ 13, 0, 170, 160, 1, 0,
+ 0, 2, 2, 0, 2, 128,
+ 13, 0, 170, 160, 1, 0,
+ 0, 2, 10, 0, 4, 128,
+ 13, 0, 170, 160, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 11, 0, 170, 128, 5, 0,
+ 0, 3, 7, 0, 2, 128,
+ 1, 0, 255, 128, 12, 0,
+ 255, 128, 88, 0, 0, 4,
+ 2, 0, 5, 128, 11, 0,
+ 0, 128, 13, 0, 170, 160,
+ 7, 0, 215, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 11, 0, 85, 128, 5, 0,
+ 0, 3, 7, 0, 1, 128,
+ 1, 0, 255, 128, 9, 0,
+ 85, 128, 88, 0, 0, 4,
+ 10, 0, 3, 128, 5, 0,
+ 170, 128, 13, 0, 170, 160,
+ 7, 0, 227, 128, 88, 0,
+ 0, 4, 2, 0, 7, 128,
+ 11, 0, 255, 128, 2, 0,
+ 228, 128, 10, 0, 228, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 11, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 4, 128, 1, 0, 255, 128,
+ 12, 0, 170, 128, 88, 0,
+ 0, 4, 1, 0, 5, 128,
+ 5, 0, 255, 128, 13, 0,
+ 170, 160, 7, 0, 246, 128,
+ 88, 0, 0, 4, 1, 0,
+ 7, 128, 5, 0, 170, 128,
+ 1, 0, 228, 128, 2, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 11, 0,
+ 0, 128, 0, 0, 228, 128,
+ 1, 0, 228, 128, 88, 0,
+ 0, 4, 1, 0, 3, 128,
+ 11, 0, 170, 128, 8, 0,
+ 228, 128, 8, 0, 225, 128,
+ 8, 0, 0, 3, 4, 0,
+ 8, 128, 12, 0, 228, 160,
+ 0, 0, 228, 128, 8, 0,
+ 0, 3, 8, 0, 8, 128,
+ 12, 0, 228, 160, 8, 0,
+ 228, 128, 2, 0, 0, 3,
+ 4, 0, 8, 128, 4, 0,
+ 255, 129, 8, 0, 255, 128,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 4, 0, 255, 128, 2, 0,
+ 0, 3, 4, 0, 8, 128,
+ 0, 0, 85, 129, 0, 0,
+ 0, 128, 88, 0, 0, 4,
+ 1, 0, 12, 128, 4, 0,
+ 255, 128, 0, 0, 20, 128,
+ 0, 0, 68, 128, 10, 0,
+ 0, 3, 4, 0, 8, 128,
+ 0, 0, 170, 128, 1, 0,
+ 170, 128, 11, 0, 0, 3,
+ 2, 0, 1, 128, 1, 0,
+ 255, 128, 0, 0, 170, 128,
+ 8, 0, 0, 3, 1, 0,
+ 4, 128, 12, 0, 228, 160,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 4, 0, 255, 129, 1, 0,
+ 170, 128, 6, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 2, 0, 0, 3,
+ 2, 0, 14, 128, 0, 0,
+ 144, 128, 1, 0, 170, 129,
+ 5, 0, 0, 3, 2, 0,
+ 14, 128, 1, 0, 170, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 4, 2, 0, 14, 128,
+ 2, 0, 228, 128, 1, 0,
+ 255, 128, 1, 0, 170, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 4, 0, 255, 128,
+ 0, 0, 228, 128, 2, 0,
+ 249, 128, 2, 0, 0, 3,
+ 2, 0, 14, 128, 1, 0,
+ 170, 129, 0, 0, 144, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 170, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 1, 0,
+ 170, 129, 2, 0, 0, 128,
+ 2, 0, 0, 3, 4, 0,
+ 8, 128, 2, 0, 0, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 2, 0, 7, 128,
+ 2, 0, 249, 128, 1, 0,
+ 255, 128, 1, 0, 170, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 4, 0, 255, 128,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 4, 0, 8, 128, 2, 0,
+ 170, 160, 2, 0, 0, 3,
+ 1, 0, 4, 128, 4, 0,
+ 255, 128, 10, 0, 170, 160,
+ 5, 0, 0, 3, 1, 0,
+ 4, 128, 1, 0, 170, 128,
+ 1, 0, 170, 128, 8, 0,
+ 0, 3, 1, 0, 8, 128,
+ 12, 0, 228, 160, 4, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 1, 128, 8, 0,
+ 255, 129, 1, 0, 255, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 129,
+ 8, 0, 255, 128, 2, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 128, 4, 0,
+ 144, 128, 4, 0, 0, 4,
+ 7, 0, 7, 128, 6, 0,
+ 228, 128, 0, 0, 255, 128,
+ 2, 0, 0, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 7, 0, 85, 129, 7, 0,
+ 0, 128, 88, 0, 0, 4,
+ 5, 0, 12, 128, 1, 0,
+ 255, 128, 7, 0, 20, 128,
+ 7, 0, 68, 128, 10, 0,
+ 0, 3, 1, 0, 8, 128,
+ 7, 0, 170, 128, 5, 0,
+ 170, 128, 11, 0, 0, 3,
+ 2, 0, 1, 128, 5, 0,
+ 255, 128, 7, 0, 170, 128,
+ 8, 0, 0, 3, 7, 0,
+ 8, 128, 12, 0, 228, 160,
+ 7, 0, 228, 128, 2, 0,
+ 0, 3, 5, 0, 4, 128,
+ 1, 0, 255, 129, 7, 0,
+ 255, 128, 6, 0, 0, 2,
+ 5, 0, 4, 128, 5, 0,
+ 170, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 7, 0,
+ 255, 129, 7, 0, 228, 128,
+ 5, 0, 0, 3, 9, 0,
+ 7, 128, 7, 0, 255, 128,
+ 9, 0, 228, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 5, 0,
+ 170, 128, 7, 0, 255, 128,
+ 88, 0, 0, 4, 7, 0,
+ 7, 128, 1, 0, 255, 128,
+ 7, 0, 228, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 7, 0,
+ 255, 129, 7, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 7, 0, 255, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 9, 0, 7, 128,
+ 1, 0, 255, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 2, 0,
+ 0, 128, 7, 0, 255, 129,
+ 2, 0, 0, 3, 9, 0,
+ 8, 128, 2, 0, 0, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 1, 0,
+ 255, 128, 7, 0, 255, 128,
+ 88, 0, 0, 4, 7, 0,
+ 7, 128, 9, 0, 255, 128,
+ 7, 0, 228, 128, 9, 0,
+ 228, 128, 88, 0, 0, 4,
+ 7, 0, 7, 128, 1, 0,
+ 170, 129, 7, 0, 228, 128,
+ 13, 0, 170, 160, 2, 0,
+ 0, 3, 7, 0, 8, 128,
+ 2, 0, 170, 129, 2, 0,
+ 85, 128, 88, 0, 0, 4,
+ 1, 0, 12, 128, 7, 0,
+ 255, 128, 2, 0, 100, 128,
+ 2, 0, 148, 128, 10, 0,
+ 0, 3, 7, 0, 8, 128,
+ 2, 0, 255, 128, 1, 0,
+ 170, 128, 11, 0, 0, 3,
+ 5, 0, 4, 128, 1, 0,
+ 255, 128, 2, 0, 255, 128,
+ 8, 0, 0, 3, 5, 0,
+ 8, 128, 12, 0, 228, 160,
+ 2, 0, 249, 128, 2, 0,
+ 0, 3, 1, 0, 4, 128,
+ 7, 0, 255, 129, 5, 0,
+ 255, 128, 6, 0, 0, 2,
+ 1, 0, 4, 128, 1, 0,
+ 170, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 2, 0,
+ 249, 128, 5, 0, 255, 129,
+ 5, 0, 0, 3, 9, 0,
+ 7, 128, 5, 0, 255, 128,
+ 9, 0, 228, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 1, 0,
+ 170, 128, 5, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 7, 0, 255, 128,
+ 2, 0, 249, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 7, 128, 5, 0,
+ 255, 129, 2, 0, 228, 128,
+ 2, 0, 0, 3, 2, 0,
+ 8, 128, 5, 0, 255, 129,
+ 6, 0, 0, 161, 5, 0,
+ 0, 3, 9, 0, 7, 128,
+ 2, 0, 255, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 8, 128, 5, 0,
+ 255, 129, 5, 0, 170, 128,
+ 2, 0, 0, 3, 7, 0,
+ 8, 128, 5, 0, 170, 129,
+ 6, 0, 0, 161, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 9, 0, 228, 128, 2, 0,
+ 255, 128, 5, 0, 255, 128,
+ 88, 0, 0, 4, 2, 0,
+ 7, 128, 7, 0, 255, 128,
+ 2, 0, 228, 128, 9, 0,
+ 228, 128, 2, 0, 0, 3,
+ 9, 0, 15, 128, 4, 0,
+ 255, 128, 11, 0, 228, 160,
+ 5, 0, 0, 3, 9, 0,
+ 15, 128, 9, 0, 228, 128,
+ 9, 0, 228, 128, 88, 0,
+ 0, 4, 2, 0, 7, 128,
+ 9, 0, 255, 129, 2, 0,
+ 228, 128, 7, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 9, 0, 170, 129,
+ 0, 0, 228, 128, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 15, 128, 4, 0,
+ 96, 129, 4, 0, 137, 128,
+ 1, 0, 0, 2, 7, 0,
+ 2, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 10, 0,
+ 2, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 11, 0,
+ 4, 128, 13, 0, 170, 160,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 2, 0, 170, 128,
+ 11, 0, 0, 3, 11, 0,
+ 8, 128, 1, 0, 0, 128,
+ 8, 0, 170, 128, 10, 0,
+ 0, 3, 5, 0, 4, 128,
+ 8, 0, 170, 128, 1, 0,
+ 85, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 5, 0,
+ 170, 129, 11, 0, 255, 128,
+ 5, 0, 0, 3, 5, 0,
+ 12, 128, 1, 0, 255, 128,
+ 5, 0, 68, 128, 5, 0,
+ 0, 3, 1, 0, 1, 128,
+ 7, 0, 255, 128, 5, 0,
+ 255, 128, 88, 0, 0, 4,
+ 11, 0, 3, 128, 2, 0,
+ 85, 128, 13, 0, 170, 160,
+ 1, 0, 227, 128, 6, 0,
+ 0, 2, 5, 0, 8, 128,
+ 5, 0, 0, 128, 5, 0,
+ 0, 3, 12, 0, 15, 128,
+ 1, 0, 255, 128, 2, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 2, 128, 5, 0,
+ 255, 128, 12, 0, 255, 128,
+ 88, 0, 0, 4, 10, 0,
+ 5, 128, 2, 0, 0, 128,
+ 13, 0, 170, 160, 1, 0,
+ 215, 128, 88, 0, 0, 4,
+ 10, 0, 7, 128, 2, 0,
+ 255, 128, 10, 0, 228, 128,
+ 11, 0, 228, 128, 6, 0,
+ 0, 2, 5, 0, 8, 128,
+ 2, 0, 255, 128, 5, 0,
+ 0, 3, 1, 0, 4, 128,
+ 5, 0, 255, 128, 5, 0,
+ 170, 128, 88, 0, 0, 4,
+ 7, 0, 5, 128, 5, 0,
+ 85, 128, 13, 0, 170, 160,
+ 1, 0, 246, 128, 88, 0,
+ 0, 4, 7, 0, 7, 128,
+ 2, 0, 85, 128, 7, 0,
+ 228, 128, 10, 0, 228, 128,
+ 1, 0, 0, 2, 10, 0,
+ 1, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 11, 0,
+ 1, 128, 13, 0, 170, 160,
+ 1, 0, 0, 2, 13, 0,
+ 4, 128, 13, 0, 170, 160,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 2, 0, 0, 128,
+ 5, 0, 0, 3, 1, 0,
+ 2, 128, 7, 0, 255, 128,
+ 12, 0, 85, 128, 88, 0,
+ 0, 4, 11, 0, 6, 128,
+ 5, 0, 0, 128, 13, 0,
+ 170, 160, 1, 0, 220, 128,
+ 6, 0, 0, 2, 7, 0,
+ 8, 128, 5, 0, 85, 128,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 7, 0, 255, 128,
+ 12, 0, 170, 128, 88, 0,
+ 0, 4, 13, 0, 3, 128,
+ 2, 0, 255, 128, 13, 0,
+ 170, 160, 1, 0, 236, 128,
+ 88, 0, 0, 4, 5, 0,
+ 7, 128, 2, 0, 85, 128,
+ 11, 0, 228, 128, 13, 0,
+ 228, 128, 6, 0, 0, 2,
+ 5, 0, 8, 128, 2, 0,
+ 85, 128, 5, 0, 0, 3,
+ 1, 0, 4, 128, 5, 0,
+ 255, 128, 12, 0, 0, 128,
+ 88, 0, 0, 4, 10, 0,
+ 6, 128, 2, 0, 170, 128,
+ 13, 0, 170, 160, 1, 0,
+ 248, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 2, 0,
+ 255, 128, 10, 0, 228, 128,
+ 5, 0, 228, 128, 88, 0,
+ 0, 4, 1, 0, 7, 128,
+ 2, 0, 0, 128, 1, 0,
+ 228, 128, 7, 0, 228, 128,
+ 8, 0, 0, 3, 1, 0,
+ 8, 128, 12, 0, 228, 160,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 129, 8, 0,
+ 255, 128, 2, 0, 0, 3,
+ 1, 0, 7, 128, 1, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 85, 129,
+ 1, 0, 0, 128, 88, 0,
+ 0, 4, 2, 0, 3, 128,
+ 1, 0, 255, 128, 1, 0,
+ 225, 128, 1, 0, 228, 128,
+ 10, 0, 0, 3, 8, 0,
+ 8, 128, 1, 0, 170, 128,
+ 2, 0, 0, 128, 11, 0,
+ 0, 3, 5, 0, 1, 128,
+ 2, 0, 85, 128, 1, 0,
+ 170, 128, 8, 0, 0, 3,
+ 1, 0, 8, 128, 12, 0,
+ 228, 160, 1, 0, 228, 128,
+ 2, 0, 0, 3, 2, 0,
+ 1, 128, 8, 0, 255, 129,
+ 1, 0, 255, 128, 6, 0,
+ 0, 2, 2, 0, 1, 128,
+ 2, 0, 0, 128, 2, 0,
+ 0, 3, 2, 0, 14, 128,
+ 1, 0, 255, 129, 1, 0,
+ 144, 128, 5, 0, 0, 3,
+ 2, 0, 14, 128, 1, 0,
+ 255, 128, 2, 0, 228, 128,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 2, 0, 249, 128,
+ 2, 0, 0, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 2, 0, 7, 128,
+ 1, 0, 255, 129, 1, 0,
+ 228, 128, 2, 0, 0, 3,
+ 2, 0, 8, 128, 1, 0,
+ 255, 129, 6, 0, 0, 161,
+ 5, 0, 0, 3, 2, 0,
+ 7, 128, 2, 0, 255, 128,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 129, 5, 0,
+ 0, 128, 2, 0, 0, 3,
+ 8, 0, 8, 128, 5, 0,
+ 0, 129, 6, 0, 0, 161,
+ 6, 0, 0, 2, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 2, 0, 228, 128,
+ 2, 0, 255, 128, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 255, 128, 1, 0, 228, 128,
+ 2, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 9, 0, 85, 129, 1, 0,
+ 228, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 2, 0, 7, 128, 4, 0,
+ 228, 128, 8, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 2, 0, 228, 128,
+ 6, 0, 85, 160, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 228, 128, 4, 0, 228, 129,
+ 1, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 9, 0, 0, 129, 5, 0,
+ 228, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 5, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 4, 0,
+ 228, 129, 35, 0, 0, 2,
+ 5, 0, 7, 128, 5, 0,
+ 228, 128, 2, 0, 0, 3,
+ 7, 0, 15, 128, 4, 0,
+ 255, 128, 8, 0, 228, 160,
+ 5, 0, 0, 3, 7, 0,
+ 15, 128, 7, 0, 228, 128,
+ 7, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 255, 129, 5, 0,
+ 228, 128, 0, 0, 228, 128,
+ 2, 0, 0, 3, 5, 0,
+ 3, 128, 4, 0, 233, 129,
+ 9, 0, 0, 160, 4, 0,
+ 0, 4, 9, 0, 7, 128,
+ 4, 0, 228, 128, 9, 0,
+ 255, 161, 9, 0, 85, 161,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 170, 128,
+ 0, 0, 255, 129, 9, 0,
+ 170, 160, 4, 0, 0, 4,
+ 10, 0, 7, 128, 8, 0,
+ 228, 128, 10, 0, 0, 160,
+ 10, 0, 85, 160, 4, 0,
+ 0, 4, 10, 0, 7, 128,
+ 10, 0, 228, 128, 8, 0,
+ 228, 128, 7, 0, 170, 161,
+ 5, 0, 0, 3, 10, 0,
+ 7, 128, 8, 0, 228, 128,
+ 10, 0, 228, 128, 7, 0,
+ 0, 2, 2, 0, 8, 128,
+ 8, 0, 170, 128, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 1, 0, 255, 128, 10, 0,
+ 170, 128, 2, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 170, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 170, 128, 1, 0, 255, 128,
+ 8, 0, 170, 128, 4, 0,
+ 0, 4, 11, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 85, 160, 6, 0, 0, 161,
+ 5, 0, 0, 3, 11, 0,
+ 7, 128, 8, 0, 228, 128,
+ 11, 0, 228, 128, 4, 0,
+ 0, 4, 12, 0, 15, 128,
+ 6, 0, 73, 128, 0, 0,
+ 255, 129, 9, 0, 165, 160,
+ 4, 0, 0, 4, 5, 0,
+ 12, 128, 11, 0, 148, 128,
+ 12, 0, 68, 129, 8, 0,
+ 148, 128, 88, 0, 0, 4,
+ 13, 0, 4, 128, 5, 0,
+ 85, 128, 5, 0, 255, 128,
+ 1, 0, 255, 128, 7, 0,
+ 0, 2, 1, 0, 8, 128,
+ 8, 0, 85, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 12, 0, 255, 128, 10, 0,
+ 85, 128, 1, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 85, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 85, 128, 1, 0, 255, 128,
+ 8, 0, 85, 128, 88, 0,
+ 0, 4, 13, 0, 2, 128,
+ 5, 0, 0, 128, 5, 0,
+ 170, 128, 1, 0, 255, 128,
+ 2, 0, 0, 3, 14, 0,
+ 15, 128, 4, 0, 36, 129,
+ 9, 0, 21, 160, 7, 0,
+ 0, 2, 1, 0, 8, 128,
+ 8, 0, 0, 128, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 1, 0, 8, 128,
+ 12, 0, 170, 128, 10, 0,
+ 0, 128, 1, 0, 255, 128,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 6, 0, 0, 128,
+ 0, 0, 255, 129, 1, 0,
+ 255, 128, 4, 0, 0, 4,
+ 1, 0, 8, 128, 9, 0,
+ 0, 128, 1, 0, 255, 128,
+ 8, 0, 0, 128, 4, 0,
+ 0, 4, 9, 0, 15, 128,
+ 6, 0, 36, 128, 0, 0,
+ 255, 129, 9, 0, 64, 160,
+ 4, 0, 0, 4, 6, 0,
+ 7, 128, 6, 0, 228, 128,
+ 0, 0, 255, 128, 6, 0,
+ 0, 160, 5, 0, 0, 3,
+ 6, 0, 7, 128, 6, 0,
+ 228, 128, 6, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 8, 128, 11, 0, 0, 128,
+ 9, 0, 255, 129, 8, 0,
+ 0, 128, 88, 0, 0, 4,
+ 13, 0, 1, 128, 14, 0,
+ 255, 128, 0, 0, 255, 128,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 170, 129, 13, 0,
+ 228, 128, 0, 0, 228, 128,
+ 2, 0, 0, 3, 10, 0,
+ 7, 128, 8, 0, 228, 128,
+ 8, 0, 228, 128, 4, 0,
+ 0, 4, 11, 0, 7, 128,
+ 4, 0, 228, 128, 6, 0,
+ 85, 161, 10, 0, 228, 128,
+ 2, 0, 0, 3, 11, 0,
+ 7, 128, 11, 0, 228, 128,
+ 6, 0, 0, 160, 4, 0,
+ 0, 4, 13, 0, 7, 128,
+ 4, 0, 228, 128, 10, 0,
+ 228, 129, 11, 0, 228, 128,
+ 5, 0, 0, 3, 10, 0,
+ 7, 128, 4, 0, 228, 128,
+ 10, 0, 228, 128, 2, 0,
+ 0, 3, 15, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 228, 128, 5, 0, 0, 3,
+ 16, 0, 7, 128, 8, 0,
+ 228, 128, 15, 0, 228, 128,
+ 4, 0, 0, 4, 11, 0,
+ 7, 128, 15, 0, 228, 128,
+ 8, 0, 228, 129, 11, 0,
+ 228, 128, 88, 0, 0, 4,
+ 9, 0, 7, 128, 9, 0,
+ 228, 128, 10, 0, 228, 128,
+ 11, 0, 228, 128, 88, 0,
+ 0, 4, 5, 0, 6, 128,
+ 5, 0, 208, 128, 16, 0,
+ 228, 128, 13, 0, 228, 128,
+ 88, 0, 0, 4, 5, 0,
+ 1, 128, 14, 0, 255, 128,
+ 16, 0, 0, 128, 13, 0,
+ 0, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 85, 129, 5, 0, 228, 128,
+ 0, 0, 228, 128, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 4, 0, 0, 128, 4, 0,
+ 0, 4, 0, 0, 8, 128,
+ 9, 0, 255, 128, 0, 0,
+ 255, 129, 6, 0, 0, 161,
+ 11, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 13, 0, 170, 160, 5, 0,
+ 0, 3, 5, 0, 7, 128,
+ 4, 0, 228, 128, 4, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 8, 128, 5, 0,
+ 0, 129, 13, 0, 170, 160,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 1, 128,
+ 6, 0, 0, 129, 6, 0,
+ 0, 161, 0, 0, 255, 128,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 85, 128,
+ 4, 0, 0, 4, 0, 0,
+ 8, 128, 12, 0, 0, 128,
+ 0, 0, 255, 129, 6, 0,
+ 0, 161, 11, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 255, 128, 13, 0, 170, 160,
+ 88, 0, 0, 4, 0, 0,
+ 8, 128, 5, 0, 85, 129,
+ 13, 0, 170, 160, 1, 0,
+ 255, 128, 88, 0, 0, 4,
+ 10, 0, 2, 128, 6, 0,
+ 85, 129, 6, 0, 0, 161,
+ 0, 0, 255, 128, 6, 0,
+ 0, 2, 0, 0, 8, 128,
+ 4, 0, 170, 128, 4, 0,
+ 0, 4, 0, 0, 8, 128,
+ 12, 0, 85, 128, 0, 0,
+ 255, 129, 6, 0, 0, 161,
+ 11, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 13, 0, 170, 160, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 5, 0, 170, 129, 13, 0,
+ 170, 160, 1, 0, 255, 128,
+ 88, 0, 0, 4, 10, 0,
+ 4, 128, 6, 0, 170, 129,
+ 6, 0, 0, 161, 0, 0,
+ 255, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 0, 129, 10, 0, 228, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 5, 0, 15, 128,
+ 4, 0, 255, 128, 7, 0,
+ 228, 160, 5, 0, 0, 3,
+ 5, 0, 15, 128, 5, 0,
+ 228, 128, 5, 0, 228, 128,
+ 2, 0, 0, 3, 6, 0,
+ 7, 128, 4, 0, 228, 128,
+ 6, 0, 0, 160, 5, 0,
+ 0, 3, 6, 0, 7, 128,
+ 6, 0, 228, 128, 6, 0,
+ 228, 128, 6, 0, 0, 2,
+ 0, 0, 8, 128, 14, 0,
+ 0, 128, 5, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 8, 0, 0, 128,
+ 10, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 6, 0, 0, 161, 88, 0,
+ 0, 4, 0, 0, 8, 128,
+ 6, 0, 0, 129, 6, 0,
+ 0, 161, 1, 0, 255, 128,
+ 5, 0, 0, 3, 7, 0,
+ 7, 128, 8, 0, 228, 128,
+ 8, 0, 228, 128, 88, 0,
+ 0, 4, 10, 0, 1, 128,
+ 7, 0, 0, 129, 13, 0,
+ 170, 160, 0, 0, 255, 128,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 14, 0, 85, 128,
+ 6, 0, 0, 2, 1, 0,
+ 8, 128, 14, 0, 170, 128,
+ 5, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 8, 0, 170, 128, 10, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 6, 0,
+ 0, 161, 88, 0, 0, 4,
+ 1, 0, 8, 128, 6, 0,
+ 170, 129, 6, 0, 0, 161,
+ 2, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 4, 128,
+ 7, 0, 170, 129, 13, 0,
+ 170, 160, 1, 0, 255, 128,
+ 5, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 8, 0, 85, 128, 10, 0,
+ 0, 3, 1, 0, 8, 128,
+ 0, 0, 255, 128, 6, 0,
+ 0, 161, 88, 0, 0, 4,
+ 0, 0, 8, 128, 6, 0,
+ 85, 129, 6, 0, 0, 161,
+ 1, 0, 255, 128, 88, 0,
+ 0, 4, 10, 0, 2, 128,
+ 7, 0, 85, 129, 13, 0,
+ 170, 160, 0, 0, 255, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 5, 0, 255, 129,
+ 10, 0, 228, 128, 0, 0,
+ 228, 128, 11, 0, 0, 3,
+ 6, 0, 7, 128, 8, 0,
+ 228, 128, 4, 0, 228, 128,
+ 10, 0, 0, 3, 7, 0,
+ 7, 128, 4, 0, 228, 128,
+ 8, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 5, 0, 170, 129, 6, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 5, 0, 85, 129,
+ 7, 0, 228, 128, 0, 0,
+ 228, 128, 88, 0, 0, 4,
+ 0, 0, 7, 128, 5, 0,
+ 0, 129, 9, 0, 228, 128,
+ 0, 0, 228, 128, 88, 0,
+ 0, 4, 0, 0, 7, 128,
+ 10, 0, 255, 129, 1, 0,
+ 228, 128, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 7, 128, 2, 0, 170, 161,
+ 2, 0, 228, 128, 0, 0,
+ 228, 128, 18, 0, 0, 4,
+ 1, 0, 7, 128, 6, 0,
+ 255, 128, 0, 0, 228, 128,
+ 4, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 6, 0, 255, 128, 6, 0,
+ 255, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 3, 0,
+ 255, 128, 1, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 3, 0, 255, 128,
+ 3, 0, 255, 128, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 3, 0, 255, 128, 88, 0,
+ 0, 4, 0, 0, 15, 128,
+ 1, 0, 0, 129, 13, 0,
+ 170, 160, 0, 0, 228, 128,
+ 88, 0, 0, 4, 0, 0,
+ 15, 128, 1, 0, 255, 129,
+ 3, 0, 228, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0,
+ 83, 72, 68, 82, 184, 38,
+ 0, 0, 64, 0, 0, 0,
+ 174, 9, 0, 0, 89, 0,
+ 0, 4, 70, 142, 32, 0,
+ 0, 0, 0, 0, 6, 0,
+ 0, 0, 90, 0, 0, 3,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 0, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 1, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 2, 0, 0, 0, 85, 85,
+ 0, 0, 88, 24, 0, 4,
+ 0, 112, 16, 0, 3, 0,
+ 0, 0, 85, 85, 0, 0,
+ 88, 24, 0, 4, 0, 112,
+ 16, 0, 5, 0, 0, 0,
+ 85, 85, 0, 0, 88, 24,
+ 0, 4, 0, 112, 16, 0,
+ 6, 0, 0, 0, 85, 85,
+ 0, 0, 98, 16, 0, 3,
+ 50, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 194, 16, 16, 0, 1, 0,
+ 0, 0, 98, 16, 0, 3,
+ 114, 16, 16, 0, 2, 0,
+ 0, 0, 101, 0, 0, 3,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 104, 0, 0, 2,
+ 22, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 0, 0, 0, 0, 230, 26,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 6, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 4, 26, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 31, 0, 0, 4,
+ 10, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 0, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 56, 0, 0, 8, 114, 0,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 6, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 54, 0, 0, 6,
+ 130, 0, 16, 0, 1, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 54, 0, 0, 5, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 255, 255,
+ 255, 255, 18, 0, 0, 1,
+ 32, 0, 0, 8, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 2, 0,
+ 0, 0, 10, 128, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 3, 0,
+ 0, 0, 10, 0, 16, 0,
+ 3, 0, 0, 0, 1, 64,
+ 0, 0, 18, 131, 128, 189,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 2, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 3, 0, 0, 0,
+ 10, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 4, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 3, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 66, 0, 16, 0,
+ 3, 0, 0, 0, 10, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 16, 0, 0, 8,
+ 18, 0, 16, 0, 4, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 3, 0, 0, 0, 16, 0,
+ 0, 8, 34, 0, 16, 0,
+ 4, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 4, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 4, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 21, 0, 0, 1, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 55, 0, 0, 10, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 2, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 255, 255, 255, 255,
+ 18, 0, 0, 1, 32, 0,
+ 0, 8, 18, 0, 16, 0,
+ 2, 0, 0, 0, 1, 64,
+ 0, 0, 1, 0, 0, 0,
+ 26, 128, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 31, 0, 4, 3, 10, 0,
+ 16, 0, 2, 0, 0, 0,
+ 31, 0, 0, 4, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 3, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 0, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 8, 114, 0, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 14, 0, 0, 7, 98, 0,
+ 16, 0, 2, 0, 0, 0,
+ 6, 17, 16, 0, 2, 0,
+ 0, 0, 166, 26, 16, 0,
+ 2, 0, 0, 0, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 4, 0, 0, 0, 150, 5,
+ 16, 0, 2, 0, 0, 0,
+ 70, 126, 16, 0, 5, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 6, 130, 0, 16, 0,
+ 3, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 70, 14,
+ 16, 0, 3, 0, 0, 0,
+ 6, 0, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 66, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 1, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 42, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 194, 0, 16, 0,
+ 2, 0, 0, 0, 6, 20,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 0, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 6, 128,
+ 32, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 14, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 255, 255, 255, 255, 18, 0,
+ 0, 1, 32, 0, 0, 8,
+ 34, 0, 16, 0, 2, 0,
+ 0, 0, 1, 64, 0, 0,
+ 2, 0, 0, 0, 10, 128,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 31, 0,
+ 4, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 194, 0, 16, 0,
+ 2, 0, 0, 0, 6, 20,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 230, 10, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 4, 0,
+ 0, 0, 70, 16, 16, 0,
+ 1, 0, 0, 0, 70, 126,
+ 16, 0, 1, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 18, 0, 16, 0, 4, 0,
+ 0, 0, 10, 0, 16, 0,
+ 4, 0, 0, 0, 1, 64,
+ 0, 0, 18, 131, 128, 189,
+ 69, 0, 0, 9, 242, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 16, 16, 0, 1, 0,
+ 0, 0, 70, 126, 16, 0,
+ 2, 0, 0, 0, 0, 96,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 34, 0,
+ 16, 0, 4, 0, 0, 0,
+ 10, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 115, 128, 0, 191, 69, 0,
+ 0, 9, 242, 0, 16, 0,
+ 5, 0, 0, 0, 70, 16,
+ 16, 0, 1, 0, 0, 0,
+ 70, 126, 16, 0, 3, 0,
+ 0, 0, 0, 96, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 66, 0, 16, 0,
+ 4, 0, 0, 0, 10, 0,
+ 16, 0, 5, 0, 0, 0,
+ 1, 64, 0, 0, 115, 128,
+ 0, 191, 16, 0, 0, 8,
+ 18, 0, 16, 0, 5, 0,
+ 0, 0, 70, 130, 32, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 8, 34, 0, 16, 0,
+ 5, 0, 0, 0, 70, 130,
+ 32, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 4, 0, 0, 0,
+ 16, 0, 0, 8, 66, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 130, 32, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 4, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 8, 242, 0, 16, 0,
+ 4, 0, 0, 0, 70, 14,
+ 16, 0, 5, 0, 0, 0,
+ 6, 128, 32, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 56, 0, 0, 7, 242, 0,
+ 16, 0, 1, 0, 0, 0,
+ 6, 0, 16, 0, 3, 0,
+ 0, 0, 70, 14, 16, 0,
+ 4, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 21, 0, 0, 1, 31, 0,
+ 0, 3, 26, 0, 16, 0,
+ 2, 0, 0, 0, 14, 0,
+ 0, 7, 98, 0, 16, 0,
+ 2, 0, 0, 0, 6, 17,
+ 16, 0, 2, 0, 0, 0,
+ 166, 26, 16, 0, 2, 0,
+ 0, 0, 69, 0, 0, 9,
+ 242, 0, 16, 0, 3, 0,
+ 0, 0, 150, 5, 16, 0,
+ 2, 0, 0, 0, 70, 126,
+ 16, 0, 5, 0, 0, 0,
+ 0, 96, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 8,
+ 242, 0, 16, 0, 1, 0,
+ 0, 0, 6, 0, 16, 0,
+ 3, 0, 0, 0, 70, 142,
+ 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 21, 0,
+ 0, 1, 21, 0, 0, 1,
+ 21, 0, 0, 1, 55, 0,
+ 0, 12, 242, 0, 16, 0,
+ 1, 0, 0, 0, 6, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 14, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 128, 63, 24, 0,
+ 0, 7, 18, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 0, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 31, 0, 4, 3,
+ 10, 0, 16, 0, 2, 0,
+ 0, 0, 54, 0, 0, 5,
+ 242, 32, 16, 0, 0, 0,
+ 0, 0, 70, 14, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 21, 0, 0, 1,
+ 24, 0, 0, 7, 18, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 1, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 10, 0, 16, 0,
+ 2, 0, 0, 0, 54, 0,
+ 0, 8, 242, 32, 16, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 62, 0, 0, 1, 21, 0,
+ 0, 1, 14, 0, 0, 7,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 0, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 1, 0, 0, 0, 55, 0,
+ 0, 10, 114, 0, 16, 0,
+ 1, 0, 0, 0, 246, 143,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 3, 0,
+ 0, 0, 29, 0, 0, 10,
+ 242, 0, 16, 0, 5, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 63, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 128, 62, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 50, 0, 0, 12, 114, 0,
+ 16, 0, 9, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 6, 0, 0, 0,
+ 0, 0, 0, 10, 114, 0,
+ 16, 0, 9, 0, 0, 0,
+ 70, 2, 16, 0, 9, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 8, 0,
+ 0, 0, 50, 0, 0, 10,
+ 114, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 9, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 5, 0, 0, 0,
+ 70, 2, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 8, 0, 0, 0,
+ 51, 0, 0, 7, 114, 0,
+ 16, 0, 7, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 32, 0,
+ 0, 11, 242, 0, 16, 0,
+ 8, 0, 0, 0, 2, 64,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 3, 0,
+ 0, 0, 4, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 52, 0, 0, 7, 114, 0,
+ 16, 0, 11, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 24, 0,
+ 0, 10, 242, 0, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 24, 0, 0, 10,
+ 242, 0, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 11, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 51, 0, 0, 10, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 12, 114, 0, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 12, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 0, 0,
+ 0, 11, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 51, 0,
+ 0, 10, 114, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 11,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 13, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 9, 18, 0, 16, 0,
+ 15, 0, 0, 0, 58, 0,
+ 16, 0, 12, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 2, 0, 0, 0, 24, 0,
+ 0, 10, 146, 0, 16, 0,
+ 14, 0, 0, 0, 86, 9,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 24, 0, 0, 10,
+ 50, 0, 16, 0, 16, 0,
+ 0, 0, 150, 5, 16, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 12, 98, 0,
+ 16, 0, 14, 0, 0, 0,
+ 6, 1, 16, 0, 16, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 86, 6,
+ 16, 0, 14, 0, 0, 0,
+ 55, 0, 0, 12, 98, 0,
+ 16, 0, 15, 0, 0, 0,
+ 6, 3, 16, 0, 14, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 0, 0, 86, 6,
+ 16, 0, 14, 0, 0, 0,
+ 29, 0, 0, 10, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 63, 0, 0, 0, 63,
+ 0, 0, 0, 63, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 50, 0,
+ 0, 10, 114, 0, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 70, 2,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 9, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 6, 0,
+ 0, 0, 32, 0, 0, 11,
+ 242, 0, 16, 0, 9, 0,
+ 0, 0, 2, 64, 0, 0,
+ 5, 0, 0, 0, 6, 0,
+ 0, 0, 7, 0, 0, 0,
+ 8, 0, 0, 0, 166, 138,
+ 32, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 50, 0,
+ 0, 16, 114, 0, 16, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 128, 65, 0, 0, 0,
+ 1, 0, 0, 0, 2, 64,
+ 0, 0, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 63, 0, 0, 128, 63,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 10, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 10, 0, 0, 0,
+ 50, 0, 0, 10, 114, 0,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 128, 65, 0,
+ 0, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 1, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 64, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 191, 0, 0,
+ 128, 191, 0, 0, 128, 191,
+ 0, 0, 0, 0, 50, 0,
+ 0, 15, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 65, 0, 0, 128, 65,
+ 0, 0, 128, 65, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 64, 193, 0, 0,
+ 64, 193, 0, 0, 64, 193,
+ 0, 0, 0, 0, 50, 0,
+ 0, 12, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 128, 64, 0, 0,
+ 128, 64, 0, 0, 128, 64,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 75, 0, 0, 5,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 130, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 10, 0, 16, 0, 16, 0,
+ 0, 0, 10, 0, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 50, 0, 0, 9, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 10, 0, 16, 0, 13, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 18, 0,
+ 16, 0, 18, 0, 0, 0,
+ 10, 0, 16, 0, 14, 0,
+ 0, 0, 10, 0, 16, 0,
+ 10, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 29, 0, 0, 10, 146, 0,
+ 16, 0, 10, 0, 0, 0,
+ 2, 64, 0, 0, 0, 0,
+ 128, 62, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 128, 62, 86, 9, 16, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 146, 0, 16, 0,
+ 10, 0, 0, 0, 6, 12,
+ 16, 0, 10, 0, 0, 0,
+ 86, 9, 16, 0, 16, 0,
+ 0, 0, 86, 9, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 146, 0, 16, 0,
+ 10, 0, 0, 0, 86, 9,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 6, 12,
+ 16, 0, 10, 0, 0, 0,
+ 50, 0, 0, 9, 146, 0,
+ 16, 0, 10, 0, 0, 0,
+ 86, 9, 16, 0, 13, 0,
+ 0, 0, 6, 12, 16, 0,
+ 10, 0, 0, 0, 86, 9,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 98, 0,
+ 16, 0, 18, 0, 0, 0,
+ 86, 6, 16, 0, 14, 0,
+ 0, 0, 86, 6, 16, 0,
+ 10, 0, 0, 0, 6, 3,
+ 16, 0, 10, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 10, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 50, 0, 0, 13,
+ 114, 0, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 128,
+ 65, 0, 0, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 0, 0, 0, 64, 0, 0,
+ 0, 64, 0, 0, 0, 64,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 2, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 2, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 13, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 29, 0, 0, 7, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 2, 0, 0, 0, 49, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 6, 2,
+ 16, 0, 1, 0, 0, 0,
+ 102, 9, 16, 0, 1, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 16, 0,
+ 0, 0, 6, 10, 16, 128,
+ 65, 0, 0, 0, 1, 0,
+ 0, 0, 150, 4, 16, 0,
+ 1, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 13, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 22, 7,
+ 16, 0, 16, 0, 0, 0,
+ 1, 0, 0, 7, 98, 0,
+ 16, 0, 16, 0, 0, 0,
+ 6, 3, 16, 0, 13, 0,
+ 0, 0, 6, 0, 16, 0,
+ 14, 0, 0, 0, 29, 0,
+ 0, 7, 146, 0, 16, 0,
+ 14, 0, 0, 0, 166, 10,
+ 16, 0, 1, 0, 0, 0,
+ 86, 1, 16, 0, 1, 0,
+ 0, 0, 1, 0, 0, 7,
+ 98, 0, 16, 0, 17, 0,
+ 0, 0, 246, 13, 16, 0,
+ 13, 0, 0, 0, 86, 5,
+ 16, 0, 14, 0, 0, 0,
+ 1, 0, 0, 7, 50, 0,
+ 16, 0, 19, 0, 0, 0,
+ 230, 10, 16, 0, 13, 0,
+ 0, 0, 166, 10, 16, 0,
+ 14, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 17, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 19, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 226, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 14, 0, 0, 0, 6, 9,
+ 16, 0, 17, 0, 0, 0,
+ 6, 9, 16, 0, 19, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 16, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 14, 0, 0, 0, 6, 0,
+ 16, 0, 14, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 150, 7, 16, 0,
+ 14, 0, 0, 0, 18, 0,
+ 0, 1, 49, 0, 0, 7,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 86, 6, 16, 0,
+ 1, 0, 0, 0, 38, 8,
+ 16, 0, 1, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 17, 0, 0, 0,
+ 86, 10, 16, 128, 65, 0,
+ 0, 0, 1, 0, 0, 0,
+ 134, 1, 16, 0, 1, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 13, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 22, 7, 16, 0,
+ 17, 0, 0, 0, 1, 0,
+ 0, 7, 82, 0, 16, 0,
+ 17, 0, 0, 0, 6, 3,
+ 16, 0, 13, 0, 0, 0,
+ 6, 0, 16, 0, 16, 0,
+ 0, 0, 29, 0, 0, 7,
+ 146, 0, 16, 0, 16, 0,
+ 0, 0, 166, 10, 16, 0,
+ 1, 0, 0, 0, 6, 4,
+ 16, 0, 1, 0, 0, 0,
+ 1, 0, 0, 7, 82, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 13, 16, 0, 13, 0,
+ 0, 0, 86, 5, 16, 0,
+ 16, 0, 0, 0, 1, 0,
+ 0, 7, 50, 0, 16, 0,
+ 13, 0, 0, 0, 182, 15,
+ 16, 0, 13, 0, 0, 0,
+ 166, 10, 16, 0, 16, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 19, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 13, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 246, 15, 16, 0, 16, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 17, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 6, 0, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 13, 0,
+ 0, 0, 21, 0, 0, 1,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 2, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 13, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 16, 0, 0, 10,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 2, 64, 0, 0,
+ 154, 153, 153, 62, 61, 10,
+ 23, 63, 174, 71, 225, 61,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 26, 0, 16, 0, 13, 0,
+ 0, 0, 10, 0, 16, 0,
+ 13, 0, 0, 0, 51, 0,
+ 0, 7, 130, 0, 16, 0,
+ 4, 0, 0, 0, 42, 0,
+ 16, 0, 13, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 26, 0, 16, 0,
+ 13, 0, 0, 0, 10, 0,
+ 16, 0, 13, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 13, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 49, 0,
+ 0, 7, 130, 0, 16, 0,
+ 6, 0, 0, 0, 58, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 4, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 13, 0, 0, 0,
+ 246, 15, 16, 0, 6, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 6, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 246, 15, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 14, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 58, 0, 16, 0, 5, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 246, 15,
+ 16, 0, 5, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 14, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 14, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 13, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 14, 0,
+ 0, 0, 70, 2, 16, 0,
+ 13, 0, 0, 0, 32, 0,
+ 0, 11, 242, 0, 16, 0,
+ 14, 0, 0, 0, 2, 64,
+ 0, 0, 9, 0, 0, 0,
+ 10, 0, 0, 0, 11, 0,
+ 0, 0, 12, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 1, 0,
+ 0, 0, 10, 0, 16, 0,
+ 1, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 3, 0, 0, 0, 42, 0,
+ 16, 0, 1, 0, 0, 0,
+ 58, 0, 16, 0, 3, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 1, 0, 0, 0, 10, 0,
+ 16, 0, 1, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 1, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 16, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 29, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 31, 0,
+ 4, 3, 58, 0, 16, 0,
+ 3, 0, 0, 0, 49, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 6, 2,
+ 16, 0, 0, 0, 0, 0,
+ 102, 9, 16, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 242, 0, 16, 0, 19, 0,
+ 0, 0, 6, 10, 16, 128,
+ 65, 0, 0, 0, 0, 0,
+ 0, 0, 150, 4, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 22, 7,
+ 16, 0, 19, 0, 0, 0,
+ 1, 0, 0, 7, 98, 0,
+ 16, 0, 19, 0, 0, 0,
+ 6, 3, 16, 0, 16, 0,
+ 0, 0, 6, 0, 16, 0,
+ 17, 0, 0, 0, 29, 0,
+ 0, 7, 146, 0, 16, 0,
+ 17, 0, 0, 0, 166, 10,
+ 16, 0, 0, 0, 0, 0,
+ 86, 1, 16, 0, 0, 0,
+ 0, 0, 1, 0, 0, 7,
+ 98, 0, 16, 0, 20, 0,
+ 0, 0, 246, 13, 16, 0,
+ 16, 0, 0, 0, 86, 5,
+ 16, 0, 17, 0, 0, 0,
+ 1, 0, 0, 7, 50, 0,
+ 16, 0, 21, 0, 0, 0,
+ 230, 10, 16, 0, 16, 0,
+ 0, 0, 166, 10, 16, 0,
+ 17, 0, 0, 0, 54, 0,
+ 0, 5, 18, 0, 16, 0,
+ 20, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 54, 0, 0, 5, 66, 0,
+ 16, 0, 21, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 226, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 17, 0, 0, 0, 6, 9,
+ 16, 0, 20, 0, 0, 0,
+ 6, 9, 16, 0, 21, 0,
+ 0, 0, 54, 0, 0, 5,
+ 18, 0, 16, 0, 19, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 17, 0, 0, 0, 6, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 150, 7, 16, 0,
+ 17, 0, 0, 0, 18, 0,
+ 0, 1, 49, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 86, 6, 16, 0,
+ 0, 0, 0, 0, 38, 8,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 242, 0,
+ 16, 0, 20, 0, 0, 0,
+ 86, 10, 16, 128, 65, 0,
+ 0, 0, 0, 0, 0, 0,
+ 134, 1, 16, 0, 0, 0,
+ 0, 0, 56, 0, 0, 7,
+ 114, 0, 16, 0, 21, 0,
+ 0, 0, 246, 15, 16, 0,
+ 16, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 21, 0,
+ 0, 0, 22, 7, 16, 0,
+ 20, 0, 0, 0, 1, 0,
+ 0, 7, 82, 0, 16, 0,
+ 20, 0, 0, 0, 6, 3,
+ 16, 0, 16, 0, 0, 0,
+ 6, 0, 16, 0, 19, 0,
+ 0, 0, 29, 0, 0, 7,
+ 146, 0, 16, 0, 19, 0,
+ 0, 0, 166, 10, 16, 0,
+ 0, 0, 0, 0, 6, 4,
+ 16, 0, 0, 0, 0, 0,
+ 1, 0, 0, 7, 82, 0,
+ 16, 0, 21, 0, 0, 0,
+ 246, 13, 16, 0, 16, 0,
+ 0, 0, 86, 5, 16, 0,
+ 19, 0, 0, 0, 1, 0,
+ 0, 7, 50, 0, 16, 0,
+ 16, 0, 0, 0, 182, 15,
+ 16, 0, 16, 0, 0, 0,
+ 166, 10, 16, 0, 19, 0,
+ 0, 0, 54, 0, 0, 5,
+ 34, 0, 16, 0, 21, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 54, 0,
+ 0, 5, 66, 0, 16, 0,
+ 16, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 19, 0,
+ 0, 0, 70, 2, 16, 0,
+ 21, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 54, 0, 0, 5, 34, 0,
+ 16, 0, 20, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 6, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 21, 0, 0, 1,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 3, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 3, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 16, 0, 0, 0, 10, 0,
+ 16, 0, 16, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 16, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 5, 0, 0, 0, 26, 0,
+ 16, 0, 16, 0, 0, 0,
+ 10, 0, 16, 0, 16, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 42, 0, 16, 0,
+ 16, 0, 0, 0, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 6, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 4, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 16, 0,
+ 0, 0, 246, 15, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 49, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 58, 0,
+ 16, 0, 5, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 16, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 246, 15,
+ 16, 0, 6, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 17, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 3, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 16, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 17, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 16, 0, 0, 10, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 2, 64, 0, 0, 154, 153,
+ 153, 62, 61, 10, 23, 63,
+ 174, 71, 225, 61, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 4, 0, 0, 0, 58, 0,
+ 16, 0, 2, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 16, 0,
+ 0, 10, 130, 0, 16, 0,
+ 4, 0, 0, 0, 2, 64,
+ 0, 0, 154, 153, 153, 62,
+ 61, 10, 23, 63, 174, 71,
+ 225, 61, 0, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 51, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 26, 0, 16, 0,
+ 17, 0, 0, 0, 10, 0,
+ 16, 0, 17, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 42, 0, 16, 0, 17, 0,
+ 0, 0, 58, 0, 16, 0,
+ 5, 0, 0, 0, 52, 0,
+ 0, 7, 130, 0, 16, 0,
+ 6, 0, 0, 0, 26, 0,
+ 16, 0, 17, 0, 0, 0,
+ 10, 0, 16, 0, 17, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 42, 0, 16, 0,
+ 17, 0, 0, 0, 58, 0,
+ 16, 0, 6, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 7, 0, 0, 0,
+ 58, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 0, 4, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 0, 0,
+ 0, 8, 130, 0, 16, 0,
+ 5, 0, 0, 0, 58, 0,
+ 16, 0, 4, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 5, 0, 0, 0,
+ 14, 0, 0, 7, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 5, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 17, 0,
+ 0, 0, 246, 15, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 49, 0, 0, 7,
+ 130, 0, 16, 0, 5, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 58, 0,
+ 16, 0, 6, 0, 0, 0,
+ 0, 0, 0, 8, 114, 0,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 128, 65, 0,
+ 0, 0, 4, 0, 0, 0,
+ 70, 2, 16, 0, 17, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 7, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 4, 0,
+ 0, 0, 1, 64, 0, 0,
+ 0, 0, 128, 63, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 246, 15,
+ 16, 0, 7, 0, 0, 0,
+ 70, 2, 16, 0, 19, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 6, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 4, 0,
+ 0, 0, 58, 0, 16, 0,
+ 6, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 246, 15, 16, 0, 6, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 19, 0,
+ 0, 0, 246, 15, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 19, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 17, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 32, 0, 0, 11, 50, 0,
+ 16, 0, 19, 0, 0, 0,
+ 2, 64, 0, 0, 13, 0,
+ 0, 0, 14, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 166, 138, 32, 0,
+ 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 58, 0, 16, 128,
+ 65, 0, 0, 0, 2, 0,
+ 0, 0, 58, 0, 16, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 16, 0, 0, 10,
+ 130, 0, 16, 0, 2, 0,
+ 0, 0, 2, 64, 0, 0,
+ 154, 153, 153, 62, 61, 10,
+ 23, 63, 174, 71, 225, 61,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 51, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 26, 0, 16, 0, 0, 0,
+ 0, 0, 10, 0, 16, 0,
+ 0, 0, 0, 0, 51, 0,
+ 0, 7, 130, 0, 16, 0,
+ 3, 0, 0, 0, 42, 0,
+ 16, 0, 0, 0, 0, 0,
+ 58, 0, 16, 0, 3, 0,
+ 0, 0, 52, 0, 0, 7,
+ 130, 0, 16, 0, 4, 0,
+ 0, 0, 26, 0, 16, 0,
+ 0, 0, 0, 0, 10, 0,
+ 16, 0, 0, 0, 0, 0,
+ 52, 0, 0, 7, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 42, 0, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 4, 0, 0, 0, 49, 0,
+ 0, 7, 130, 0, 16, 0,
+ 5, 0, 0, 0, 58, 0,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 2, 0, 0, 0, 56, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 20, 0,
+ 0, 0, 0, 0, 0, 8,
+ 130, 0, 16, 0, 3, 0,
+ 0, 0, 58, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 3, 0, 0, 0, 14, 0,
+ 0, 7, 114, 0, 16, 0,
+ 20, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 246, 15, 16, 0, 3, 0,
+ 0, 0, 0, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 246, 15, 16, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 5, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 49, 0, 0, 7, 130, 0,
+ 16, 0, 3, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 58, 0, 16, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 8, 114, 0, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 128, 65, 0, 0, 0,
+ 2, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 5, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 64, 0, 0, 0, 0,
+ 128, 63, 56, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 246, 15, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 20, 0, 0, 0,
+ 0, 0, 0, 8, 130, 0,
+ 16, 0, 4, 0, 0, 0,
+ 58, 0, 16, 128, 65, 0,
+ 0, 0, 2, 0, 0, 0,
+ 58, 0, 16, 0, 4, 0,
+ 0, 0, 14, 0, 0, 7,
+ 114, 0, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 246, 15,
+ 16, 0, 4, 0, 0, 0,
+ 0, 0, 0, 7, 114, 0,
+ 16, 0, 20, 0, 0, 0,
+ 246, 15, 16, 0, 2, 0,
+ 0, 0, 70, 2, 16, 0,
+ 20, 0, 0, 0, 55, 0,
+ 0, 9, 114, 0, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 20, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 7, 114, 0, 16, 0,
+ 0, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 19, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 19, 0, 0, 0, 70, 2,
+ 16, 0, 17, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 16, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 166, 10, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 13, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 9,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 86, 5, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 0, 3, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 55, 0, 0, 10,
+ 114, 0, 16, 0, 0, 0,
+ 0, 0, 6, 0, 16, 0,
+ 14, 0, 0, 0, 70, 2,
+ 16, 128, 129, 0, 0, 0,
+ 10, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 18, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 10, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 6, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 15, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 9, 0,
+ 0, 0, 70, 2, 16, 0,
+ 12, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 246, 15, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 11, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 10, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 7, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 86, 5, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 5, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 9, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 6, 0, 16, 0, 8, 0,
+ 0, 0, 70, 2, 16, 0,
+ 4, 0, 0, 0, 70, 2,
+ 16, 0, 0, 0, 0, 0,
+ 55, 0, 0, 10, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 166, 138, 32, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 70, 2, 16, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 8, 18, 0, 16, 0,
+ 2, 0, 0, 0, 58, 0,
+ 16, 128, 65, 0, 0, 0,
+ 0, 0, 0, 0, 1, 64,
+ 0, 0, 0, 0, 128, 63,
+ 56, 0, 0, 7, 114, 0,
+ 16, 0, 0, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 246, 15, 16, 0,
+ 0, 0, 0, 0, 50, 0,
+ 0, 9, 114, 0, 16, 0,
+ 0, 0, 0, 0, 6, 0,
+ 16, 0, 2, 0, 0, 0,
+ 70, 2, 16, 0, 1, 0,
+ 0, 0, 70, 2, 16, 0,
+ 0, 0, 0, 0, 56, 0,
+ 0, 7, 114, 32, 16, 0,
+ 0, 0, 0, 0, 246, 15,
+ 16, 0, 1, 0, 0, 0,
+ 70, 2, 16, 0, 0, 0,
+ 0, 0, 54, 0, 0, 5,
+ 130, 32, 16, 0, 0, 0,
+ 0, 0, 58, 0, 16, 0,
+ 1, 0, 0, 0, 62, 0,
+ 0, 1, 83, 84, 65, 84,
+ 116, 0, 0, 0, 77, 1,
+ 0, 0, 22, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 191, 0, 0, 0,
+ 9, 0, 0, 0, 13, 0,
+ 0, 0, 13, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 82, 68, 69, 70, 156, 3,
+ 0, 0, 1, 0, 0, 0,
+ 80, 1, 0, 0, 8, 0,
+ 0, 0, 28, 0, 0, 0,
+ 0, 4, 255, 255, 0, 1,
+ 0, 0, 116, 3, 0, 0,
+ 28, 1, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 37, 1, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 42, 1,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 1, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 45, 1, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 2, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 49, 1, 0, 0,
+ 2, 0, 0, 0, 5, 0,
+ 0, 0, 4, 0, 0, 0,
+ 255, 255, 255, 255, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 13, 0, 0, 0, 53, 1,
+ 0, 0, 2, 0, 0, 0,
+ 5, 0, 0, 0, 4, 0,
+ 0, 0, 255, 255, 255, 255,
+ 5, 0, 0, 0, 1, 0,
+ 0, 0, 13, 0, 0, 0,
+ 59, 1, 0, 0, 2, 0,
+ 0, 0, 5, 0, 0, 0,
+ 4, 0, 0, 0, 255, 255,
+ 255, 255, 6, 0, 0, 0,
+ 1, 0, 0, 0, 13, 0,
+ 0, 0, 69, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 83,
+ 97, 109, 112, 108, 101, 114,
+ 0, 116, 82, 71, 66, 0,
+ 116, 89, 0, 116, 67, 98,
+ 0, 116, 67, 114, 0, 116,
+ 77, 97, 115, 107, 0, 116,
+ 66, 97, 99, 107, 100, 114,
+ 111, 112, 0, 36, 71, 108,
+ 111, 98, 97, 108, 115, 0,
+ 171, 171, 69, 1, 0, 0,
+ 11, 0, 0, 0, 104, 1,
+ 0, 0, 96, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 112, 2, 0, 0,
+ 0, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 124, 2, 0, 0, 0, 0,
+ 0, 0, 140, 2, 0, 0,
+ 16, 0, 0, 0, 4, 0,
+ 0, 0, 2, 0, 0, 0,
+ 156, 2, 0, 0, 0, 0,
+ 0, 0, 172, 2, 0, 0,
+ 32, 0, 0, 0, 16, 0,
+ 0, 0, 2, 0, 0, 0,
+ 188, 2, 0, 0, 0, 0,
+ 0, 0, 204, 2, 0, 0,
+ 48, 0, 0, 0, 44, 0,
+ 0, 0, 2, 0, 0, 0,
+ 220, 2, 0, 0, 0, 0,
+ 0, 0, 236, 2, 0, 0,
+ 96, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 12, 3, 0, 0,
+ 160, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 24, 3, 0, 0,
+ 224, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 124, 2, 0, 0, 0, 0,
+ 0, 0, 44, 3, 0, 0,
+ 240, 0, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 76, 3, 0, 0,
+ 0, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 87, 3, 0, 0,
+ 16, 1, 0, 0, 16, 0,
+ 0, 0, 0, 0, 0, 0,
+ 60, 3, 0, 0, 0, 0,
+ 0, 0, 97, 3, 0, 0,
+ 32, 1, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 252, 2, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 105, 66, 108, 101,
+ 110, 100, 67, 111, 110, 102,
+ 105, 103, 0, 171, 171, 171,
+ 1, 0, 19, 0, 1, 0,
+ 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 76, 97, 121, 101, 114,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 109, 80, 114, 111,
+ 106, 101, 99, 116, 105, 111,
+ 110, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 118,
+ 77, 97, 115, 107, 81, 117,
+ 97, 100, 0, 109, 66, 97,
+ 99, 107, 100, 114, 111, 112,
+ 84, 114, 97, 110, 115, 102,
+ 111, 114, 109, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 49, 48, 46,
+ 49, 0, 73, 83, 71, 78,
+ 128, 0, 0, 0, 4, 0,
+ 0, 0, 8, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0,
+ 116, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 3, 3, 0, 0,
+ 116, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 12, 12, 0, 0,
+ 116, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 2, 0,
+ 0, 0, 7, 7, 0, 0,
+ 83, 86, 95, 80, 111, 115,
+ 105, 116, 105, 111, 110, 0,
+ 84, 69, 88, 67, 79, 79,
+ 82, 68, 0, 171, 171, 171,
+ 79, 83, 71, 78, 44, 0,
+ 0, 0, 1, 0, 0, 0,
+ 8, 0, 0, 0, 32, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 0, 83, 86,
+ 95, 84, 97, 114, 103, 101,
+ 116, 0, 171, 171
+};
+ShaderBytes sBlendShader = { BlendShader, sizeof(BlendShader) };
diff --git a/gfx/layers/d3d11/ReadbackManagerD3D11.cpp b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
new file mode 100644
index 000000000..88d75869d
--- /dev/null
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ReadbackManagerD3D11.h"
+#include "ReadbackProcessor.h"
+#include "ReadbackLayer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/gfx/2D.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+// Structure that contains the information required to execute a readback task,
+// the only member accessed off the main thread here is mReadbackTexture. Since
+// mSink may be released only on the main thread this object should always be
+// destroyed on the main thread!
+struct ReadbackTask {
+ // The texture that we copied the contents of the paintedlayer to.
+ RefPtr<ID3D10Texture2D> mReadbackTexture;
+ // The sink that we're trying to read back to.
+ RefPtr<TextureReadbackSink> mSink;
+};
+
+// This class is created and dispatched from the Readback thread but it must be
+// destroyed by the main thread.
+class ReadbackResultWriterD3D11 final : public nsIRunnable
+{
+ ~ReadbackResultWriterD3D11() {}
+ NS_DECL_THREADSAFE_ISUPPORTS
+public:
+ ReadbackResultWriterD3D11(ReadbackTask *aTask) : mTask(aTask) {}
+
+ NS_IMETHOD Run() override
+ {
+ D3D10_TEXTURE2D_DESC desc;
+ mTask->mReadbackTexture->GetDesc(&desc);
+
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ // Unless there is an error this map should succeed immediately, as we've
+ // recently mapped (and unmapped) this copied data on our task thread.
+ HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+
+ if (FAILED(hr)) {
+ mTask->mSink->ProcessReadback(nullptr);
+ return NS_OK;
+ }
+
+ {
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)mappedTex.pData, mappedTex.RowPitch,
+ IntSize(desc.Width, desc.Height),
+ SurfaceFormat::B8G8R8A8);
+
+ mTask->mSink->ProcessReadback(surf);
+
+ MOZ_ASSERT(surf->hasOneRef());
+ }
+
+ mTask->mReadbackTexture->Unmap(0);
+
+ return NS_OK;
+ }
+
+private:
+ nsAutoPtr<ReadbackTask> mTask;
+};
+
+NS_IMPL_ISUPPORTS(ReadbackResultWriterD3D11, nsIRunnable)
+
+DWORD WINAPI ReadbackManagerD3D11::StartTaskThread(void *aManager)
+{
+ static_cast<ReadbackManagerD3D11*>(aManager)->ProcessTasks();
+
+ return 0;
+}
+
+ReadbackManagerD3D11::ReadbackManagerD3D11()
+ : mRefCnt(0)
+{
+ ::InitializeCriticalSection(&mTaskMutex);
+ mShutdownEvent = ::CreateEventA(nullptr, FALSE, FALSE, nullptr);
+ mTaskSemaphore = ::CreateSemaphoreA(nullptr, 0, 1000000, nullptr);
+ mTaskThread = ::CreateThread(nullptr, 0, StartTaskThread, this, 0, 0);
+}
+
+ReadbackManagerD3D11::~ReadbackManagerD3D11()
+{
+ ::SetEvent(mShutdownEvent);
+
+ // This shouldn't take longer than 5 seconds, if it does we're going to choose
+ // to leak the thread and its synchronisation in favor of crashing or freezing
+ DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
+ if (result != WAIT_TIMEOUT) {
+ ::DeleteCriticalSection(&mTaskMutex);
+ ::CloseHandle(mShutdownEvent);
+ ::CloseHandle(mTaskSemaphore);
+ ::CloseHandle(mTaskThread);
+ } else {
+ NS_RUNTIMEABORT("ReadbackManager: Task thread did not shutdown in 5 seconds.");
+ }
+}
+
+void
+ReadbackManagerD3D11::PostTask(ID3D10Texture2D *aTexture, TextureReadbackSink* aSink)
+{
+ ReadbackTask *task = new ReadbackTask;
+ task->mReadbackTexture = aTexture;
+ task->mSink = aSink;
+
+ ::EnterCriticalSection(&mTaskMutex);
+ mPendingReadbackTasks.AppendElement(task);
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ ::ReleaseSemaphore(mTaskSemaphore, 1, nullptr);
+}
+
+void
+ReadbackManagerD3D11::ProcessTasks()
+{
+ HANDLE handles[] = { mTaskSemaphore, mShutdownEvent };
+
+ while (true) {
+ DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (result != WAIT_OBJECT_0) {
+ return;
+ }
+
+ ::EnterCriticalSection(&mTaskMutex);
+ if (mPendingReadbackTasks.Length() == 0) {
+ NS_RUNTIMEABORT("Trying to read from an empty array, bad bad bad");
+ }
+ ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
+ mPendingReadbackTasks.RemoveElementAt(0);
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ // We want to block here until the texture contents are available, the
+ // easiest thing is to simply map and unmap.
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+ nextReadbackTask->mReadbackTexture->Unmap(0);
+
+ // We can only send the update to the sink on the main thread, so post an
+ // event there to do so. Ownership of the task is passed from
+ // mPendingReadbackTasks to ReadbackResultWriter here.
+ nsCOMPtr<nsIThread> thread = do_GetMainThread();
+ thread->Dispatch(new ReadbackResultWriterD3D11(nextReadbackTask),
+ nsIEventTarget::DISPATCH_NORMAL);
+ }
+}
+
+}
+}
diff --git a/gfx/layers/d3d11/ReadbackManagerD3D11.h b/gfx/layers/d3d11/ReadbackManagerD3D11.h
new file mode 100644
index 000000000..d15346fd8
--- /dev/null
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKMANAGERD3D11_H
+#define GFX_READBACKMANAGERD3D11_H
+
+#include <windows.h>
+#include <d3d10_1.h>
+
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureReadbackSink;
+struct ReadbackTask;
+
+class ReadbackManagerD3D11 final
+{
+ NS_INLINE_DECL_REFCOUNTING(ReadbackManagerD3D11)
+public:
+ ReadbackManagerD3D11();
+
+ /**
+ * Tell the readback manager to post a readback task.
+ *
+ * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that
+ * was readback.
+ * @param aSink TextureReadbackSink that the resulting DataSourceSurface
+ * should be dispatched to.
+ */
+ void PostTask(ID3D10Texture2D* aTexture, TextureReadbackSink* aSink);
+
+private:
+ ~ReadbackManagerD3D11();
+
+ static DWORD WINAPI StartTaskThread(void *aManager);
+
+ void ProcessTasks();
+
+ // The invariant maintained by |mTaskSemaphore| is that the readback thread
+ // will awaken from WaitForMultipleObjects() at least once per readback
+ // task enqueued by the main thread. Since the readback thread processes
+ // exactly one task per wakeup (with one exception), no tasks are lost. The
+ // exception is when the readback thread is shut down, which orphans the
+ // remaining tasks, on purpose.
+ HANDLE mTaskSemaphore;
+ // Event signaled when the task thread should shutdown
+ HANDLE mShutdownEvent;
+ // Handle to the task thread
+ HANDLE mTaskThread;
+
+ // FiFo list of readback tasks that are to be executed. Access is synchronized
+ // by mTaskMutex.
+ CRITICAL_SECTION mTaskMutex;
+ nsTArray<nsAutoPtr<ReadbackTask>> mPendingReadbackTasks;
+};
+
+}
+}
+
+#endif /* GFX_READBACKMANAGERD3D11_H */
diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp
new file mode 100644
index 000000000..8fbcfd234
--- /dev/null
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -0,0 +1,1291 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureD3D11.h"
+#include "CompositorD3D11.h"
+#include "gfxContext.h"
+#include "Effects.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxPrefs.h"
+#include "ReadbackManagerD3D11.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static const GUID sD3D11TextureUsage =
+{ 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
+
+/* This class gets its lifetime tied to a D3D texture
+ * and increments memory usage on construction and decrements
+ * on destruction */
+class TextureMemoryMeasurer : public IUnknown
+{
+public:
+ TextureMemoryMeasurer(size_t aMemoryUsed)
+ {
+ mMemoryUsed = aMemoryUsed;
+ gfxWindowsPlatform::sD3D11SharedTextures += mMemoryUsed;
+ mRefCnt = 0;
+ }
+ STDMETHODIMP_(ULONG) AddRef() {
+ mRefCnt++;
+ return mRefCnt;
+ }
+ STDMETHODIMP QueryInterface(REFIID riid,
+ void **ppvObject)
+ {
+ IUnknown *punk = nullptr;
+ if (riid == IID_IUnknown) {
+ punk = this;
+ }
+ *ppvObject = punk;
+ if (punk) {
+ punk->AddRef();
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ STDMETHODIMP_(ULONG) Release() {
+ int refCnt = --mRefCnt;
+ if (refCnt == 0) {
+ gfxWindowsPlatform::sD3D11SharedTextures -= mMemoryUsed;
+ delete this;
+ }
+ return refCnt;
+ }
+private:
+ int mRefCnt;
+ int mMemoryUsed;
+};
+
+static DXGI_FORMAT
+SurfaceFormatToDXGIFormat(gfx::SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::B8G8R8X8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::R8G8B8A8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::R8G8B8X8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::A8:
+ return DXGI_FORMAT_R8_UNORM;
+ default:
+ MOZ_ASSERT(false, "unsupported format");
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+static uint32_t
+GetRequiredTilesD3D11(uint32_t aSize, uint32_t aMaxSize)
+{
+ uint32_t requiredTiles = aSize / aMaxSize;
+ if (aSize % aMaxSize) {
+ requiredTiles++;
+ }
+ return requiredTiles;
+}
+
+static IntRect
+GetTileRectD3D11(uint32_t aID, IntSize aSize, uint32_t aMaxSize)
+{
+ uint32_t horizontalTiles = GetRequiredTilesD3D11(aSize.width, aMaxSize);
+ uint32_t verticalTiles = GetRequiredTilesD3D11(aSize.height, aMaxSize);
+
+ uint32_t verticalTile = aID / horizontalTiles;
+ uint32_t horizontalTile = aID % horizontalTiles;
+
+ return IntRect(horizontalTile * aMaxSize,
+ verticalTile * aMaxSize,
+ horizontalTile < (horizontalTiles - 1) ? aMaxSize : aSize.width % aMaxSize,
+ verticalTile < (verticalTiles - 1) ? aMaxSize : aSize.height % aMaxSize);
+}
+
+AutoTextureLock::AutoTextureLock(IDXGIKeyedMutex* aMutex,
+ HRESULT& aResult,
+ uint32_t aTimeout)
+{
+ mMutex = aMutex;
+ mResult = mMutex->AcquireSync(0, aTimeout);
+ aResult = mResult;
+}
+
+AutoTextureLock::~AutoTextureLock()
+{
+ if (!FAILED(mResult) && mResult != WAIT_TIMEOUT &&
+ mResult != WAIT_ABANDONED) {
+ mMutex->ReleaseSync(0);
+ }
+}
+
+ID3D11ShaderResourceView*
+TextureSourceD3D11::GetShaderResourceView()
+{
+ MOZ_ASSERT(mTexture == GetD3D11Texture(), "You need to override GetShaderResourceView if you're overriding GetD3D11Texture!");
+
+ if (!mSRV && mTexture) {
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ // see comment in CompositingRenderTargetD3D11 constructor
+ CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D, mFormatOverride);
+ D3D11_SHADER_RESOURCE_VIEW_DESC *desc = mFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &srvDesc;
+
+ HRESULT hr = device->CreateShaderResourceView(mTexture, desc, getter_AddRefs(mSRV));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "[D3D11] TextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mSRV;
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(SurfaceFormat aFormat,
+ CompositorD3D11* aCompositor,
+ TextureFlags aFlags)
+ : mCompositor(aCompositor)
+ , mFormat(aFormat)
+ , mFlags(aFlags)
+ , mCurrentTile(0)
+ , mIsTiled(false)
+ , mIterating(false)
+ , mAllowTextureUploads(true)
+{
+ MOZ_COUNT_CTOR(DataTextureSourceD3D11);
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(SurfaceFormat aFormat,
+ CompositorD3D11* aCompositor,
+ ID3D11Texture2D* aTexture)
+: mCompositor(aCompositor)
+, mFormat(aFormat)
+, mFlags(TextureFlags::NO_FLAGS)
+, mCurrentTile(0)
+, mIsTiled(false)
+, mIterating(false)
+, mAllowTextureUploads(false)
+{
+ MOZ_COUNT_CTOR(DataTextureSourceD3D11);
+
+ mTexture = aTexture;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+
+ mSize = IntSize(desc.Width, desc.Height);
+}
+
+
+
+DataTextureSourceD3D11::~DataTextureSourceD3D11()
+{
+ MOZ_COUNT_DTOR(DataTextureSourceD3D11);
+}
+
+
+template<typename T> // ID3D10Texture2D or ID3D11Texture2D
+static bool LockD3DTexture(T* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ // Textures created by the DXVA decoders don't have a mutex for synchronization
+ if (mutex) {
+ HRESULT hr = mutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ return false;
+ }
+ }
+ return true;
+}
+
+template<typename T>
+static bool HasKeyedMutex(T* aTexture)
+{
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ return !!mutex;
+}
+
+template<typename T> // ID3D10Texture2D or ID3D11Texture2D
+static void UnlockD3DTexture(T* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ if (mutex) {
+ HRESULT hr = mutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+}
+
+DXGITextureData::DXGITextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent)
+: mSize(aSize)
+, mFormat(aFormat)
+, mNeedsClear(aNeedsClear)
+, mNeedsClearWhite(aNeedsClearWhite)
+, mHasSynchronization(false)
+, mIsForOutOfBandContent(aIsForOutOfBandContent)
+{}
+
+D3D11TextureData::D3D11TextureData(ID3D11Texture2D* aTexture,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent)
+: DXGITextureData(aSize, aFormat, aNeedsClear, aNeedsClearWhite, aIsForOutOfBandContent)
+, mTexture(aTexture)
+{
+ MOZ_ASSERT(aTexture);
+ mHasSynchronization = HasKeyedMutex(aTexture);
+}
+
+D3D11TextureData::~D3D11TextureData()
+{
+#ifdef DEBUG
+ // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
+ // when it calls EndDraw. This EndDraw should not execute anything so it
+ // shouldn't -really- need the lock but the debug layer chokes on this.
+ if (mDrawTarget) {
+ Lock(OpenMode::OPEN_NONE);
+ mDrawTarget = nullptr;
+ Unlock();
+ }
+#endif
+}
+
+bool
+D3D11TextureData::Lock(OpenMode aMode)
+{
+ if (!LockD3DTexture(mTexture.get())) {
+ return false;
+ }
+
+ if (NS_IsMainThread() && !mIsForOutOfBandContent) {
+ if (!PrepareDrawTargetInLock(aMode)) {
+ Unlock();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+DXGITextureData::PrepareDrawTargetInLock(OpenMode aMode)
+{
+ // Make sure that successful write-lock means we will have a DrawTarget to
+ // write into.
+ if (!mDrawTarget && (aMode & OpenMode::OPEN_WRITE || mNeedsClear || mNeedsClearWhite)) {
+ mDrawTarget = BorrowDrawTarget();
+ if (!mDrawTarget) {
+ return false;
+ }
+ }
+
+ if (mNeedsClear) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mNeedsClear = false;
+ }
+ if (mNeedsClearWhite) {
+ mDrawTarget->FillRect(Rect(0, 0, mSize.width, mSize.height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ mNeedsClearWhite = false;
+ }
+
+ return true;
+}
+
+void
+D3D11TextureData::Unlock()
+{
+ UnlockD3DTexture(mTexture.get());
+}
+
+
+void
+DXGITextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = mHasSynchronization;
+}
+
+void
+D3D11TextureData::SyncWithObject(SyncObject* aSyncObject)
+{
+ if (!aSyncObject || !NS_IsMainThread() || mIsForOutOfBandContent) {
+ // When off the main thread we sync using a keyed mutex per texture.
+ return;
+ }
+
+ MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11);
+ SyncObjectD3D11* sync = static_cast<SyncObjectD3D11*>(aSyncObject);
+ sync->RegisterTexture(mTexture);
+}
+
+bool
+DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ RefPtr<IDXGIResource> resource;
+ GetDXGIResource((IDXGIResource**)getter_AddRefs(resource));
+ if (!resource) {
+ return false;
+ }
+ HANDLE sharedHandle;
+ HRESULT hr = resource->GetSharedHandle(&sharedHandle);
+ if (FAILED(hr)) {
+ LOGD3D11("Error getting shared handle for texture.");
+ return false;
+ }
+
+ aOutDescriptor = SurfaceDescriptorD3D10((WindowsHandle)sharedHandle, mFormat, mSize);
+ return true;
+}
+
+DXGITextureData*
+DXGITextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags)
+{
+ if (aFormat == SurfaceFormat::A8) {
+ // Currently we don't support A8 surfaces. Fallback.
+ return nullptr;
+ }
+
+ return D3D11TextureData::Create(aSize, aFormat, aFlags);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat, SourceSurface* aSurface,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ // Just grab any device. We never use the immediate context, so the devices are fine
+ // to use from any thread.
+ RefPtr<ID3D11Device> device = aDevice;
+ if (!device) {
+ device = DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return nullptr;
+ }
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ aSize.width, aSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ if (!NS_IsMainThread() || !!(aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT)) {
+ // On the main thread we use the syncobject to handle synchronization.
+ if (!(aFlags & ALLOC_MANUAL_SYNCHRONIZATION)) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+ }
+
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX &&
+ !DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ D3D11_SUBRESOURCE_DATA uploadData;
+ D3D11_SUBRESOURCE_DATA* uploadDataPtr = nullptr;
+ RefPtr<DataSourceSurface> srcSurf;
+ if (aSurface) {
+ srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in D3D11TextureData::Create";
+ return nullptr;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for D3D11TextureData::Create";
+ return nullptr;
+ }
+
+ uploadData.pSysMem = sourceMap.mData;
+ uploadData.SysMemPitch = sourceMap.mStride;
+ uploadData.SysMemSlicePitch = 0; // unused
+
+ uploadDataPtr = &uploadData;
+ }
+
+ RefPtr<ID3D11Texture2D> texture11;
+ HRESULT hr = device->CreateTexture2D(&newDesc, uploadDataPtr, getter_AddRefs(texture11));
+ if (srcSurf) {
+ srcSurf->Unmap();
+ }
+ if (FAILED(hr)) {
+ gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
+ << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
+ return nullptr;
+ }
+
+ // If we created the texture with a keyed mutex, then we expect all operations
+ // on it to be synchronized using it. If we did an initial upload using aSurface
+ // then bizarely this isn't covered, so we insert a manual lock/unlock pair
+ // to force this.
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) {
+ if (!LockD3DTexture(texture11.get())) {
+ return nullptr;
+ }
+ UnlockD3DTexture(texture11.get());
+ }
+ texture11->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4));
+ return new D3D11TextureData(texture11, aSize, aFormat,
+ aFlags & ALLOC_CLEAR_BUFFER,
+ aFlags & ALLOC_CLEAR_BUFFER_WHITE,
+ aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ return D3D11TextureData::Create(aSize, aFormat, nullptr, aFlags, aDevice);
+}
+
+DXGITextureData*
+D3D11TextureData::Create(SourceSurface* aSurface,
+ TextureAllocationFlags aFlags, ID3D11Device* aDevice)
+{
+ if (aSurface->GetFormat() == SurfaceFormat::A8) {
+ // Currently we don't support A8 surfaces. Fallback.
+ return nullptr;
+ }
+
+ return D3D11TextureData::Create(aSurface->GetSize(), aSurface->GetFormat(),
+ aSurface, aFlags, aDevice);
+}
+
+void
+D3D11TextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ mDrawTarget = nullptr;
+ mTexture = nullptr;
+}
+
+already_AddRefed<TextureClient>
+CreateD3D11TextureClientWithDevice(IntSize aSize, SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator)
+{
+ TextureData* data = D3D11TextureData::Create(aSize, aFormat, aAllocFlags, aDevice);
+ if (!data) {
+ return nullptr;
+ }
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
+}
+
+TextureData*
+D3D11TextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ return D3D11TextureData::Create(mSize, mFormat, aAllocFlags);
+}
+
+void
+D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource)
+{
+ mTexture->QueryInterface(aOutResource);
+}
+
+DXGIYCbCrTextureData*
+DXGIYCbCrTextureData::Create(TextureFlags aFlags,
+ IUnknown* aTextureY,
+ IUnknown* aTextureCb,
+ IUnknown* aTextureCr,
+ HANDLE aHandleY,
+ HANDLE aHandleCb,
+ HANDLE aHandleCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr)
+{
+ if (!aHandleY || !aHandleCb || !aHandleCr ||
+ !aTextureY || !aTextureCb || !aTextureCr) {
+ return nullptr;
+ }
+
+ DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData();
+ texture->mHandles[0] = aHandleY;
+ texture->mHandles[1] = aHandleCb;
+ texture->mHandles[2] = aHandleCr;
+ texture->mHoldRefs[0] = aTextureY;
+ texture->mHoldRefs[1] = aTextureCb;
+ texture->mHoldRefs[2] = aTextureCr;
+ texture->mSize = aSize;
+ texture->mSizeY = aSizeY;
+ texture->mSizeCbCr = aSizeCbCr;
+
+ return texture;
+}
+
+DXGIYCbCrTextureData*
+DXGIYCbCrTextureData::Create(TextureFlags aFlags,
+ ID3D11Texture2D* aTextureY,
+ ID3D11Texture2D* aTextureCb,
+ ID3D11Texture2D* aTextureCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr)
+{
+ if (!aTextureY || !aTextureCb || !aTextureCr) {
+ return nullptr;
+ }
+
+ aTextureY->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSize.width * aSize.height));
+ aTextureCb->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+ aTextureCr->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+
+ RefPtr<IDXGIResource> resource;
+
+ aTextureY->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleY;
+ HRESULT hr = resource->GetSharedHandle(&handleY);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCb->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleCb;
+ hr = resource->GetSharedHandle(&handleCb);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCr->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+ HANDLE handleCr;
+ hr = resource->GetSharedHandle(&handleCr);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ return DXGIYCbCrTextureData::Create(aFlags,
+ aTextureY, aTextureCb, aTextureCr,
+ handleY, handleCb, handleCr,
+ aSize, aSizeY, aSizeCbCr);
+}
+
+void
+DXGIYCbCrTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::YUV;
+ aInfo.supportsMoz2D = false;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+}
+
+bool
+DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorDXGIYCbCr(
+ (WindowsHandle)mHandles[0], (WindowsHandle)mHandles[1], (WindowsHandle)mHandles[2],
+ mSize, mSizeY, mSizeCbCr
+ );
+ return true;
+}
+
+void
+DXGIYCbCrTextureData::Deallocate(LayersIPCChannel*)
+{
+ mHoldRefs[0] = nullptr;
+ mHoldRefs[1] = nullptr;
+ mHoldRefs[2] = nullptr;
+}
+
+already_AddRefed<TextureHost>
+CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+ result = new DXGITextureHostD3D11(aFlags,
+ aDesc.get_SurfaceDescriptorD3D10());
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
+ result = new DXGIYCbCrTextureHostD3D11(aFlags,
+ aDesc.get_SurfaceDescriptorDXGIYCbCr());
+ break;
+ }
+ default: {
+ NS_WARNING("Unsupported SurfaceDescriptor type");
+ }
+ }
+ return result.forget();
+}
+
+
+already_AddRefed<DrawTarget>
+D3D11TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mDrawTarget && mTexture) {
+ // This may return a null DrawTarget
+ mDrawTarget = Factory::CreateDrawTargetForD3D11Texture(mTexture, mFormat);
+ if (!mDrawTarget) {
+ gfxCriticalNote << "Could not borrow DrawTarget (D3D11) " << (int)mFormat;
+ }
+ }
+
+ RefPtr<DrawTarget> result = mDrawTarget;
+ return result.forget();
+}
+
+bool
+D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ // Supporting texture updates after creation requires an ID3D11DeviceContext and those
+ // aren't threadsafe. We'd need to either lock, or have a device for whatever thread
+ // this runs on and we're trying to avoid extra devices (bug 1284672).
+ MOZ_ASSERT(false, "UpdateFromSurface not supported for D3D11! Use CreateFromSurface instead");
+ return false;
+}
+
+DXGITextureHostD3D11::DXGITextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor)
+ : TextureHost(aFlags)
+ , mSize(aDescriptor.size())
+ , mHandle(aDescriptor.handle())
+ , mFormat(aDescriptor.format())
+ , mIsLocked(false)
+{
+}
+
+bool
+DXGITextureHostD3D11::OpenSharedHandle()
+{
+ if (!GetDevice()) {
+ return false;
+ }
+
+ HRESULT hr = GetDevice()->OpenSharedResource((HANDLE)mHandle,
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mTexture));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture");
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ mTexture->GetDesc(&desc);
+ mSize = IntSize(desc.Width, desc.Height);
+ return true;
+}
+
+RefPtr<ID3D11Device>
+DXGITextureHostD3D11::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ return DeviceManagerDx::Get()->GetCompositorDevice();
+}
+
+static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor)
+{
+ CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11()
+ : nullptr;
+ if (!compositor) {
+ gfxCriticalNote << "[D3D11] Attempt to set an incompatible compositor";
+ }
+ return compositor;
+}
+
+void
+DXGITextureHostD3D11::SetCompositor(Compositor* aCompositor)
+{
+ CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+ if (!d3dCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = d3dCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+Compositor*
+DXGITextureHostD3D11::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+DXGITextureHostD3D11::Lock()
+{
+ if (!mCompositor) {
+ // Make an early return here if we call SetCompositor() with an incompatible
+ // compositor. This check tries to prevent the problem where we use that
+ // incompatible compositor to compose this texture.
+ return false;
+ }
+
+ return LockInternal();
+}
+
+bool
+DXGITextureHostD3D11::LockWithoutCompositor()
+{
+ // Unlike the normal Lock() function, this function may be called when
+ // mCompositor is nullptr such as during WebVR frame submission. So, there is
+ // no 'mCompositor' checking here.
+ return LockInternal();
+}
+
+void
+DXGITextureHostD3D11::Unlock()
+{
+ UnlockInternal();
+}
+
+void
+DXGITextureHostD3D11::UnlockWithoutCompositor()
+{
+ UnlockInternal();
+}
+
+bool
+DXGITextureHostD3D11::LockInternal()
+{
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+
+ if (!mTextureSource) {
+ if (!mTexture && !OpenSharedHandle()) {
+ DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
+ return false;
+ }
+
+ mTextureSource = new DataTextureSourceD3D11(mFormat, mCompositor, mTexture);
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+void
+DXGITextureHostD3D11::UnlockInternal()
+{
+ UnlockD3DTexture(mTextureSource->GetD3D11Texture());
+}
+
+bool
+DXGITextureHostD3D11::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSource);
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor)
+ : TextureHost(aFlags)
+ , mSize(aDescriptor.size())
+ , mIsLocked(false)
+{
+ mHandles[0] = aDescriptor.handleY();
+ mHandles[1] = aDescriptor.handleCb();
+ mHandles[2] = aDescriptor.handleCr();
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::OpenSharedHandle()
+{
+ RefPtr<ID3D11Device> device = GetDevice();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<ID3D11Texture2D> textures[3];
+
+ HRESULT hr = device->OpenSharedResource((HANDLE)mHandles[0],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[0]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Y Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource((HANDLE)mHandles[1],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[1]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cb Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource((HANDLE)mHandles[2],
+ __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[2]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cr Plane");
+ return false;
+ }
+
+ mTextures[0] = textures[0].forget();
+ mTextures[1] = textures[1].forget();
+ mTextures[2] = textures[2].forget();
+
+ return true;
+}
+
+RefPtr<ID3D11Device>
+DXGIYCbCrTextureHostD3D11::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ return DeviceManagerDx::Get()->GetCompositorDevice();
+}
+
+void
+DXGIYCbCrTextureHostD3D11::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertD3D11Compositor(aCompositor);
+ if (!mCompositor) {
+ mTextureSources[0] = nullptr;
+ mTextureSources[1] = nullptr;
+ mTextureSources[2] = nullptr;
+ return;
+ }
+
+ if (mTextureSources[0]) {
+ mTextureSources[0]->SetCompositor(aCompositor);
+ }
+}
+
+Compositor*
+DXGIYCbCrTextureHostD3D11::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::Lock()
+{
+ if (!mCompositor) {
+ NS_WARNING("no suitable compositor");
+ return false;
+ }
+
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+ if (!mTextureSources[0]) {
+ if (!mTextures[0] && !OpenSharedHandle()) {
+ return false;
+ }
+
+ MOZ_ASSERT(mTextures[1] && mTextures[2]);
+
+ mTextureSources[0] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[0]);
+ mTextureSources[1] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[1]);
+ mTextureSources[2] = new DataTextureSourceD3D11(SurfaceFormat::A8, mCompositor, mTextures[2]);
+ mTextureSources[0]->SetNextSibling(mTextureSources[1]);
+ mTextureSources[1]->SetNextSibling(mTextureSources[2]);
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSources[0]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[1]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+void
+DXGIYCbCrTextureHostD3D11::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ UnlockD3DTexture(mTextureSources[0]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[1]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+ mIsLocked = false;
+}
+
+bool
+DXGIYCbCrTextureHostD3D11::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSources[0] && mTextureSources[1] && mTextureSources[2]);
+ aTexture = mTextureSources[0].get();
+ return !!aTexture;
+}
+
+bool
+DataTextureSourceD3D11::Update(DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ IntPoint* aSrcOffset)
+{
+ // Incremental update with a source offset is only used on Mac so it is not
+ // clear that we ever will need to support it for D3D.
+ MOZ_ASSERT(!aSrcOffset);
+ MOZ_ASSERT(aSurface);
+
+ MOZ_ASSERT(mAllowTextureUploads);
+ if (!mAllowTextureUploads) {
+ return false;
+ }
+
+ HRESULT hr;
+
+ if (!mCompositor || !mCompositor->GetDevice()) {
+ return false;
+ }
+
+ uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+ DXGI_FORMAT dxgiFormat = SurfaceFormatToDXGIFormat(aSurface->GetFormat());
+
+ mSize = aSurface->GetSize();
+ mFormat = aSurface->GetFormat();
+
+ CD3D11_TEXTURE2D_DESC desc(dxgiFormat, mSize.width, mSize.height, 1, 1);
+
+ int32_t maxSize = mCompositor->GetMaxTextureSize();
+ if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
+ (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+
+ if (mTexture) {
+ D3D11_TEXTURE2D_DESC currentDesc;
+ mTexture->GetDesc(&currentDesc);
+
+ // Make sure there's no size mismatch, if there is, recreate.
+ if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height ||
+ currentDesc.Format != dxgiFormat) {
+ mTexture = nullptr;
+ // Make sure we upload the whole surface.
+ aDestRegion = nullptr;
+ }
+ }
+
+ nsIntRegion *regionToUpdate = aDestRegion;
+ if (!mTexture) {
+ hr = mCompositor->GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture));
+ mIsTiled = false;
+ if (FAILED(hr) || !mTexture) {
+ Reset();
+ return false;
+ }
+
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ regionToUpdate = nullptr;
+ }
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ gfxCriticalError() << "Failed to map surface.";
+ Reset();
+ return false;
+ }
+
+ if (regionToUpdate) {
+ for (auto iter = regionToUpdate->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = rect.x;
+ box.top = rect.y;
+ box.right = rect.XMost();
+ box.bottom = rect.YMost();
+
+ void* data = map.mData + map.mStride * rect.y + BytesPerPixel(aSurface->GetFormat()) * rect.x;
+
+ mCompositor->GetDC()->UpdateSubresource(mTexture, 0, &box, data, map.mStride, map.mStride * rect.height);
+ }
+ } else {
+ mCompositor->GetDC()->UpdateSubresource(mTexture, 0, nullptr, aSurface->GetData(),
+ aSurface->Stride(), aSurface->Stride() * mSize.height);
+ }
+
+ aSurface->Unmap();
+ } else {
+ mIsTiled = true;
+ uint32_t tileCount = GetRequiredTilesD3D11(mSize.width, maxSize) *
+ GetRequiredTilesD3D11(mSize.height, maxSize);
+
+ mTileTextures.resize(tileCount);
+ mTileSRVs.resize(tileCount);
+ mTexture = nullptr;
+
+ for (uint32_t i = 0; i < tileCount; i++) {
+ IntRect tileRect = GetTileRect(i);
+
+ desc.Width = tileRect.width;
+ desc.Height = tileRect.height;
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+
+ D3D11_SUBRESOURCE_DATA initData;
+ initData.pSysMem = aSurface->GetData() +
+ tileRect.y * aSurface->Stride() +
+ tileRect.x * bpp;
+ initData.SysMemPitch = aSurface->Stride();
+
+ hr = mCompositor->GetDevice()->CreateTexture2D(&desc, &initData, getter_AddRefs(mTileTextures[i]));
+ if (FAILED(hr) || !mTileTextures[i]) {
+ Reset();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ID3D11Texture2D*
+DataTextureSourceD3D11::GetD3D11Texture() const
+{
+ return mIterating ? mTileTextures[mCurrentTile]
+ : mTexture;
+}
+
+ID3D11ShaderResourceView*
+DataTextureSourceD3D11::GetShaderResourceView()
+{
+ if (mIterating) {
+ if (!mTileSRVs[mCurrentTile]) {
+ if (!mTileTextures[mCurrentTile]) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ mTileTextures[mCurrentTile]->GetDevice(getter_AddRefs(device));
+ HRESULT hr = device->CreateShaderResourceView(mTileTextures[mCurrentTile], nullptr, getter_AddRefs(mTileSRVs[mCurrentTile]));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "[D3D11] DataTextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mTileSRVs[mCurrentTile];
+ }
+
+ return TextureSourceD3D11::GetShaderResourceView();
+}
+
+void
+DataTextureSourceD3D11::Reset()
+{
+ mTexture = nullptr;
+ mTileSRVs.resize(0);
+ mTileTextures.resize(0);
+ mIsTiled = false;
+ mSize.width = 0;
+ mSize.height = 0;
+}
+
+IntRect
+DataTextureSourceD3D11::GetTileRect(uint32_t aIndex) const
+{
+ return GetTileRectD3D11(aIndex, mSize, mCompositor->GetMaxTextureSize());
+}
+
+IntRect
+DataTextureSourceD3D11::GetTileRect()
+{
+ IntRect rect = GetTileRect(mCurrentTile);
+ return IntRect(rect.x, rect.y, rect.width, rect.height);
+}
+
+void
+DataTextureSourceD3D11::SetCompositor(Compositor* aCompositor)
+{
+ CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+ if (!d3dCompositor) {
+ return;
+ }
+ mCompositor = d3dCompositor;
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+CompositingRenderTargetD3D11::CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
+ const gfx::IntPoint& aOrigin,
+ DXGI_FORMAT aFormatOverride)
+ : CompositingRenderTarget(aOrigin)
+{
+ MOZ_ASSERT(aTexture);
+
+ mTexture = aTexture;
+
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ mFormatOverride = aFormatOverride;
+
+ // If we happen to have a typeless underlying DXGI surface, we need to be explicit
+ // about the format here. (Such a surface could come from an external source, such
+ // as the Oculus compositor)
+ CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D, mFormatOverride);
+ D3D11_RENDER_TARGET_VIEW_DESC *desc = aFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &rtvDesc;
+
+ HRESULT hr = device->CreateRenderTargetView(mTexture, desc, getter_AddRefs(mRTView));
+
+ if (FAILED(hr)) {
+ LOGD3D11("Failed to create RenderTargetView.");
+ }
+}
+
+void
+CompositingRenderTargetD3D11::BindRenderTarget(ID3D11DeviceContext* aContext)
+{
+ if (mClearOnBind) {
+ FLOAT clear[] = { 0, 0, 0, 0 };
+ aContext->ClearRenderTargetView(mRTView, clear);
+ mClearOnBind = false;
+ }
+ ID3D11RenderTargetView* view = mRTView;
+ aContext->OMSetRenderTargets(1, &view, nullptr);
+}
+
+IntSize
+CompositingRenderTargetD3D11::GetSize() const
+{
+ return TextureSourceD3D11::GetSize();
+}
+
+SyncObjectD3D11::SyncObjectD3D11(SyncHandle aHandle)
+{
+ MOZ_ASSERT(aHandle);
+ mD3D11Device = DeviceManagerDx::Get()->GetContentDevice();
+ mHandle = aHandle;
+}
+
+void
+SyncObjectD3D11::RegisterTexture(ID3D11Texture2D* aTexture)
+{
+ mD3D11SyncedTextures.push_back(aTexture);
+}
+
+bool
+SyncObjectD3D11::IsSyncObjectValid()
+{
+ RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
+ if (!dev || (dev != mD3D11Device)) {
+ return false;
+ }
+ return true;
+}
+
+void
+SyncObjectD3D11::FinalizeFrame()
+{
+ HRESULT hr;
+
+ if (!mD3D11Texture && mD3D11SyncedTextures.size()) {
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetContentDevice();
+
+ hr = device->OpenSharedResource(mHandle, __uuidof(ID3D11Texture2D), (void**)(ID3D11Texture2D**)getter_AddRefs(mD3D11Texture));
+
+ if (FAILED(hr) || !mD3D11Texture) {
+ gfxCriticalError() << "Failed to D3D11 OpenSharedResource for frame finalization: " << hexa(hr);
+
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ return;
+ }
+
+ gfxDevCrash(LogReason::D3D11FinalizeFrame) << "Without device reset: " << hexa(hr);
+ }
+
+ // test QI
+ RefPtr<IDXGIKeyedMutex> mutex;
+ hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+ if (FAILED(hr) || !mutex) {
+ // Leave both the critical error and MOZ_CRASH for now; the critical error lets
+ // us "save" the hr value. We will probably eventuall replace this with gfxDevCrash.
+ gfxCriticalError() << "Failed to get KeyedMutex (2): " << hexa(hr);
+ MOZ_CRASH("GFX: Cannot get D3D11 KeyedMutex");
+ }
+ }
+
+ if (mD3D11SyncedTextures.size()) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ {
+ AutoTextureLock lock(mutex, hr, 20000);
+
+ if (hr == WAIT_TIMEOUT) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ gfxWarning() << "AcquireSync timed out because of device reset.";
+ return;
+ }
+ gfxDevCrash(LogReason::D3D11SyncLock) << "Timeout on the D3D11 sync lock";
+ }
+
+ D3D11_BOX box;
+ box.front = box.top = box.left = 0;
+ box.back = box.bottom = box.right = 1;
+
+ RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
+ if (!dev) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ return;
+ }
+ MOZ_CRASH("GFX: Invalid D3D11 content device");
+ }
+
+ RefPtr<ID3D11DeviceContext> ctx;
+ dev->GetImmediateContext(getter_AddRefs(ctx));
+
+ for (auto iter = mD3D11SyncedTextures.begin(); iter != mD3D11SyncedTextures.end(); iter++) {
+ ctx->CopySubresourceRegion(mD3D11Texture, 0, 0, 0, 0, *iter, 0, &box);
+ }
+ }
+
+ mD3D11SyncedTextures.clear();
+ }
+}
+
+}
+}
diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h
new file mode 100644
index 000000000..831342aa2
--- /dev/null
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -0,0 +1,455 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURED3D11_H
+#define MOZILLA_GFX_TEXTURED3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/GfxMessageUtils.h"
+#include <d3d11.h>
+#include "d3d9.h"
+#include <vector>
+
+namespace mozilla {
+namespace layers {
+
+class MOZ_RAII AutoTextureLock
+{
+public:
+ AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
+ uint32_t aTimeout = 0);
+ ~AutoTextureLock();
+
+private:
+ RefPtr<IDXGIKeyedMutex> mMutex;
+ HRESULT mResult;
+};
+
+class CompositorD3D11;
+
+class DXGITextureData : public TextureData
+{
+public:
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
+
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
+
+protected:
+ bool PrepareDrawTargetInLock(OpenMode aMode);
+
+ DXGITextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent);
+
+ virtual void GetDXGIResource(IDXGIResource** aOutResource) = 0;
+
+ // Hold on to the DrawTarget because it is expensive to create one each ::Lock.
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ bool mNeedsClear;
+ bool mNeedsClearWhite;
+ bool mHasSynchronization;
+ bool mIsForOutOfBandContent;
+};
+
+class D3D11TextureData : public DXGITextureData
+{
+public:
+ // If aDevice is null, use one provided by gfxWindowsPlatform.
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+ static DXGITextureData*
+ Create(gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const override;
+
+ virtual void SyncWithObject(SyncObject* aSync) override;
+
+ ID3D11Texture2D* GetD3D11Texture() { return mTexture; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ D3D11TextureData* AsD3D11TextureData() override {
+ return this;
+ }
+
+ ~D3D11TextureData();
+protected:
+ D3D11TextureData(ID3D11Texture2D* aTexture,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aNeedsClear, bool aNeedsClearWhite,
+ bool aIsForOutOfBandContent);
+
+ virtual void GetDXGIResource(IDXGIResource** aOutResource) override;
+
+ static DXGITextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ RefPtr<ID3D11Texture2D> mTexture;
+};
+
+already_AddRefed<TextureClient>
+CreateD3D11extureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator);
+
+class DXGIYCbCrTextureData : public TextureData
+{
+public:
+ static DXGIYCbCrTextureData*
+ Create(TextureFlags aFlags,
+ IUnknown* aTextureY,
+ IUnknown* aTextureCb,
+ IUnknown* aTextureCr,
+ HANDLE aHandleY,
+ HANDLE aHandleCb,
+ HANDLE aHandleCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr);
+
+ static DXGIYCbCrTextureData*
+ Create(TextureFlags aFlags,
+ ID3D11Texture2D* aTextureCb,
+ ID3D11Texture2D* aTextureY,
+ ID3D11Texture2D* aTextureCr,
+ const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY,
+ const gfx::IntSize& aSizeCbCr);
+
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override { return nullptr; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface*) override { return false; }
+
+ virtual TextureFlags GetTextureFlags() const override
+ {
+ return TextureFlags::DEALLOCATE_MAIN_THREAD;
+ }
+
+protected:
+ RefPtr<IUnknown> mHoldRefs[3];
+ HANDLE mHandles[3];
+ gfx::IntSize mSize;
+ gfx::IntSize mSizeY;
+ gfx::IntSize mSizeCbCr;
+};
+
+/**
+ * TextureSource that provides with the necessary APIs to be composited by a
+ * CompositorD3D11.
+ */
+class TextureSourceD3D11
+{
+public:
+ TextureSourceD3D11() : mFormatOverride(DXGI_FORMAT_UNKNOWN) {}
+ virtual ~TextureSourceD3D11() {}
+
+ virtual ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
+ virtual ID3D11ShaderResourceView* GetShaderResourceView();
+protected:
+ virtual gfx::IntSize GetSize() const { return mSize; }
+
+ gfx::IntSize mSize;
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID3D11ShaderResourceView> mSRV;
+ DXGI_FORMAT mFormatOverride;
+};
+
+/**
+ * A TextureSource that implements the DataTextureSource interface.
+ * it can be used without a TextureHost and is able to upload texture data
+ * from a gfx::DataSourceSurface.
+ */
+class DataTextureSourceD3D11 : public DataTextureSource
+ , public TextureSourceD3D11
+ , public BigImageIterator
+{
+public:
+ /// Constructor allowing the texture to perform texture uploads.
+ ///
+ /// The texture can be used as an actual DataTextureSource.
+ DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
+ TextureFlags aFlags);
+
+ /// Constructor for textures created around DXGI shared handles, disallowing
+ /// texture uploads.
+ ///
+ /// The texture CANNOT be used as a DataTextureSource.
+ DataTextureSourceD3D11(gfx::SurfaceFormat aFormat, CompositorD3D11* aCompositor,
+ ID3D11Texture2D* aTexture);
+
+ virtual ~DataTextureSourceD3D11();
+
+ virtual const char* Name() const override { return "DataTextureSourceD3D11"; }
+
+ // DataTextureSource
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ // TextureSource
+
+ virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
+
+ virtual ID3D11Texture2D* GetD3D11Texture() const override;
+
+ virtual ID3D11ShaderResourceView* GetShaderResourceView() override;
+
+ // Returns nullptr if this texture was created by a DXGI TextureHost.
+ virtual DataTextureSource* AsDataTextureSource() override { return mAllowTextureUploads ? this : false; }
+
+ virtual void DeallocateDeviceData() override { mTexture = nullptr; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ // BigImageIterator
+
+ virtual BigImageIterator* AsBigImageIterator() override { return mIsTiled ? this : nullptr; }
+
+ virtual size_t GetTileCount() override { return mTileTextures.size(); }
+
+ virtual bool NextTile() override { return (++mCurrentTile < mTileTextures.size()); }
+
+ virtual gfx::IntRect GetTileRect() override;
+
+ virtual void EndBigImageIteration() override { mIterating = false; }
+
+ virtual void BeginBigImageIteration() override
+ {
+ mIterating = true;
+ mCurrentTile = 0;
+ }
+
+protected:
+ gfx::IntRect GetTileRect(uint32_t aIndex) const;
+
+ void Reset();
+
+ std::vector< RefPtr<ID3D11Texture2D> > mTileTextures;
+ std::vector< RefPtr<ID3D11ShaderResourceView> > mTileSRVs;
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::SurfaceFormat mFormat;
+ TextureFlags mFlags;
+ uint32_t mCurrentTile;
+ bool mIsTiled;
+ bool mIterating;
+ // Sadly, the code was originally organized so that this class is used both in
+ // the cases where we want to perform texture uploads through the DataTextureSource
+ // interface, and the cases where we wrap the texture around an existing DXGI
+ // handle in which case we should not use it as a DataTextureSource.
+ // This member differentiates the two scenarios. When it is false the texture
+ // "pretends" to not be a DataTextureSource.
+ bool mAllowTextureUploads;
+};
+
+already_AddRefed<TextureClient>
+CreateD3D11TextureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ LayersIPCChannel* aAllocator);
+
+
+/**
+ * A TextureHost for shared D3D11 textures.
+ */
+class DXGITextureHostD3D11 : public TextureHost
+{
+public:
+ DXGITextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual bool Lock() override;
+ virtual void Unlock() override;
+
+ virtual bool LockWithoutCompositor() override;
+ virtual void UnlockWithoutCompositor() override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+protected:
+ bool LockInternal();
+ void UnlockInternal();
+
+ RefPtr<ID3D11Device> GetDevice();
+
+ bool OpenSharedHandle();
+
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<DataTextureSourceD3D11> mTextureSource;
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::IntSize mSize;
+ WindowsHandle mHandle;
+ gfx::SurfaceFormat mFormat;
+ bool mIsLocked;
+};
+
+class DXGIYCbCrTextureHostD3D11 : public TextureHost
+{
+public:
+ DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override{}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override{ return gfx::SurfaceFormat::YUV; }
+
+ // Bug 1305906 fixes YUVColorSpace handling
+ virtual YUVColorSpace GetYUVColorSpace() const override { return YUVColorSpace::BT601; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+protected:
+ RefPtr<ID3D11Device> GetDevice();
+
+ bool OpenSharedHandle();
+
+ RefPtr<ID3D11Texture2D> mTextures[3];
+ RefPtr<DataTextureSourceD3D11> mTextureSources[3];
+
+ RefPtr<CompositorD3D11> mCompositor;
+ gfx::IntSize mSize;
+ WindowsHandle mHandles[3];
+ bool mIsLocked;
+};
+
+class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
+ public TextureSourceD3D11
+{
+public:
+ CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
+ const gfx::IntPoint& aOrigin,
+ DXGI_FORMAT aFormatOverride = DXGI_FORMAT_UNKNOWN);
+
+ virtual const char* Name() const override { return "CompositingRenderTargetD3D11"; }
+
+ virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
+
+ void BindRenderTarget(ID3D11DeviceContext* aContext);
+
+ virtual gfx::IntSize GetSize() const override;
+
+ void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }
+
+private:
+ friend class CompositorD3D11;
+ RefPtr<ID3D11RenderTargetView> mRTView;
+};
+
+class SyncObjectD3D11 : public SyncObject
+{
+public:
+ SyncObjectD3D11(SyncHandle aSyncHandle);
+ virtual void FinalizeFrame();
+ virtual bool IsSyncObjectValid();
+
+ virtual SyncType GetSyncType() { return SyncType::D3D11; }
+
+ void RegisterTexture(ID3D11Texture2D* aTexture);
+
+private:
+ RefPtr<ID3D11Texture2D> mD3D11Texture;
+ RefPtr<ID3D11Device> mD3D11Device;
+ std::vector<ID3D11Texture2D*> mD3D11SyncedTextures;
+ SyncHandle mHandle;
+};
+
+inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel)
+{
+ int32_t maxTextureSize;
+ switch (aFeatureLevel) {
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ maxTextureSize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ case D3D_FEATURE_LEVEL_9_3:
+ maxTextureSize = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ break;
+ default:
+ maxTextureSize = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ }
+ return maxTextureSize;
+}
+
+}
+}
+
+#endif /* MOZILLA_GFX_TEXTURED3D11_H */
diff --git a/gfx/layers/d3d11/genshaders.sh b/gfx/layers/d3d11/genshaders.sh
new file mode 100644
index 000000000..0928e3f49
--- /dev/null
+++ b/gfx/layers/d3d11/genshaders.sh
@@ -0,0 +1,50 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+tempfile=tmpShaderHeader
+
+FXC_DEBUG_FLAGS="-Zi -Fd shaders.pdb"
+FXC_FLAGS=""
+
+# If DEBUG is in the environment, then rebuild with debug info
+if [ "$DEBUG" != "" ] ; then
+ FXC_FLAGS="$FXC_DEBUG_FLAGS"
+fi
+
+makeShaderVS() {
+ fxc -nologo $FXC_FLAGS -Tvs_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
+ echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
+ cat $tempfile >> $DEST
+}
+
+makeShaderPS() {
+ fxc -nologo $FXC_FLAGS -Tps_4_0_level_9_3 $SRC -E$1 -Vn$1 -Fh$tempfile
+ echo "ShaderBytes s$1 = { $1, sizeof($1) };" >> $tempfile;
+ cat $tempfile >> $DEST
+}
+
+SRC=CompositorD3D11.hlsl
+DEST=CompositorD3D11Shaders.h
+
+rm -f $DEST
+echo "struct ShaderBytes { const void* mData; size_t mLength; };" >> $DEST;
+makeShaderVS LayerQuadVS
+makeShaderPS SolidColorShader
+makeShaderPS RGBShader
+makeShaderPS RGBAShader
+makeShaderPS ComponentAlphaShader
+makeShaderPS YCbCrShader
+makeShaderVS LayerQuadMaskVS
+makeShaderPS SolidColorShaderMask
+makeShaderPS RGBShaderMask
+makeShaderPS RGBAShaderMask
+makeShaderPS YCbCrShaderMask
+makeShaderPS ComponentAlphaShaderMask
+
+# Mix-blend shaders
+makeShaderVS LayerQuadBlendVS
+makeShaderVS LayerQuadBlendMaskVS
+makeShaderPS BlendShader
+
+rm $tempfile
diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp
new file mode 100644
index 000000000..0f7e942c1
--- /dev/null
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -0,0 +1,1045 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorD3D9.h"
+#include "LayerManagerD3D9Shaders.h"
+#include "gfxWindowsPlatform.h"
+#include "nsIWidget.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/ContentHost.h"
+#include "mozilla/layers/Effects.h"
+#include "nsWindowsHelpers.h"
+#include "Nv3DVUtils.h"
+#include "gfxFailure.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "gfxPrefs.h"
+#include "gfxCrashReporterUtils.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+#include "D3D9SurfaceImage.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CompositorD3D9::CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mDeviceResetCount(0)
+ , mFailedResetAttempts(0)
+{
+}
+
+CompositorD3D9::~CompositorD3D9()
+{
+ mSwapChain = nullptr;
+ mDeviceManager = nullptr;
+}
+
+bool
+CompositorD3D9::Initialize(nsCString* const out_failureReason)
+{
+ ScopedGfxFeatureReporter reporter("D3D9 Layers");
+
+ mDeviceManager = DeviceManagerD3D9::Get();
+ if (!mDeviceManager) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_DEVICE_MANAGER";
+ return false;
+ }
+
+ mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
+ if (!mSwapChain) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_SWAP_CHAIN";
+ return false;
+ }
+
+ if (!mWidget->InitCompositor(this)) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_INIT_COMPOSITOR";
+ return false;
+ }
+
+ reporter.SetSuccessful();
+
+ return true;
+}
+
+TextureFactoryIdentifier
+CompositorD3D9::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident;
+ ident.mMaxTextureSize = GetMaxTextureSize();
+ ident.mParentBackend = LayersBackend::LAYERS_D3D9;
+ ident.mParentProcessType = XRE_GetProcessType();
+ ident.mSupportsComponentAlpha = SupportsEffect(EffectTypes::COMPONENT_ALPHA);
+ return ident;
+}
+
+bool
+CompositorD3D9::CanUseCanvasLayerForSize(const IntSize &aSize)
+{
+ int32_t maxTextureSize = GetMaxTextureSize();
+
+ if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
+ return false;
+ }
+
+ return true;
+}
+
+int32_t
+CompositorD3D9::GetMaxTextureSize() const
+{
+ return mDeviceManager ? mDeviceManager->GetMaxTextureSize() : INT32_MAX;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorD3D9::CreateDataTextureSource(TextureFlags aFlags)
+{
+ return MakeAndAddRef<DataTextureSourceD3D9>(SurfaceFormat::UNKNOWN, this, aFlags);
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D9::CreateRenderTarget(const gfx::IntRect &aRect,
+ SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!mDeviceManager) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HRESULT hr = device()->CreateTexture(aRect.width, aRect.height, 1,
+ D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT, getter_AddRefs(texture),
+ nullptr);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTarget: Failed to create texture"),
+ hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<CompositingRenderTargetD3D9>(texture, aInit, aRect);
+}
+
+already_AddRefed<IDirect3DTexture9>
+CompositorD3D9::CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!mDeviceManager) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HRESULT hr = device()->CreateTexture(aRect.width, aRect.height, 1,
+ D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT, getter_AddRefs(texture),
+ nullptr);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTargetFromSource: Failed to create texture"),
+ hr);
+ return nullptr;
+ }
+
+ if (aSource) {
+ RefPtr<IDirect3DSurface9> sourceSurface =
+ static_cast<const CompositingRenderTargetD3D9*>(aSource)->GetD3D9Surface();
+
+ RefPtr<IDirect3DSurface9> destSurface;
+ hr = texture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to get texture surface level for dest.");
+ }
+
+ if (sourceSurface && destSurface) {
+ RECT sourceRect;
+ sourceRect.left = aSourcePoint.x;
+ sourceRect.right = aSourcePoint.x + aRect.width;
+ sourceRect.top = aSourcePoint.y;
+ sourceRect.bottom = aSourcePoint.y + aRect.height;
+ RECT destRect;
+ destRect.left = 0;
+ destRect.right = aRect.width;
+ destRect.top = 0;
+ destRect.bottom = aRect.height;
+
+ // copy the source to the dest
+ hr = device()->StretchRect(sourceSurface,
+ &sourceRect,
+ destSurface,
+ &destRect,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTargetFromSource: Failed to update texture"),
+ hr);
+ }
+ }
+ }
+
+ return texture.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint)
+{
+ RefPtr<IDirect3DTexture9> texture = CreateTexture(aRect, aSource, aSourcePoint);
+
+ if (!texture) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<CompositingRenderTargetD3D9>(texture,
+ INIT_MODE_NONE,
+ aRect);
+}
+
+void
+CompositorD3D9::SetRenderTarget(CompositingRenderTarget *aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget && mDeviceManager);
+ RefPtr<CompositingRenderTargetD3D9> oldRT = mCurrentRT;
+ mCurrentRT = static_cast<CompositingRenderTargetD3D9*>(aRenderTarget);
+ mCurrentRT->BindRenderTarget(device());
+ PrepareViewport(mCurrentRT->GetSize());
+}
+
+static DeviceManagerD3D9::ShaderMode
+ShaderModeForEffectType(EffectTypes aEffectType, gfx::SurfaceFormat aFormat)
+{
+ switch (aEffectType) {
+ case EffectTypes::SOLID_COLOR:
+ return DeviceManagerD3D9::SOLIDCOLORLAYER;
+ case EffectTypes::RENDER_TARGET:
+ return DeviceManagerD3D9::RGBALAYER;
+ case EffectTypes::RGB:
+ if (aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::R8G8B8A8)
+ return DeviceManagerD3D9::RGBALAYER;
+ return DeviceManagerD3D9::RGBLAYER;
+ case EffectTypes::YCBCR:
+ return DeviceManagerD3D9::YCBCRLAYER;
+ }
+
+ MOZ_CRASH("GFX: Bad effect type");
+}
+
+void
+CompositorD3D9::ClearRect(const gfx::Rect& aRect)
+{
+ D3DRECT rect;
+ rect.x1 = aRect.X();
+ rect.y1 = aRect.Y();
+ rect.x2 = aRect.XMost();
+ rect.y2 = aRect.YMost();
+
+ device()->Clear(1, &rect, D3DCLEAR_TARGET,
+ 0x00000000, 0, 0);
+}
+
+void
+CompositorD3D9::DrawQuad(const gfx::Rect &aRect,
+ const gfx::IntRect &aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ if (!mDeviceManager) {
+ return;
+ }
+
+ IDirect3DDevice9* d3d9Device = device();
+ MOZ_ASSERT(d3d9Device, "We should be able to get a device now");
+
+ MOZ_ASSERT(mCurrentRT, "No render target");
+ d3d9Device->SetVertexShaderConstantF(CBmLayerTransform, &aTransform._11, 4);
+
+ IntPoint origin = mCurrentRT->GetOrigin();
+ float renderTargetOffset[] = { float(origin.x), float(origin.y), 0, 0 };
+ d3d9Device->SetVertexShaderConstantF(CBvRenderTargetOffset,
+ renderTargetOffset,
+ 1);
+ d3d9Device->SetVertexShaderConstantF(CBvLayerQuad,
+ ShaderConstantRect(aRect.x,
+ aRect.y,
+ aRect.width,
+ aRect.height),
+ 1);
+
+ if (aEffectChain.mPrimaryEffect->mType != EffectTypes::SOLID_COLOR) {
+ float opacity[4];
+ /*
+ * We always upload a 4 component float, but the shader will use only the
+ * first component since it's declared as a 'float'.
+ */
+ opacity[0] = aOpacity;
+ d3d9Device->SetPixelShaderConstantF(CBfLayerOpacity, opacity, 1);
+ }
+
+ bool isPremultiplied = true;
+
+ MaskType maskType = MaskType::MaskNone;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ maskType = MaskType::Mask;
+ }
+
+ gfx::Rect backdropDest;
+ gfx::IntRect backdropRect;
+ gfx::Matrix4x4 backdropTransform;
+ RefPtr<IDirect3DTexture9> backdropTexture;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+
+ // Pixel Shader Model 2.0 is too limited to perform blending in the same way
+ // as Direct3D 11 - there are too many instructions, and we don't have
+ // configurable shaders (as we do with OGL) that would avoid a huge shader
+ // matrix.
+ //
+ // Instead, we use a multi-step process for blending on D3D9:
+ // (1) Capture the backdrop into a temporary surface.
+ // (2) Render the effect chain onto the backdrop, with OP_SOURCE.
+ // (3) Capture the backdrop again into another surface - these are our source pixels.
+ // (4) Perform a final blend step using software.
+ // (5) Blit the blended result back to the render target.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ backdropRect = ComputeBackdropCopyRect(
+ aRect, aClipRect, aTransform, &backdropTransform, &backdropDest);
+
+ // If this fails, don't set a blend op.
+ backdropTexture = CreateTexture(backdropRect, mCurrentRT, backdropRect.TopLeft());
+ if (!backdropTexture) {
+ blendMode = gfx::CompositionOp::OP_OVER;
+ }
+ }
+ }
+
+ RECT scissor;
+ scissor.left = aClipRect.x;
+ scissor.right = aClipRect.XMost();
+ scissor.top = aClipRect.y;
+ scissor.bottom = aClipRect.YMost();
+ d3d9Device->SetScissorRect(&scissor);
+
+ uint32_t maskTexture = 0;
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ {
+ // output color is premultiplied, so we need to adjust all channels.
+ Color layerColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
+ float color[4];
+ color[0] = layerColor.r * layerColor.a * aOpacity;
+ color[1] = layerColor.g * layerColor.a * aOpacity;
+ color[2] = layerColor.b * layerColor.a * aOpacity;
+ color[3] = layerColor.a * aOpacity;
+
+ d3d9Device->SetPixelShaderConstantF(CBvColor, color, 1);
+
+ maskTexture = mDeviceManager
+ ->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER, maskType);
+ }
+ break;
+ case EffectTypes::RENDER_TARGET:
+ case EffectTypes::RGB:
+ {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ Rect textureCoords = texturedEffect->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
+
+ TextureSourceD3D9* source = texturedEffect->mTexture->AsSourceD3D9();
+ d3d9Device->SetTexture(0, source->GetD3D9Texture());
+
+ maskTexture = mDeviceManager
+ ->SetShaderMode(ShaderModeForEffectType(aEffectChain.mPrimaryEffect->mType,
+ texturedEffect->mTexture->GetFormat()),
+ maskType);
+
+ isPremultiplied = texturedEffect->mPremultiplied;
+ }
+ break;
+ case EffectTypes::YCBCR:
+ {
+ EffectYCbCr* ycbcrEffect =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+
+ SetSamplerForSamplingFilter(SamplingFilter::LINEAR);
+
+ Rect textureCoords = ycbcrEffect->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSource* source = ycbcrEffect->mTexture;
+
+ if (!source) {
+ NS_WARNING("No texture to composite");
+ return;
+ }
+
+ if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || !source->GetSubSource(Cr)) {
+ // This can happen if we failed to upload the textures, most likely
+ // because of unsupported dimensions (we don't tile YCbCr textures).
+ return;
+ }
+
+
+ float* yuvToRgb = gfxUtils::Get4x3YuvColorMatrix(ycbcrEffect->mYUVColorSpace);
+ d3d9Device->SetPixelShaderConstantF(CBmYuvColorMatrix, yuvToRgb, 3);
+
+ TextureSourceD3D9* sourceY = source->GetSubSource(Y)->AsSourceD3D9();
+ TextureSourceD3D9* sourceCb = source->GetSubSource(Cb)->AsSourceD3D9();
+ TextureSourceD3D9* sourceCr = source->GetSubSource(Cr)->AsSourceD3D9();
+
+
+ MOZ_ASSERT(sourceY->GetD3D9Texture());
+ MOZ_ASSERT(sourceCb->GetD3D9Texture());
+ MOZ_ASSERT(sourceCr->GetD3D9Texture());
+
+ /*
+ * Send 3d control data and metadata
+ */
+ if (mDeviceManager->GetNv3DVUtils()) {
+ Nv_Stereo_Mode mode;
+ switch (source->AsSourceD3D9()->GetStereoMode()) {
+ case StereoMode::LEFT_RIGHT:
+ mode = NV_STEREO_MODE_LEFT_RIGHT;
+ break;
+ case StereoMode::RIGHT_LEFT:
+ mode = NV_STEREO_MODE_RIGHT_LEFT;
+ break;
+ case StereoMode::BOTTOM_TOP:
+ mode = NV_STEREO_MODE_BOTTOM_TOP;
+ break;
+ case StereoMode::TOP_BOTTOM:
+ mode = NV_STEREO_MODE_TOP_BOTTOM;
+ break;
+ case StereoMode::MONO:
+ mode = NV_STEREO_MODE_MONO;
+ break;
+ }
+
+ // Send control data even in mono case so driver knows to leave stereo mode.
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE);
+
+ if (source->AsSourceD3D9()->GetStereoMode() != StereoMode::MONO) {
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE);
+
+ RefPtr<IDirect3DSurface9> renderTarget;
+ d3d9Device->GetRenderTarget(0, getter_AddRefs(renderTarget));
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)aRect.width,
+ (unsigned int)aRect.height,
+ (HANDLE)(sourceY->GetD3D9Texture()),
+ (HANDLE)(renderTarget));
+ }
+ }
+
+ // Linear scaling is default here, adhering to mFilter is difficult since
+ // presumably even with point filtering we'll still want chroma upsampling
+ // to be linear. In the current approach we can't.
+ device()->SetTexture(Y, sourceY->GetD3D9Texture());
+ device()->SetTexture(Cb, sourceCb->GetD3D9Texture());
+ device()->SetTexture(Cr, sourceCr->GetD3D9Texture());
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER, maskType);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceD3D9* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D9();
+ TextureSourceD3D9* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D9();
+
+ Rect textureCoords = effectComponentAlpha->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);
+
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1, maskType);
+ SetMask(aEffectChain, maskTexture);
+ d3d9Device->SetTexture(0, sourceOnBlack->GetD3D9Texture());
+ d3d9Device->SetTexture(1, sourceOnWhite->GetD3D9Texture());
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS2, maskType);
+ SetMask(aEffectChain, maskTexture);
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ // Restore defaults
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ d3d9Device->SetTexture(1, nullptr);
+ }
+ return;
+ default:
+ NS_WARNING("Unknown shader type");
+ return;
+ }
+
+ SetMask(aEffectChain, maskTexture);
+
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ // Use SOURCE instead of OVER to get the original source pixels without
+ // having to render to another intermediate target.
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
+ }
+ if (!isPremultiplied) {
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ }
+
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ // Restore defaults.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ }
+ if (!isPremultiplied) {
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ }
+
+ // Final pass - if mix-blending, do it now that we have the backdrop and
+ // source textures.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ FinishMixBlend(
+ backdropRect,
+ backdropDest,
+ backdropTransform,
+ backdropTexture,
+ blendMode);
+ }
+}
+
+void
+CompositorD3D9::SetMask(const EffectChain &aEffectChain, uint32_t aMaskTexture)
+{
+ EffectMask *maskEffect =
+ static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ if (!maskEffect) {
+ return;
+ }
+
+ TextureSourceD3D9 *source = maskEffect->mMaskTexture->AsSourceD3D9();
+
+ device()->SetTexture(aMaskTexture, source->GetD3D9Texture());
+
+ const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskEffect->mSize));
+ bounds = maskTransform.As2D().TransformBounds(bounds);
+
+ device()->SetVertexShaderConstantF(DeviceManagerD3D9::sMaskQuadRegister,
+ ShaderConstantRect(bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height),
+ 1);
+}
+
+/**
+ * In the next few methods we call |mParent->InvalidateRemoteLayers()| - that has
+ * a few uses - if our device or swap chain is not ready, it causes us to try
+ * to render again, that means we keep trying to get a good device and swap
+ * chain and don't block the main thread (which we would if we kept trying in
+ * a busy loop because this is likely to happen in a sync transaction).
+ * If we had to recreate our device, then we have new textures and we
+ * need to reupload everything (not just what is currently invalid) from the
+ * client side. That means we need to invalidate everything on the client.
+ * If we just reset and didn't need to recreate, then we don't need to reupload
+ * our textures, but we do need to redraw the whole window, which means we still
+ * need to invalidate everything.
+ * Currently we probably do this complete invalidation too much. But it is better
+ * to do that than to miss an invalidation which would result in a black layer
+ * (or multiple layers) until the user moves the mouse. The unnecessary invalidtion
+ * only happens when the device is reset, so that should be pretty rare and when
+ * other things are happening so the user does not expect super performance.
+ */
+
+bool
+CompositorD3D9::EnsureSwapChain()
+{
+ MOZ_ASSERT(mDeviceManager, "Don't call EnsureSwapChain without a device manager");
+
+ if (!mSwapChain) {
+ mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
+ // We could not create a swap chain, return false
+ if (!mSwapChain) {
+ // Check the state of the device too
+ DeviceManagerState state = mDeviceManager->VerifyReadyForRendering();
+ if (state == DeviceMustRecreate) {
+ mDeviceManager = nullptr;
+ }
+ mParent->InvalidateRemoteLayers();
+ return false;
+ }
+ }
+
+ // We have a swap chain, lets initialise it
+ DeviceManagerState state = mSwapChain->PrepareForRendering();
+ if (state == DeviceOK) {
+ mFailedResetAttempts = 0;
+ return true;
+ }
+ // Swap chain could not be initialised, handle the failure
+ if (state == DeviceMustRecreate) {
+ mDeviceManager = nullptr;
+ mSwapChain = nullptr;
+ }
+ mParent->InvalidateRemoteLayers();
+ return false;
+}
+
+void
+CompositorD3D9::CheckResetCount()
+{
+ if (mDeviceResetCount != mDeviceManager->GetDeviceResetCount()) {
+ mParent->InvalidateRemoteLayers();
+ }
+ mDeviceResetCount = mDeviceManager->GetDeviceResetCount();
+}
+
+bool
+CompositorD3D9::Ready()
+{
+ if (mDeviceManager) {
+ if (EnsureSwapChain()) {
+ // We don't need to call VerifyReadyForRendering because that is
+ // called by mSwapChain->PrepareForRendering() via EnsureSwapChain().
+ CheckResetCount();
+ return true;
+ }
+ return false;
+ }
+
+ NS_ASSERTION(!mCurrentRT && !mDefaultRT,
+ "Shouldn't have any render targets around, they must be released before our device");
+ mSwapChain = nullptr;
+
+ mDeviceManager = DeviceManagerD3D9::Get();
+ if (!mDeviceManager) {
+ FailedToResetDevice();
+ mParent->InvalidateRemoteLayers();
+ return false;
+ }
+ if (EnsureSwapChain()) {
+ CheckResetCount();
+ return true;
+ }
+ return false;
+}
+
+void
+CompositorD3D9::FailedToResetDevice() {
+ mFailedResetAttempts += 1;
+ // 10 is a totally arbitrary number that we may want to increase or decrease
+ // depending on how things behave in the wild.
+ if (mFailedResetAttempts > 10) {
+ mFailedResetAttempts = 0;
+ DeviceManagerDx::Get()->NotifyD3D9DeviceReset();
+ gfxCriticalNote << "[D3D9] Unable to get a working D3D9 Compositor";
+ }
+}
+
+void
+CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect *aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect *aClipRectOut,
+ IntRect *aRenderBoundsOut)
+{
+ MOZ_ASSERT(mDeviceManager && mSwapChain);
+
+ mDeviceManager->SetupRenderState();
+
+ EnsureSize();
+
+ device()->Clear(0, nullptr, D3DCLEAR_TARGET, 0x00000000, 0, 0);
+ device()->BeginScene();
+
+ if (aClipRectOut) {
+ *aClipRectOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+
+ RECT r;
+ if (aClipRectIn) {
+ r.left = (LONG)aClipRectIn->x;
+ r.top = (LONG)aClipRectIn->y;
+ r.right = (LONG)(aClipRectIn->x + aClipRectIn->width);
+ r.bottom = (LONG)(aClipRectIn->y + aClipRectIn->height);
+ } else {
+ r.left = r.top = 0;
+ r.right = mSize.width;
+ r.bottom = mSize.height;
+ }
+ device()->SetScissorRect(&r);
+
+ RefPtr<IDirect3DSurface9> backBuffer = mSwapChain->GetBackBuffer();
+ mDefaultRT = new CompositingRenderTargetD3D9(backBuffer,
+ INIT_MODE_CLEAR,
+ IntRect(0, 0, mSize.width, mSize.height));
+ SetRenderTarget(mDefaultRT);
+}
+
+void
+CompositorD3D9::EndFrame()
+{
+ if (mDeviceManager) {
+ device()->EndScene();
+
+ LayoutDeviceIntSize oldSize = mSize;
+ EnsureSize();
+ if (oldSize == mSize) {
+ if (mTarget) {
+ PaintToTarget();
+ } else {
+ mSwapChain->Present();
+ }
+ }
+ }
+
+ Compositor::EndFrame();
+
+ mCurrentRT = nullptr;
+ mDefaultRT = nullptr;
+}
+
+void
+CompositorD3D9::PrepareViewport(const gfx::IntSize& aSize)
+{
+ Matrix4x4 viewMatrix;
+ /*
+ * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
+ * <1.0, -1.0> bottomright)
+ */
+ viewMatrix._11 = 2.0f / aSize.width;
+ viewMatrix._22 = -2.0f / aSize.height;
+ viewMatrix._41 = -1.0f;
+ viewMatrix._42 = 1.0f;
+ viewMatrix._33 = 0.0f;
+
+ HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to set projection matrix");
+ }
+}
+
+bool
+CompositorD3D9::SupportsEffect(EffectTypes aEffect)
+{
+ if (aEffect == EffectTypes::COMPONENT_ALPHA &&
+ !mDeviceManager->HasComponentAlpha()) {
+ return false;
+ }
+
+ return Compositor::SupportsEffect(aEffect);
+}
+
+void
+CompositorD3D9::EnsureSize()
+{
+ mSize = mWidget->GetClientSize();
+}
+
+void
+CompositorD3D9::SetSamplerForSamplingFilter(SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::LINEAR:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ return;
+ case SamplingFilter::POINT:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
+ return;
+ default:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ }
+}
+
+void
+CompositorD3D9::PaintToTarget()
+{
+ if (!mDeviceManager) {
+ return;
+ }
+
+ RefPtr<IDirect3DSurface9> backBuff;
+ RefPtr<IDirect3DSurface9> destSurf;
+ device()->GetRenderTarget(0, getter_AddRefs(backBuff));
+
+ D3DSURFACE_DESC desc;
+ backBuff->GetDesc(&desc);
+
+ device()->CreateOffscreenPlainSurface(desc.Width, desc.Height,
+ D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(destSurf), nullptr);
+
+ device()->GetRenderTargetData(backBuff, destSurf);
+
+ D3DLOCKED_RECT rect;
+ HRESULT hr = destSurf->LockRect(&rect, nullptr, D3DLOCK_READONLY);
+ if (FAILED(hr) || !rect.pBits) {
+ gfxCriticalError() << "Failed to lock rect in paint to target D3D9 " << hexa(hr);
+ return;
+ }
+ RefPtr<DataSourceSurface> sourceSurface =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)rect.pBits,
+ rect.Pitch,
+ IntSize(desc.Width, desc.Height),
+ SurfaceFormat::B8G8R8A8);
+ mTarget->CopySurface(sourceSurface,
+ IntRect(0, 0, desc.Width, desc.Height),
+ IntPoint(-mTargetBounds.x, -mTargetBounds.y));
+ mTarget->Flush();
+ destSurf->UnlockRect();
+}
+
+void
+CompositorD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode)
+{
+ // We could choose to abort here when hr == E_OUTOFMEMORY.
+ nsCString msg;
+ msg.Append(aMsg);
+ msg.AppendLiteral(" Error code: ");
+ msg.AppendInt(uint32_t(aCode));
+ NS_WARNING(msg.BeginReading());
+
+ gfx::LogFailure(msg);
+}
+
+static inline already_AddRefed<IDirect3DSurface9>
+GetSurfaceOfTexture(IDirect3DTexture9* aTexture)
+{
+ RefPtr<IDirect3DSurface9> surface;
+ HRESULT hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(surface));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to grab texture surface " << hexa(hr);
+ return nullptr;
+ }
+ return surface.forget();
+}
+
+static inline already_AddRefed<IDirect3DSurface9>
+CreateDataSurfaceForTexture(IDirect3DDevice9* aDevice,
+ IDirect3DSurface9* aSource,
+ const D3DSURFACE_DESC& aDesc)
+{
+ RefPtr<IDirect3DSurface9> dest;
+ HRESULT hr = aDevice->CreateOffscreenPlainSurface(
+ aDesc.Width, aDesc.Height,
+ aDesc.Format, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(dest), nullptr);
+ if (FAILED(hr) || !dest) {
+ gfxCriticalNote << "Failed to create offscreen plain surface " << hexa(hr);
+ return nullptr;
+ }
+
+ hr = aDevice->GetRenderTargetData(aSource, dest);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to get render target data " << hexa(hr);
+ return nullptr;
+ }
+
+ return dest.forget();
+}
+
+class AutoSurfaceLock
+{
+ public:
+ AutoSurfaceLock(IDirect3DSurface9* aSurface, DWORD aFlags = 0) {
+ PodZero(&mRect);
+
+ HRESULT hr = aSurface->LockRect(&mRect, nullptr, aFlags);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to lock surface rect " << hexa(hr);
+ return;
+ }
+ mSurface = aSurface;
+ }
+ ~AutoSurfaceLock() {
+ if (mSurface) {
+ mSurface->UnlockRect();
+ }
+ }
+
+ bool Okay() const {
+ return !!mSurface;
+ }
+ int Pitch() const {
+ MOZ_ASSERT(Okay());
+ return mRect.Pitch;
+ }
+ uint8_t* Bits() const {
+ MOZ_ASSERT(Okay());
+ return reinterpret_cast<uint8_t*>(mRect.pBits);
+ }
+
+ private:
+ RefPtr<IDirect3DSurface9> mSurface;
+ D3DLOCKED_RECT mRect;
+};
+
+void
+CompositorD3D9::FinishMixBlend(const gfx::IntRect& aBackdropRect,
+ const gfx::Rect& aBackdropDest,
+ const gfx::Matrix4x4& aBackdropTransform,
+ RefPtr<IDirect3DTexture9> aBackdrop,
+ gfx::CompositionOp aBlendMode)
+{
+ HRESULT hr;
+
+ RefPtr<IDirect3DTexture9> source =
+ CreateTexture(aBackdropRect, mCurrentRT, aBackdropRect.TopLeft());
+ if (!source) {
+ return;
+ }
+
+ // Slow path - do everything in software. Unfortunately this requires
+ // a lot of copying, since we have to readback the source and backdrop,
+ // then upload the blended result, then blit it back.
+
+ IDirect3DDevice9* d3d9Device = device();
+
+ // Query geometry/format of the two surfaces.
+ D3DSURFACE_DESC backdropDesc, sourceDesc;
+ if (FAILED(aBackdrop->GetLevelDesc(0, &backdropDesc)) ||
+ FAILED(source->GetLevelDesc(0, &sourceDesc)))
+ {
+ gfxCriticalNote << "Failed to query mix-blend texture descriptor";
+ return;
+ }
+
+ MOZ_ASSERT(backdropDesc.Format == D3DFMT_A8R8G8B8);
+ MOZ_ASSERT(sourceDesc.Format == D3DFMT_A8R8G8B8);
+
+ // Acquire a temporary data surface for the backdrop texture.
+ RefPtr<IDirect3DSurface9> backdropSurface = GetSurfaceOfTexture(aBackdrop);
+ if (!backdropSurface) {
+ return;
+ }
+ RefPtr<IDirect3DSurface9> tmpBackdrop =
+ CreateDataSurfaceForTexture(d3d9Device, backdropSurface, backdropDesc);
+ if (!tmpBackdrop) {
+ return;
+ }
+
+ // New scope for locks and temporary surfaces.
+ {
+ // Acquire a temporary data surface for the source texture.
+ RefPtr<IDirect3DSurface9> sourceSurface = GetSurfaceOfTexture(source);
+ if (!sourceSurface) {
+ return;
+ }
+ RefPtr<IDirect3DSurface9> tmpSource =
+ CreateDataSurfaceForTexture(d3d9Device, sourceSurface, sourceDesc);
+ if (!tmpSource) {
+ return;
+ }
+
+ // Perform the readback and blend in software.
+ AutoSurfaceLock backdropLock(tmpBackdrop);
+ AutoSurfaceLock sourceLock(tmpSource, D3DLOCK_READONLY);
+ if (!backdropLock.Okay() || !sourceLock.Okay()) {
+ return;
+ }
+
+ RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(
+ sourceLock.Bits(), sourceLock.Pitch(),
+ gfx::IntSize(sourceDesc.Width, sourceDesc.Height),
+ SurfaceFormat::B8G8R8A8);
+
+ RefPtr<DrawTarget> dest = Factory::CreateDrawTargetForData(
+ BackendType::CAIRO,
+ backdropLock.Bits(),
+ gfx::IntSize(backdropDesc.Width, backdropDesc.Height),
+ backdropLock.Pitch(),
+ SurfaceFormat::B8G8R8A8);
+
+ // The backdrop rect is rounded out - account for any difference between
+ // it and the actual destination.
+ gfx::Rect destRect(
+ aBackdropDest.x - aBackdropRect.x,
+ aBackdropDest.y - aBackdropRect.y,
+ aBackdropDest.width,
+ aBackdropDest.height);
+
+ dest->DrawSurface(
+ source, destRect, destRect,
+ gfx::DrawSurfaceOptions(),
+ gfx::DrawOptions(1.0f, aBlendMode));
+ }
+
+ // Upload the new blended surface to the backdrop texture.
+ d3d9Device->UpdateSurface(tmpBackdrop, nullptr, backdropSurface, nullptr);
+
+ // Finally, drop in the new backdrop. We don't need to do another
+ // DrawPrimitive() since the software blend will have included the
+ // final OP_OVER step for us.
+ RECT destRect = {
+ aBackdropRect.x, aBackdropRect.y,
+ aBackdropRect.XMost(), aBackdropRect.YMost()
+ };
+ hr = d3d9Device->StretchRect(backdropSurface,
+ nullptr,
+ mCurrentRT->GetD3D9Surface(),
+ &destRect,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "StretcRect with mix-blend failed " << hexa(hr);
+ }
+}
+
+}
+}
diff --git a/gfx/layers/d3d9/CompositorD3D9.h b/gfx/layers/d3d9/CompositorD3D9.h
new file mode 100644
index 000000000..1453d8c7c
--- /dev/null
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITORD3D9_H
+#define MOZILLA_GFX_COMPOSITORD3D9_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Point.h"
+#include "gfx2DGlue.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureD3D9.h"
+#include "DeviceManagerD3D9.h"
+
+class nsWidget;
+
+namespace mozilla {
+namespace layers {
+
+class CompositorD3D9 : public Compositor
+{
+public:
+ CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
+ ~CompositorD3D9();
+
+ virtual CompositorD3D9* AsCompositorD3D9() override { return this; }
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual TextureFactoryIdentifier
+ GetTextureFactoryIdentifier() override;
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
+ virtual int32_t GetMaxTextureSize() const final;
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override {}
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect,
+ SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override;
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
+ {
+ return mCurrentRT;
+ }
+
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
+
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ virtual void DrawQuad(const gfx::Rect &aRect,
+ const gfx::IntRect &aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+
+ virtual void EndFrame() override;
+
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override {}
+
+ virtual void PrepareViewport(const gfx::IntSize& aSize);
+
+ virtual bool SupportsPartialTextureUpdate() override{ return true; }
+
+ virtual bool SupportsEffect(EffectTypes aEffect) override;
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "Direct3D9"; }
+#endif
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_D3D9;
+ }
+
+ IDirect3DDevice9* device() const
+ {
+ // If the reset counts don't match it means the device was lost and we are
+ // in the process of recreating a new one or will be soon.
+ // cf. comment in EnsureSwapChain.
+ return mDeviceManager && mDeviceResetCount == mDeviceManager->GetDeviceResetCount()
+ ? mDeviceManager->device()
+ : nullptr;
+ }
+
+ /**
+ * Returns true if the Compositor is ready to go.
+ * D3D9 devices can be awkward and there is a bunch of logic around
+ * resetting/recreating devices and swap chains. That is handled by this method.
+ * If we don't have a device and swap chain ready for rendering, we will return
+ * false and if necessary destroy the device and/or swap chain. We will also
+ * schedule another composite so we get another go at rendering, thus we shouldn't
+ * miss a composite due to re-creating a device.
+ */
+ virtual bool Ready() override;
+
+ /**
+ * Declare an offset to use when rendering layers. This will be ignored when
+ * rendering to a target instead of the screen.
+ */
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override
+ {
+ if (aOffset.x || aOffset.y) {
+ NS_RUNTIMEABORT("SetScreenRenderOffset not supported by CompositorD3D9.");
+ }
+ // If the offset is 0, 0 that's okay.
+ }
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+private:
+ // ensure mSize is up to date with respect to mWidget
+ void EnsureSize();
+ void SetSamplerForSamplingFilter(gfx::SamplingFilter aSamplingFilter);
+ void PaintToTarget();
+ void SetMask(const EffectChain &aEffectChain, uint32_t aMaskTexture);
+ /**
+ * Ensure we have a swap chain and it is ready for rendering.
+ * Requires mDeviceManger to be non-null.
+ * Returns true if we have a working swap chain; false otherwise.
+ * If we cannot create or validate the swap chain due to a bad device manager,
+ * then the device will be destroyed and set mDeviceManager to null. We will
+ * schedule another composite if it is a good idea to try again or we need to
+ * recreate the device.
+ */
+ bool EnsureSwapChain();
+
+ already_AddRefed<IDirect3DTexture9>
+ CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint);
+
+ /**
+ * Complete a mix-blend step at the end of DrawQuad().
+ */
+ void FinishMixBlend(const gfx::IntRect& aBackdropRect,
+ const gfx::Rect& aBackdropDest,
+ const gfx::Matrix4x4& aBackdropTransform,
+ RefPtr<IDirect3DTexture9> aBackdrop,
+ gfx::CompositionOp aBlendMode);
+
+ /**
+ * DeviceManagerD3D9 keeps a count of the number of times its device is
+ * reset or recreated. We keep a parallel count (mDeviceResetCount). It
+ * is possible that we miss a reset if it is 'caused' by another
+ * compositor (for another window). In which case we need to invalidate
+ * everything and render it all. This method checks the reset counts
+ * match and if not invalidates everything (a long comment on that in
+ * the cpp file).
+ */
+ void CheckResetCount();
+
+ void FailedToResetDevice();
+
+ void ReportFailure(const nsACString &aMsg, HRESULT aCode);
+
+ /* Device manager instance for this compositor */
+ RefPtr<DeviceManagerD3D9> mDeviceManager;
+
+ /* Swap chain associated with this compositor */
+ RefPtr<SwapChainD3D9> mSwapChain;
+
+ RefPtr<CompositingRenderTargetD3D9> mDefaultRT;
+ RefPtr<CompositingRenderTargetD3D9> mCurrentRT;
+
+ LayoutDeviceIntSize mSize;
+
+ uint32_t mDeviceResetCount;
+ uint32_t mFailedResetAttempts;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
new file mode 100644
index 000000000..09778bc9c
--- /dev/null
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
@@ -0,0 +1,966 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DeviceManagerD3D9.h"
+#include "LayerManagerD3D9Shaders.h"
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+#include "nsPrintfCString.h"
+#include "Nv3DVUtils.h"
+#include "plstr.h"
+#include <algorithm>
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+#include "gfxWindowsPlatform.h"
+#include "TextureD3D9.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "gfxPrefs.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+const LPCWSTR kClassName = L"D3D9WindowClass";
+
+#define USE_D3D9EX
+
+struct vertex {
+ float x, y;
+};
+
+static StaticAutoPtr<mozilla::Mutex> sDeviceManagerLock;
+static StaticRefPtr<DeviceManagerD3D9> sDeviceManager;
+
+/* static */ void
+DeviceManagerD3D9::Init()
+{
+ MOZ_ASSERT(!sDeviceManagerLock);
+ sDeviceManagerLock = new Mutex("DeviceManagerD3D9.sDeviceManagerLock");
+}
+
+/* static */ void
+DeviceManagerD3D9::Shutdown()
+{
+ sDeviceManagerLock = nullptr;
+ sDeviceManager = nullptr;
+}
+
+SwapChainD3D9::SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager)
+ : mDeviceManager(aDeviceManager)
+ , mWnd(0)
+{
+ mDeviceManager->mSwapChains.AppendElement(this);
+}
+
+SwapChainD3D9::~SwapChainD3D9()
+{
+ mDeviceManager->mSwapChains.RemoveElement(this);
+}
+
+bool
+SwapChainD3D9::Init(HWND hWnd)
+{
+ RECT r;
+ ::GetClientRect(hWnd, &r);
+
+ mWnd = hWnd;
+
+ D3DPRESENT_PARAMETERS pp;
+ memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS));
+
+ pp.BackBufferFormat = D3DFMT_A8R8G8B8;
+ pp.SwapEffect = D3DSWAPEFFECT_COPY;
+ pp.Windowed = TRUE;
+ pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ pp.hDeviceWindow = mWnd;
+ if (r.left == r.right || r.top == r.bottom) {
+ pp.BackBufferHeight = 1;
+ pp.BackBufferWidth = 1;
+ }
+
+ HRESULT hr = mDeviceManager->device()->
+ CreateAdditionalSwapChain(&pp,
+ getter_AddRefs(mSwapChain));
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to create swap chain for window.");
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<IDirect3DSurface9>
+SwapChainD3D9::GetBackBuffer()
+{
+ RefPtr<IDirect3DSurface9> backBuffer;
+ mSwapChain->GetBackBuffer(0,
+ D3DBACKBUFFER_TYPE_MONO,
+ getter_AddRefs(backBuffer));
+ return backBuffer.forget();
+}
+
+DeviceManagerState
+SwapChainD3D9::PrepareForRendering()
+{
+ RECT r;
+ if (!::GetClientRect(mWnd, &r)) {
+ return DeviceFail;
+ }
+
+ DeviceManagerState deviceState = mDeviceManager->VerifyReadyForRendering();
+ if (deviceState != DeviceOK) {
+ return deviceState;
+ }
+
+ if (!mSwapChain) {
+ Init(mWnd);
+ }
+
+ if (mSwapChain) {
+ RefPtr<IDirect3DSurface9> backBuffer = GetBackBuffer();
+
+ D3DSURFACE_DESC desc;
+ backBuffer->GetDesc(&desc);
+
+ if (desc.Width == r.right - r.left && desc.Height == r.bottom - r.top) {
+ mDeviceManager->device()->SetRenderTarget(0, backBuffer);
+ return DeviceOK;
+ }
+
+ mSwapChain = nullptr;
+
+ Init(mWnd);
+
+ if (!mSwapChain) {
+ return DeviceFail;
+ }
+
+ backBuffer = GetBackBuffer();
+ mDeviceManager->device()->SetRenderTarget(0, backBuffer);
+
+ return DeviceOK;
+ }
+
+ return DeviceFail;
+}
+
+void
+SwapChainD3D9::Present(const gfx::IntRect &aRect)
+{
+ RECT r;
+ r.left = aRect.x;
+ r.top = aRect.y;
+ r.right = aRect.XMost();
+ r.bottom = aRect.YMost();
+
+ mSwapChain->Present(&r, &r, 0, 0, 0);
+}
+
+void
+SwapChainD3D9::Present()
+{
+ mSwapChain->Present(nullptr, nullptr, 0, 0, 0);
+}
+
+void
+SwapChainD3D9::Reset()
+{
+ mSwapChain = nullptr;
+}
+
+#define HAS_CAP(a, b) (((a) & (b)) == (b))
+#define LACKS_CAP(a, b) !(((a) & (b)) == (b))
+
+uint32_t DeviceManagerD3D9::sMaskQuadRegister = 11;
+
+DeviceManagerD3D9::DeviceManagerD3D9()
+ : mTextureHostList(nullptr)
+ , mDeviceResetCount(0)
+ , mMaxTextureSize(0)
+ , mTextureAddressingMode(D3DTADDRESS_CLAMP)
+ , mHasComponentAlpha(true)
+ , mHasDynamicTextures(false)
+ , mDeviceWasRemoved(false)
+{
+}
+
+DeviceManagerD3D9::~DeviceManagerD3D9()
+{
+ DestroyDevice();
+}
+
+/* static */ RefPtr<DeviceManagerD3D9>
+DeviceManagerD3D9::Get()
+{
+ MutexAutoLock lock(*sDeviceManagerLock);
+
+ bool canCreate =
+ !gfxPlatform::UsesOffMainThreadCompositing() ||
+ CompositorThreadHolder::IsInCompositorThread();
+ if (!sDeviceManager && canCreate) {
+ sDeviceManager = new DeviceManagerD3D9();
+ if (!sDeviceManager->Initialize()) {
+ gfxCriticalError() << "[D3D9] Could not Initialize the DeviceManagerD3D9";
+ sDeviceManager = nullptr;
+ }
+ }
+
+ return sDeviceManager;
+}
+
+/* static */ RefPtr<IDirect3DDevice9>
+DeviceManagerD3D9::GetDevice()
+{
+ MutexAutoLock lock(*sDeviceManagerLock);
+ return sDeviceManager ? sDeviceManager->device() : nullptr;
+}
+
+/* static */ void
+DeviceManagerD3D9::OnDeviceManagerDestroy(DeviceManagerD3D9* aDeviceManager)
+{
+ if (!sDeviceManagerLock) {
+ // If the device manager has shutdown, we don't care anymore. We can get
+ // here when the compositor shuts down asynchronously.
+ MOZ_ASSERT(!sDeviceManager);
+ return;
+ }
+
+ MutexAutoLock lock(*sDeviceManagerLock);
+ if (aDeviceManager == sDeviceManager) {
+ sDeviceManager = nullptr;
+ }
+}
+
+bool
+DeviceManagerD3D9::Initialize()
+{
+ WNDCLASSW wc;
+ HRESULT hr;
+
+ if (!GetClassInfoW(GetModuleHandle(nullptr), kClassName, &wc)) {
+ ZeroMemory(&wc, sizeof(WNDCLASSW));
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.lpfnWndProc = ::DefWindowProc;
+ wc.lpszClassName = kClassName;
+ if (!RegisterClassW(&wc)) {
+ gfxCriticalError() << "[D3D9] Failed to register class for DeviceManager";
+ return false;
+ }
+ }
+
+ mFocusWnd = ::CreateWindowW(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr,
+ nullptr, GetModuleHandle(nullptr), nullptr);
+
+ if (!mFocusWnd) {
+ gfxCriticalError() << "[D3D9] Failed to create a window";
+ return false;
+ }
+
+ if (gfxPrefs::StereoVideoEnabled()) {
+ /* Create an Nv3DVUtils instance */
+ if (!mNv3DVUtils) {
+ mNv3DVUtils = new Nv3DVUtils();
+ if (!mNv3DVUtils) {
+ NS_WARNING("Could not create a new instance of Nv3DVUtils.");
+ }
+ }
+
+ /* Initialize the Nv3DVUtils object */
+ if (mNv3DVUtils) {
+ mNv3DVUtils->Initialize();
+ }
+ }
+
+ HMODULE d3d9 = LoadLibraryW(L"d3d9.dll");
+ decltype(Direct3DCreate9)* d3d9Create = (decltype(Direct3DCreate9)*)
+ GetProcAddress(d3d9, "Direct3DCreate9");
+ decltype(Direct3DCreate9Ex)* d3d9CreateEx = (decltype(Direct3DCreate9Ex)*)
+ GetProcAddress(d3d9, "Direct3DCreate9Ex");
+
+#ifdef USE_D3D9EX
+ if (d3d9CreateEx) {
+ hr = d3d9CreateEx(D3D_SDK_VERSION, getter_AddRefs(mD3D9Ex));
+ if (SUCCEEDED(hr)) {
+ mD3D9 = mD3D9Ex;
+ }
+ }
+#endif
+
+ if (!mD3D9) {
+ if (!d3d9Create) {
+ gfxCriticalError() << "[D3D9] Failed to load symbols";
+ return false;
+ }
+
+ mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION));
+
+ if (!mD3D9) {
+ gfxCriticalError() << "[D3D9] Failed to create the IDirect3D9 object";
+ return false;
+ }
+ }
+
+ D3DADAPTER_IDENTIFIER9 ident;
+ hr = mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &ident);
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D3D9] Failed to create the environment code: " << gfx::hexa(hr);
+ return false;
+ }
+
+ D3DPRESENT_PARAMETERS pp;
+ memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS));
+
+ pp.BackBufferWidth = 1;
+ pp.BackBufferHeight = 1;
+ pp.BackBufferFormat = D3DFMT_A8R8G8B8;
+ pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ pp.Windowed = TRUE;
+ pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ pp.hDeviceWindow = mFocusWnd;
+
+ if (mD3D9Ex) {
+ hr = mD3D9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ mFocusWnd,
+ D3DCREATE_FPU_PRESERVE |
+ D3DCREATE_MULTITHREADED |
+ D3DCREATE_MIXED_VERTEXPROCESSING,
+ &pp,
+ nullptr,
+ getter_AddRefs(mDeviceEx));
+ if (SUCCEEDED(hr)) {
+ mDevice = mDeviceEx;
+ }
+
+ D3DCAPS9 caps;
+ if (mDeviceEx && mDeviceEx->GetDeviceCaps(&caps)) {
+ if (LACKS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) {
+ // XXX - Should we actually hit this we'll need a CanvasLayer that
+ // supports static D3DPOOL_DEFAULT textures.
+ NS_WARNING("D3D9Ex device not used because of lack of support for \
+ dynamic textures. This is unexpected.");
+ mDevice = nullptr;
+ mDeviceEx = nullptr;
+ }
+ }
+ }
+
+ if (!mDevice) {
+ hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ mFocusWnd,
+ D3DCREATE_FPU_PRESERVE |
+ D3DCREATE_MULTITHREADED |
+ D3DCREATE_MIXED_VERTEXPROCESSING,
+ &pp,
+ getter_AddRefs(mDevice));
+
+ if (FAILED(hr) || !mDevice) {
+ gfxCriticalError() << "[D3D9] Failed to create the device, code: " << hexa(hr);
+ return false;
+ }
+ }
+
+ if (!VerifyCaps()) {
+ gfxCriticalError() << "[D3D9] insufficient capabilities";
+ return false;
+ }
+
+ /* Grab the associated HMONITOR so that we can find out
+ * if it changed later */
+ D3DDEVICE_CREATION_PARAMETERS parameters;
+ if (FAILED(mDevice->GetCreationParameters(&parameters)))
+ return false;
+ mDeviceMonitor = mD3D9->GetAdapterMonitor(parameters.AdapterOrdinal);
+
+
+ /*
+ * Do some post device creation setup
+ */
+ if (mNv3DVUtils) {
+ IUnknown* devUnknown = nullptr;
+ if (mDevice) {
+ mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown);
+ }
+ mNv3DVUtils->SetDeviceInfo(devUnknown);
+ }
+
+ auto failCreateShaderMsg = "[D3D9] failed to create a critical resource (shader) code";
+
+ hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS,
+ getter_AddRefs(mLayerVS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "LayerQuadVS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS,
+ getter_AddRefs(mRGBPS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "RGBShaderPS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPS,
+ getter_AddRefs(mRGBAPS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "RGBAShaderPS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPS,
+ getter_AddRefs(mComponentPass1PS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "ComponentPass1ShaderPS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPS,
+ getter_AddRefs(mComponentPass2PS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "ComponentPass2ShaderPS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS,
+ getter_AddRefs(mYCbCrPS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "YCbCrShaderPS: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS,
+ getter_AddRefs(mSolidColorPS));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "SolidColorShaderPS" << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVSMask,
+ getter_AddRefs(mLayerVSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "LayerQuadVSMask: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPSMask,
+ getter_AddRefs(mRGBPSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "RGBShaderPSMask " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPSMask,
+ getter_AddRefs(mRGBAPSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "RGBAShaderPSMask: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPSMask,
+ getter_AddRefs(mComponentPass1PSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "ComponentPass1ShaderPSMask: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPSMask,
+ getter_AddRefs(mComponentPass2PSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "ComponentPass2ShaderPSMask: ";
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPSMask,
+ getter_AddRefs(mYCbCrPSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "YCbCrShaderPSMask: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPSMask,
+ getter_AddRefs(mSolidColorPSMask));
+
+ if (FAILED(hr)) {
+ gfxCriticalError() << failCreateShaderMsg << "SolidColorShaderPSMask: " << gfx::hexa(hr);
+ return false;
+ }
+
+ if (!CreateVertexBuffer()) {
+ gfxCriticalError() << "[D3D9] Failed to create a critical resource (vbo)";
+ return false;
+ }
+
+ hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex));
+ if (FAILED(hr)) {
+ gfxCriticalError() << "[D3D9] Failed to set the stream source code: " << gfx::hexa(hr);
+ return false;
+ }
+
+ D3DVERTEXELEMENT9 elements[] = {
+ { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
+ D3DDECLUSAGE_POSITION, 0 },
+ D3DDECL_END()
+ };
+
+ mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD));
+
+ nsCOMPtr<nsIConsoleService>
+ console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+ D3DADAPTER_IDENTIFIER9 identifier;
+ mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier);
+
+ if (console) {
+ nsString msg;
+ msg +=
+ NS_LITERAL_STRING("Direct3D 9 DeviceManager Initialized Successfully.\nDriver: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)identifier.Driver));
+ msg += NS_LITERAL_STRING("\nDescription: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)identifier.Description));
+ msg += NS_LITERAL_STRING("\nVersion: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsPrintfCString("%d.%d.%d.%d",
+ HIWORD(identifier.DriverVersion.HighPart),
+ LOWORD(identifier.DriverVersion.HighPart),
+ HIWORD(identifier.DriverVersion.LowPart),
+ LOWORD(identifier.DriverVersion.LowPart)));
+ console->LogStringMessage(msg.get());
+ }
+
+ return true;
+}
+
+void
+DeviceManagerD3D9::SetupRenderState()
+{
+ mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex));
+ mDevice->SetVertexDeclaration(mVD);
+ mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+ mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
+ mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
+ mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
+ mDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
+ mDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
+ mDevice->SetRenderState(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
+ mDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, mTextureAddressingMode);
+ mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, mTextureAddressingMode);
+ mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, mTextureAddressingMode);
+ mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, mTextureAddressingMode);
+ mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, mTextureAddressingMode);
+ mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, mTextureAddressingMode);
+}
+
+already_AddRefed<SwapChainD3D9>
+DeviceManagerD3D9::CreateSwapChain(HWND hWnd)
+{
+ RefPtr<SwapChainD3D9> swapChain = new SwapChainD3D9(this);
+
+ // See bug 604647. This line means that if we create a window while the
+ // device is lost LayerManager initialization will fail, this window
+ // will be permanently unaccelerated. This should be a rare situation
+ // though and the need for a low-risk fix for this bug outweighs the
+ // downside.
+ if (VerifyReadyForRendering() != DeviceOK) {
+ return nullptr;
+ }
+
+ if (!swapChain->Init(hWnd)) {
+ return nullptr;
+ }
+
+ return swapChain.forget();
+}
+
+uint32_t
+DeviceManagerD3D9::SetShaderMode(ShaderMode aMode, MaskType aMaskType)
+{
+ if (aMaskType == MaskType::MaskNone) {
+ switch (aMode) {
+ case RGBLAYER:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mRGBPS);
+ break;
+ case RGBALAYER:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mRGBAPS);
+ break;
+ case COMPONENTLAYERPASS1:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mComponentPass1PS);
+ break;
+ case COMPONENTLAYERPASS2:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mComponentPass2PS);
+ break;
+ case YCBCRLAYER:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mYCbCrPS);
+ break;
+ case SOLIDCOLORLAYER:
+ mDevice->SetVertexShader(mLayerVS);
+ mDevice->SetPixelShader(mSolidColorPS);
+ break;
+ }
+ return 0;
+ }
+
+ uint32_t maskTexRegister;
+ switch (aMode) {
+ case RGBLAYER:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mRGBPSMask);
+ maskTexRegister = 1;
+ break;
+ case RGBALAYER:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mRGBAPSMask);
+ maskTexRegister = 1;
+ break;
+ case COMPONENTLAYERPASS1:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mComponentPass1PSMask);
+ maskTexRegister = 2;
+ break;
+ case COMPONENTLAYERPASS2:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mComponentPass2PSMask);
+ maskTexRegister = 2;
+ break;
+ case YCBCRLAYER:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mYCbCrPSMask);
+ maskTexRegister = 3;
+ break;
+ case SOLIDCOLORLAYER:
+ mDevice->SetVertexShader(mLayerVSMask);
+ mDevice->SetPixelShader(mSolidColorPSMask);
+ maskTexRegister = 0;
+ break;
+ }
+ return maskTexRegister;
+}
+
+void
+DeviceManagerD3D9::DestroyDevice()
+{
+ ++mDeviceResetCount;
+ mDeviceWasRemoved = true;
+ if (!IsD3D9Ex()) {
+ ReleaseTextureResources();
+ }
+ DeviceManagerD3D9::OnDeviceManagerDestroy(this);
+}
+
+DeviceManagerState
+DeviceManagerD3D9::VerifyReadyForRendering()
+{
+ if (mDeviceWasRemoved) {
+ return DeviceMustRecreate;
+ }
+
+ HRESULT hr = mDevice->TestCooperativeLevel();
+
+ if (SUCCEEDED(hr)) {
+ if (IsD3D9Ex()) {
+ hr = mDeviceEx->CheckDeviceState(mFocusWnd);
+
+ if (FAILED(hr)) {
+ DestroyDevice();
+ return DeviceMustRecreate;
+ }
+ }
+ return DeviceOK;
+ }
+
+ ReleaseTextureResources();
+ for (unsigned int i = 0; i < mSwapChains.Length(); i++) {
+ mSwapChains[i]->Reset();
+ }
+
+ mVB = nullptr;
+
+ D3DPRESENT_PARAMETERS pp;
+ memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS));
+
+ pp.BackBufferWidth = 1;
+ pp.BackBufferHeight = 1;
+ pp.BackBufferFormat = D3DFMT_A8R8G8B8;
+ pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ pp.Windowed = TRUE;
+ pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ pp.hDeviceWindow = mFocusWnd;
+
+ // Whatever happens from now on, either we reset the device, or we should
+ // pretend we reset the device so that the layer manager or compositor
+ // doesn't ignore it.
+ ++mDeviceResetCount;
+
+ // if we got this far, we know !SUCCEEDEED(hr), that means hr is one of
+ // D3DERR_DEVICELOST, D3DERR_DEVICENOTRESET, D3DERR_DRIVERINTERNALERROR.
+ // It is only worth resetting if we get D3DERR_DEVICENOTRESET. If we get
+ // D3DERR_DEVICELOST we can wait and see if we get D3DERR_DEVICENOTRESET
+ // later, then reset.
+ if (hr == D3DERR_DEVICELOST) {
+ HMONITOR hMonitorWindow;
+ hMonitorWindow = MonitorFromWindow(mFocusWnd, MONITOR_DEFAULTTOPRIMARY);
+ if (hMonitorWindow != mDeviceMonitor) {
+ /* jrmuizel: I'm not sure how to trigger this case. Usually, we get
+ * DEVICENOTRESET right away and Reset() succeeds without going through a
+ * set of DEVICELOSTs. This is presumeably because we don't call
+ * VerifyReadyForRendering when we don't have any reason to paint.
+ * Hopefully comparing HMONITORs is not overly aggressive.
+ * See bug 626678.
+ */
+ /* The monitor has changed. We have to assume that the
+ * DEVICENOTRESET will not be coming. */
+ DestroyDevice();
+ return DeviceMustRecreate;
+ }
+ return DeviceFail;
+ }
+ if (hr == D3DERR_DEVICENOTRESET) {
+ hr = mDevice->Reset(&pp);
+ }
+
+ if (FAILED(hr) || !CreateVertexBuffer()) {
+ DestroyDevice();
+ return DeviceMustRecreate;
+ }
+
+ return DeviceOK;
+}
+
+bool
+DeviceManagerD3D9::VerifyCaps()
+{
+ D3DCAPS9 caps;
+ HRESULT hr = mDevice->GetDeviceCaps(&caps);
+
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) ||
+ LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) ||
+ LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) ||
+ HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) ||
+ (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) &&
+ LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) ||
+ LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) {
+ return false;
+ }
+
+ if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) {
+ return false;
+ }
+
+ if (caps.MaxTextureHeight < 4096 ||
+ caps.MaxTextureWidth < 4096) {
+ return false;
+ }
+ mMaxTextureSize = std::min(caps.MaxTextureHeight, caps.MaxTextureWidth);
+
+ if ((caps.PixelShaderVersion & 0xffff) < 0x200 ||
+ (caps.VertexShaderVersion & 0xffff) < 0x200) {
+ return false;
+ }
+
+ if (HAS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) {
+ mHasDynamicTextures = true;
+ }
+
+ if (HAS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_WRAP) &&
+ LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) {
+ mTextureAddressingMode = D3DTADDRESS_WRAP;
+ } else {
+ gfxPlatform::DisableBufferRotation();
+ }
+
+ if (LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCCOLOR)) {
+ mHasComponentAlpha = false;
+ }
+
+ return true;
+}
+
+bool
+DeviceManagerD3D9::CreateVertexBuffer()
+{
+ HRESULT hr;
+
+ hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4,
+ D3DUSAGE_WRITEONLY,
+ 0,
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(mVB),
+ nullptr);
+
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ vertex *vertices;
+ hr = mVB->Lock(0, 0, (void**)&vertices, 0);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ vertices[0].x = vertices[0].y = 0;
+ vertices[1].x = 1; vertices[1].y = 0;
+ vertices[2].x = 0; vertices[2].y = 1;
+ vertices[3].x = 1; vertices[3].y = 1;
+
+ mVB->Unlock();
+
+ return true;
+}
+
+already_AddRefed<IDirect3DTexture9>
+DeviceManagerD3D9::CreateTexture(const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ D3DPOOL aPool,
+ TextureSourceD3D9* aTextureHost)
+{
+ if (mDeviceWasRemoved) {
+ return nullptr;
+ }
+ RefPtr<IDirect3DTexture9> result;
+ if (FAILED(device()->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, aPool,
+ getter_AddRefs(result), nullptr))) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(aPool != D3DPOOL_MANAGED,
+ "Should not be using MANAGED texture pool. We will get an error when we have to recreate the device");
+ if (aPool == D3DPOOL_DEFAULT) {
+ MOZ_ASSERT(aTextureHost, "We need a texture host to track so we can release the texture.");
+ RegisterTextureHost(aTextureHost);
+ }
+
+ return result.forget();
+}
+
+#ifdef DEBUG
+bool
+DeviceManagerD3D9::IsInTextureHostList(TextureSourceD3D9* aFind)
+{
+ TextureSourceD3D9* cur = mTextureHostList;
+ while(cur) {
+ if (cur == aFind) {
+ return true;
+ }
+ cur = cur->mNextHost;
+ }
+
+ return false;
+}
+#endif
+
+void
+DeviceManagerD3D9::RegisterTextureHost(TextureSourceD3D9* aHost)
+{
+ if (!aHost) {
+ return;
+ }
+
+ // Don't add aHost to the list twice.
+ if (aHost->mPreviousHost ||
+ mTextureHostList == aHost) {
+ MOZ_ASSERT(IsInTextureHostList(aHost));
+ return;
+ }
+
+ MOZ_ASSERT(!aHost->mNextHost);
+ MOZ_ASSERT(!IsInTextureHostList(aHost));
+
+ if (mTextureHostList) {
+ MOZ_ASSERT(!mTextureHostList->mPreviousHost);
+ mTextureHostList->mPreviousHost = aHost;
+ aHost->mNextHost = mTextureHostList;
+ }
+ mTextureHostList = aHost;
+ MOZ_ASSERT(!aHost->mCreatingDeviceManager, "Already created texture?");
+ MOZ_ASSERT(IsInTextureHostList(aHost));
+ aHost->mCreatingDeviceManager = this;
+}
+
+void
+DeviceManagerD3D9::ReleaseTextureResources()
+{
+ TextureSourceD3D9* host = mTextureHostList;
+ while (host) {
+ host->ReleaseTextureResources();
+ TextureSourceD3D9* oldHost = host;
+ host = oldHost->mNextHost;
+ oldHost->mPreviousHost = nullptr;
+ oldHost->mNextHost = nullptr;
+ oldHost->mCreatingDeviceManager = nullptr;
+ }
+ mTextureHostList = nullptr;
+}
+
+void
+DeviceManagerD3D9::RemoveTextureListHead(TextureSourceD3D9* aHost)
+{
+ MOZ_ASSERT(!aHost->mCreatingDeviceManager || aHost->mCreatingDeviceManager == this,
+ "Wrong device manager");
+ MOZ_ASSERT(aHost && mTextureHostList == aHost,
+ "aHost is not the head of the texture host list");
+ mTextureHostList = aHost->mNextHost;
+}
+
+} /* namespace layers */
+} /* namespace mozilla */
diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h
new file mode 100644
index 000000000..d27b679ab
--- /dev/null
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DEVICEMANAGERD3D9_H
+#define GFX_DEVICEMANAGERD3D9_H
+
+#include "gfxTypes.h"
+#include "nsAutoPtr.h"
+#include "d3d9.h"
+#include "nsTArray.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class DeviceManagerD3D9;
+class Nv3DVUtils;
+class Layer;
+class TextureSourceD3D9;
+
+// Shader Constant locations
+const int CBmLayerTransform = 0;
+const int CBmProjection = 4;
+const int CBvRenderTargetOffset = 8;
+const int CBvTextureCoords = 9;
+const int CBvLayerQuad = 10;
+// we don't use opacity with solid color shaders
+const int CBfLayerOpacity = 0;
+const int CBvColor = 0;
+const int CBmYuvColorMatrix = 1;
+
+enum DeviceManagerState {
+ // The device and swap chain are OK.
+ DeviceOK,
+ // The device or swap chain are in a bad state, and we should not render.
+ DeviceFail,
+ // The device is lost and cannot be reset, the user should forget the
+ // current device manager and create a new one.
+ DeviceMustRecreate,
+};
+
+
+/**
+ * This structure is used to pass rectangles to our shader constant. We can use
+ * this for passing rectangular areas to SetVertexShaderConstant. In the format
+ * of a 4 component float(x,y,width,height). Our vertex shader can then use
+ * this to construct rectangular positions from the 0,0-1,1 quad that we source
+ * it with.
+ */
+struct ShaderConstantRect
+{
+ float mX, mY, mWidth, mHeight;
+
+ // Provide all the commonly used argument types to prevent all the local
+ // casts in the code.
+ ShaderConstantRect(float aX, float aY, float aWidth, float aHeight)
+ : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
+ { }
+
+ ShaderConstantRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+ : mX((float)aX), mY((float)aY)
+ , mWidth((float)aWidth), mHeight((float)aHeight)
+ { }
+
+ ShaderConstantRect(int32_t aX, int32_t aY, float aWidth, float aHeight)
+ : mX((float)aX), mY((float)aY), mWidth(aWidth), mHeight(aHeight)
+ { }
+
+ // For easy passing to SetVertexShaderConstantF.
+ operator float* () { return &mX; }
+};
+
+/**
+ * SwapChain class, this class manages the swap chain belonging to a
+ * LayerManagerD3D9.
+ */
+class SwapChainD3D9 final
+{
+ NS_INLINE_DECL_REFCOUNTING(SwapChainD3D9)
+public:
+
+ /**
+ * This function will prepare the device this swap chain belongs to for
+ * rendering to this swap chain. Only after calling this function can the
+ * swap chain be drawn to, and only until this function is called on another
+ * swap chain belonging to this device will the device draw to it. Passed in
+ * is the size of the swap chain. If the window size differs from the size
+ * during the last call to this function the swap chain will resize. Note that
+ * in no case does this function guarantee the backbuffer to still have its
+ * old content.
+ */
+ DeviceManagerState PrepareForRendering();
+
+ already_AddRefed<IDirect3DSurface9> GetBackBuffer();
+
+ /**
+ * This function will present the selected rectangle of the swap chain to
+ * its associated window.
+ */
+ void Present(const gfx::IntRect &aRect);
+ void Present();
+
+private:
+ friend class DeviceManagerD3D9;
+
+ SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager);
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~SwapChainD3D9();
+
+ bool Init(HWND hWnd);
+
+ /**
+ * This causes us to release our swap chain, clearing out our resource usage
+ * so the master device may reset.
+ */
+ void Reset();
+
+ RefPtr<IDirect3DSwapChain9> mSwapChain;
+ RefPtr<DeviceManagerD3D9> mDeviceManager;
+ HWND mWnd;
+};
+
+/**
+ * Device manager, this class is used by the layer managers to share the D3D9
+ * device and create swap chains for the individual windows the layer managers
+ * belong to.
+ */
+class DeviceManagerD3D9 final
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeviceManagerD3D9)
+
+ /**
+ * Setup or tear down static resources needed for D3D9.
+ */
+ static void Init();
+ static void Shutdown();
+
+ /**
+ * Static accessors and helpers for accessing the global DeviceManagerD3D9
+ * instance.
+ */
+ static RefPtr<DeviceManagerD3D9> Get();
+ static RefPtr<IDirect3DDevice9> GetDevice();
+ static void OnDeviceManagerDestroy(DeviceManagerD3D9* aDeviceManager);
+
+ /**
+ * Sets up the render state for the device for layer rendering.
+ */
+ void SetupRenderState();
+
+ /**
+ * Create a swap chain setup to work with the specified window.
+ */
+ already_AddRefed<SwapChainD3D9> CreateSwapChain(HWND hWnd);
+
+ IDirect3DDevice9 *device() { return mDevice; }
+
+ bool IsD3D9Ex() { return mDeviceEx; }
+
+ bool HasComponentAlpha() { return mHasComponentAlpha; }
+
+ bool HasDynamicTextures() { return mHasDynamicTextures; }
+
+ enum ShaderMode {
+ RGBLAYER,
+ RGBALAYER,
+ COMPONENTLAYERPASS1,
+ COMPONENTLAYERPASS2,
+ YCBCRLAYER,
+ SOLIDCOLORLAYER
+ };
+
+ // returns the register to be used for the mask texture, if appropriate
+ uint32_t SetShaderMode(ShaderMode aMode, MaskType aMaskType);
+
+ /**
+ * Return pointer to the Nv3DVUtils instance
+ */
+ Nv3DVUtils *GetNv3DVUtils() { return mNv3DVUtils; }
+
+ /**
+ * Returns true if this device was removed.
+ */
+ bool DeviceWasRemoved() { return mDeviceWasRemoved; }
+
+ uint32_t GetDeviceResetCount() { return mDeviceResetCount; }
+
+ int32_t GetMaxTextureSize() { return mMaxTextureSize; }
+
+ // Removes aHost from our list of texture hosts if it is the head.
+ void RemoveTextureListHead(TextureSourceD3D9* aHost);
+
+ /**
+ * Creates a texture using our device.
+ * If needed, we keep a record of the new texture, so the texture can be
+ * released. In this case, aTextureHostIDirect3DTexture9 must be non-null.
+ */
+ already_AddRefed<IDirect3DTexture9> CreateTexture(const gfx::IntSize &aSize,
+ _D3DFORMAT aFormat,
+ D3DPOOL aPool,
+ TextureSourceD3D9* aTextureHostIDirect3DTexture9);
+#ifdef DEBUG
+ // Looks for aFind in the list of texture hosts.
+ // O(n) so only use for assertions.
+ bool IsInTextureHostList(TextureSourceD3D9* aFind);
+#endif
+
+ /**
+ * This function verifies the device is ready for rendering, internally this
+ * will test the cooperative level of the device and reset the device if
+ * needed. If this returns false subsequent rendering calls may return errors.
+ */
+ DeviceManagerState VerifyReadyForRendering();
+
+ static uint32_t sMaskQuadRegister;
+
+private:
+ friend class SwapChainD3D9;
+
+ DeviceManagerD3D9();
+ ~DeviceManagerD3D9();
+
+ /**
+ * Initialises the device manager, the underlying device, and everything else
+ * the manager needs.
+ * Returns true if initialisation succeeds, false otherwise.
+ * Note that if initisalisation fails, you cannot try again - you must throw
+ * away the DeviceManagerD3D9 and create a new one.
+ */
+ bool Initialize();
+
+ void DestroyDevice();
+
+ /**
+ * This will fill our vertex buffer with the data of our quad, it may be
+ * called when the vertex buffer is recreated.
+ */
+ bool CreateVertexBuffer();
+
+ /**
+ * Release all textures created by this device manager.
+ */
+ void ReleaseTextureResources();
+ /**
+ * Add aHost to our list of texture hosts.
+ */
+ void RegisterTextureHost(TextureSourceD3D9* aHost);
+
+ /* Array used to store all swap chains for device resets */
+ nsTArray<SwapChainD3D9*> mSwapChains;
+
+ /* The D3D device we use */
+ RefPtr<IDirect3DDevice9> mDevice;
+
+ /* The D3D9Ex device - only valid on Vista+ with WDDM */
+ RefPtr<IDirect3DDevice9Ex> mDeviceEx;
+
+ /* An instance of the D3D9 object */
+ RefPtr<IDirect3D9> mD3D9;
+
+ /* An instance of the D3D9Ex object - only valid on Vista+ with WDDM */
+ RefPtr<IDirect3D9Ex> mD3D9Ex;
+
+ /* Vertex shader used for layer quads */
+ RefPtr<IDirect3DVertexShader9> mLayerVS;
+
+ /* Pixel shader used for RGB textures */
+ RefPtr<IDirect3DPixelShader9> mRGBPS;
+
+ /* Pixel shader used for RGBA textures */
+ RefPtr<IDirect3DPixelShader9> mRGBAPS;
+
+ /* Pixel shader used for component alpha textures (pass 1) */
+ RefPtr<IDirect3DPixelShader9> mComponentPass1PS;
+
+ /* Pixel shader used for component alpha textures (pass 2) */
+ RefPtr<IDirect3DPixelShader9> mComponentPass2PS;
+
+ /* Pixel shader used for RGB textures */
+ RefPtr<IDirect3DPixelShader9> mYCbCrPS;
+
+ /* Pixel shader used for solid colors */
+ RefPtr<IDirect3DPixelShader9> mSolidColorPS;
+
+ /* As above, but using a mask layer */
+ RefPtr<IDirect3DVertexShader9> mLayerVSMask;
+ RefPtr<IDirect3DPixelShader9> mRGBPSMask;
+ RefPtr<IDirect3DPixelShader9> mRGBAPSMask;
+ RefPtr<IDirect3DPixelShader9> mComponentPass1PSMask;
+ RefPtr<IDirect3DPixelShader9> mComponentPass2PSMask;
+ RefPtr<IDirect3DPixelShader9> mYCbCrPSMask;
+ RefPtr<IDirect3DPixelShader9> mSolidColorPSMask;
+
+ /* Vertex buffer containing our basic vertex structure */
+ RefPtr<IDirect3DVertexBuffer9> mVB;
+
+ /* Our vertex declaration */
+ RefPtr<IDirect3DVertexDeclaration9> mVD;
+
+ /* We maintain a doubly linked list of all d3d9 texture hosts which host
+ * d3d9 textures created by this device manager.
+ * Texture hosts must remove themselves when they disappear (i.e., we
+ * expect all hosts in the list to be valid).
+ * The list is cleared when we release the textures.
+ */
+ TextureSourceD3D9* mTextureHostList;
+
+ /* Our focus window - this is really a dummy window we can associate our
+ * device with.
+ */
+ HWND mFocusWnd;
+
+ /* we use this to help track if our device temporarily or permanently lost */
+ HMONITOR mDeviceMonitor;
+
+ uint32_t mDeviceResetCount;
+
+ uint32_t mMaxTextureSize;
+
+ /**
+ * Wrap (repeat) or clamp textures. We prefer the former so we can do buffer
+ * rotation, but some older hardware doesn't support it.
+ */
+ D3DTEXTUREADDRESS mTextureAddressingMode;
+
+ /* If this device supports component alpha */
+ bool mHasComponentAlpha;
+
+ /* If this device supports dynamic textures */
+ bool mHasDynamicTextures;
+
+ /* If this device was removed */
+ bool mDeviceWasRemoved;
+
+ /* Nv3DVUtils instance */
+ nsAutoPtr<Nv3DVUtils> mNv3DVUtils;
+
+ /**
+ * Verifies all required device capabilities are present.
+ */
+ bool VerifyCaps();
+};
+
+} /* namespace layers */
+} /* namespace mozilla */
+
+#endif /* GFX_DEVICEMANAGERD3D9_H */
diff --git a/gfx/layers/d3d9/LayerManagerD3D9Shaders.h b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
new file mode 100755
index 000000000..105970c0a
--- /dev/null
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
@@ -0,0 +1,1631 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float4x4 mLayerTransform;
+// float4x4 mProjection;
+// float4 vLayerQuad;
+// float4 vRenderTargetOffset;
+// float4 vTextureCoords;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------------- ----- ----
+// mLayerTransform c0 4
+// mProjection c4 4
+// vRenderTargetOffset c8 1
+// vTextureCoords c9 1
+// vLayerQuad c10 1
+//
+
+ vs_2_0
+ def c11, -0.5, 0, 0, 0
+ dcl_position v0
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c1
+ mad r0, c0, r0.x, r1
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ rcp r1.x, r0.w
+ mul r0.xyz, r0, r1.x
+ add r0, r0, -c8
+ mad r0.xy, r0, r0.w, c11.x
+ mul r1, r0.y, c5
+ mad r1, c4, r0.x, r1
+ mul r0.x, r0.w, r0.z
+ mad r1, c6, r0.x, r1
+ mad oPos, c7, r0.w, r1
+ mad oT0.xy, v0, c9.zwzw, c9
+
+// approximately 15 instruction slots used
+#endif
+
+const BYTE LayerQuadVS[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 76, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 2, 1,
+ 0, 0, 0, 2, 254, 255,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 251, 0, 0, 0, 128, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 160, 0, 0, 0, 2, 0,
+ 4, 0, 4, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 172, 0, 0, 0,
+ 2, 0, 10, 0, 1, 0,
+ 0, 0, 184, 0, 0, 0,
+ 0, 0, 0, 0, 200, 0,
+ 0, 0, 2, 0, 8, 0,
+ 1, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 0, 0,
+ 236, 0, 0, 0, 2, 0,
+ 9, 0, 1, 0, 0, 0,
+ 184, 0, 0, 0, 0, 0,
+ 0, 0, 109, 76, 97, 121,
+ 101, 114, 84, 114, 97, 110,
+ 115, 102, 111, 114, 109, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 109, 80,
+ 114, 111, 106, 101, 99, 116,
+ 105, 111, 110, 0, 118, 76,
+ 97, 121, 101, 114, 81, 117,
+ 97, 100, 0, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 118, 84, 101, 120,
+ 116, 117, 114, 101, 67, 111,
+ 111, 114, 100, 115, 0, 118,
+ 115, 95, 50, 95, 48, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 49,
+ 48, 46, 49, 0, 171, 171,
+ 81, 0, 0, 5, 11, 0,
+ 15, 160, 0, 0, 0, 191,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 0, 0, 228, 144,
+ 10, 0, 238, 160, 10, 0,
+ 228, 160, 5, 0, 0, 3,
+ 1, 0, 15, 128, 0, 0,
+ 85, 128, 1, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 228, 160,
+ 0, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 2, 0,
+ 228, 160, 0, 0, 170, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 3, 0, 228, 160, 0, 0,
+ 255, 144, 0, 0, 228, 128,
+ 6, 0, 0, 2, 1, 0,
+ 1, 128, 0, 0, 255, 128,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 1, 0, 0, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 8, 0,
+ 228, 161, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 0, 0, 255, 128,
+ 11, 0, 0, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 5, 0,
+ 228, 160, 4, 0, 0, 4,
+ 1, 0, 15, 128, 4, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 0, 0,
+ 170, 128, 4, 0, 0, 4,
+ 1, 0, 15, 128, 6, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 192,
+ 7, 0, 228, 160, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+//
+
+ ps_2_0
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 3 instruction slots used (1 texture, 2 arithmetic)
+#endif
+
+const BYTE RGBAShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 43, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 127, 0,
+ 0, 0, 0, 2, 255, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 120, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 84, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 0, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DWhite s1 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t0, s0
+ texld r1, t0, s1
+ add r0.xyz, r0, -r1
+ add r0.xyz, r0, c1.x
+ mul r0.xyz, r0, c0.x
+ mov r0.w, r0.y
+ mov oC0, r0
+
+// approximately 7 instruction slots used (2 texture, 5 arithmetic)
+#endif
+
+const BYTE ComponentPass1ShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 55, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 175, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 168, 0, 0, 0, 88, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 120, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 152, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 87,
+ 104, 105, 116, 101, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 228, 129,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 1, 0, 0, 160, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 0, 8, 128, 0, 0,
+ 85, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DWhite s1 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t0, s1
+ texld r1, t0, s0
+ add r0.x, -r0.y, r1.y
+ add r1.w, r0.x, c1.x
+ mul r0, r1, c0.x
+ mov oC0, r0
+
+// approximately 6 instruction slots used (2 texture, 4 arithmetic)
+#endif
+
+const BYTE ComponentPass2ShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 55, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 175, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 168, 0, 0, 0, 88, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 120, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 152, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 87,
+ 104, 105, 116, 101, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 2, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 85, 129, 1, 0, 85, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 0, 128,
+ 1, 0, 0, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mov r0.w, c1.x
+ mul r0, r0, c0.x
+ mov oC0, r0
+
+// approximately 4 instruction slots used (1 texture, 3 arithmetic)
+#endif
+
+const BYTE RGBShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 43, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 127, 0,
+ 0, 0, 0, 2, 255, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 120, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 84, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 0, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 81, 0, 0, 5, 1, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 1, 0, 0, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 0,
+ 0, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 0, 0,
+ 228, 128, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// row_major float3x3 mYuvColorMatrix;
+// sampler2D s2DCb;
+// sampler2D s2DCr;
+// sampler2D s2DY;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// --------------- ----- ----
+// fLayerOpacity c0 1
+// mYuvColorMatrix c1 3
+// s2DY s0 1
+// s2DCb s1 1
+// s2DCr s2 1
+//
+
+ ps_2_0
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t0, s1
+ texld r2, t0, s2
+ mov r3.w, c4.z
+ add r0.x, r0.w, c4.x
+ add r0.y, r1.w, c4.y
+ add r0.z, r2.w, c4.y
+ dp3 r3.x, c1, r0
+ dp3 r3.y, c2, r0
+ dp3 r3.z, c3, r0
+ mul r0, r3, c0.x
+ mov oC0, r0
+
+// approximately 12 instruction slots used (3 texture, 9 arithmetic)
+#endif
+
+const BYTE YCbCrShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 79, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 15, 1,
+ 0, 0, 0, 2, 255, 255,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 8, 1, 0, 0, 128, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 160, 0, 0, 0, 2, 0,
+ 1, 0, 3, 0, 6, 0,
+ 176, 0, 0, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 200, 0, 0, 0,
+ 0, 0, 0, 0, 216, 0,
+ 0, 0, 3, 0, 2, 0,
+ 1, 0, 0, 0, 224, 0,
+ 0, 0, 0, 0, 0, 0,
+ 240, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 248, 0, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 79, 112, 97, 99,
+ 105, 116, 121, 0, 171, 171,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 109, 89,
+ 117, 118, 67, 111, 108, 111,
+ 114, 77, 97, 116, 114, 105,
+ 120, 0, 2, 0, 3, 0,
+ 3, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 67, 98, 0,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 67, 114, 0,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 89, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 18, 131, 128, 189,
+ 115, 128, 0, 191, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 1, 0, 0, 2, 3, 0,
+ 8, 128, 4, 0, 170, 160,
+ 2, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 255, 128,
+ 4, 0, 0, 160, 2, 0,
+ 0, 3, 0, 0, 2, 128,
+ 1, 0, 255, 128, 4, 0,
+ 85, 160, 2, 0, 0, 3,
+ 0, 0, 4, 128, 2, 0,
+ 255, 128, 4, 0, 85, 160,
+ 8, 0, 0, 3, 3, 0,
+ 1, 128, 1, 0, 228, 160,
+ 0, 0, 228, 128, 8, 0,
+ 0, 3, 3, 0, 2, 128,
+ 2, 0, 228, 160, 0, 0,
+ 228, 128, 8, 0, 0, 3,
+ 3, 0, 4, 128, 3, 0,
+ 228, 160, 0, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 3, 0, 228, 128,
+ 0, 0, 0, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float4 fLayerColor;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fLayerColor c0 1
+//
+
+ ps_2_0
+ mov oC0, c0
+
+// approximately 1 instruction slot used
+#endif
+
+const BYTE SolidColorShaderPS[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 32, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 83, 0,
+ 0, 0, 0, 2, 255, 255,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 76, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 67, 111, 108, 111, 114, 0,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 49, 48,
+ 46, 49, 0, 171, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 160, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float4x4 mLayerTransform;
+// float4x4 mProjection;
+// float4 vLayerQuad;
+// float4 vMaskQuad;
+// float4 vRenderTargetOffset;
+// float4 vTextureCoords;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------------- ----- ----
+// mLayerTransform c0 4
+// mProjection c4 4
+// vRenderTargetOffset c8 1
+// vTextureCoords c9 1
+// vLayerQuad c10 1
+// vMaskQuad c11 1
+//
+
+ vs_2_0
+ def c12, -0.5, 1, 0, 0
+ dcl_position v0
+ mad r0.xy, v0, c10.zwzw, c10
+ mul r1, r0.y, c1
+ mad r0, c0, r0.x, r1
+ add r0, r0, c3
+ rcp r1.x, r0.w
+ mul r1.xyz, r0, r1.x
+ mov r1.w, r0.w
+ add r2, r1, -c8
+ mad r0.zw, r2.xyxy, r2.w, c12.x
+ mul r3, r0.w, c5
+ mad r3, c4, r0.z, r3
+ mul r0.z, r2.w, r2.z
+ mad r3, c6, r0.z, r3
+ mad oPos, c7, r2.w, r3
+ add r0.xy, r0, -c11
+ rcp r0.z, c11.z
+ mul r1.x, r0.z, r0.x
+ rcp r0.x, c11.w
+ mul r1.y, r0.x, r0.y
+ mov r1.z, c12.y
+ mul oT1.xyz, r1.w, r1
+ mad oT0.xy, v0, c9.zwzw, c9
+
+// approximately 22 instruction slots used
+#endif
+
+const BYTE LayerQuadVSMask[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 84, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 34, 1,
+ 0, 0, 0, 2, 254, 255,
+ 6, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 27, 1, 0, 0, 148, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 0, 0, 0, 2, 0,
+ 4, 0, 4, 0, 0, 0,
+ 164, 0, 0, 0, 0, 0,
+ 0, 0, 192, 0, 0, 0,
+ 2, 0, 10, 0, 1, 0,
+ 0, 0, 204, 0, 0, 0,
+ 0, 0, 0, 0, 220, 0,
+ 0, 0, 2, 0, 11, 0,
+ 1, 0, 0, 0, 204, 0,
+ 0, 0, 0, 0, 0, 0,
+ 230, 0, 0, 0, 2, 0,
+ 8, 0, 1, 0, 0, 0,
+ 252, 0, 0, 0, 0, 0,
+ 0, 0, 12, 1, 0, 0,
+ 2, 0, 9, 0, 1, 0,
+ 0, 0, 204, 0, 0, 0,
+ 0, 0, 0, 0, 109, 76,
+ 97, 121, 101, 114, 84, 114,
+ 97, 110, 115, 102, 111, 114,
+ 109, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 80, 114, 111, 106, 101,
+ 99, 116, 105, 111, 110, 0,
+ 118, 76, 97, 121, 101, 114,
+ 81, 117, 97, 100, 0, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 118, 77,
+ 97, 115, 107, 81, 117, 97,
+ 100, 0, 118, 82, 101, 110,
+ 100, 101, 114, 84, 97, 114,
+ 103, 101, 116, 79, 102, 102,
+ 115, 101, 116, 0, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 118, 84,
+ 101, 120, 116, 117, 114, 101,
+ 67, 111, 111, 114, 100, 115,
+ 0, 118, 115, 95, 50, 95,
+ 48, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 49, 48, 46, 49, 0,
+ 171, 171, 81, 0, 0, 5,
+ 12, 0, 15, 160, 0, 0,
+ 0, 191, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 4, 0, 0, 4,
+ 0, 0, 3, 128, 0, 0,
+ 228, 144, 10, 0, 238, 160,
+ 10, 0, 228, 160, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 6, 0, 0, 2,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 5, 0, 0, 3,
+ 1, 0, 7, 128, 0, 0,
+ 228, 128, 1, 0, 0, 128,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 2, 0, 0, 3, 2, 0,
+ 15, 128, 1, 0, 228, 128,
+ 8, 0, 228, 161, 4, 0,
+ 0, 4, 0, 0, 12, 128,
+ 2, 0, 68, 128, 2, 0,
+ 255, 128, 12, 0, 0, 160,
+ 5, 0, 0, 3, 3, 0,
+ 15, 128, 0, 0, 255, 128,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 3, 0, 15, 128,
+ 4, 0, 228, 160, 0, 0,
+ 170, 128, 3, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 4, 128, 2, 0, 255, 128,
+ 2, 0, 170, 128, 4, 0,
+ 0, 4, 3, 0, 15, 128,
+ 6, 0, 228, 160, 0, 0,
+ 170, 128, 3, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 192, 7, 0, 228, 160,
+ 2, 0, 255, 128, 3, 0,
+ 228, 128, 2, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 228, 128, 11, 0, 228, 161,
+ 6, 0, 0, 2, 0, 0,
+ 4, 128, 11, 0, 170, 160,
+ 5, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 170, 128,
+ 0, 0, 0, 128, 6, 0,
+ 0, 2, 0, 0, 1, 128,
+ 11, 0, 255, 160, 5, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 0, 128, 0, 0,
+ 85, 128, 1, 0, 0, 2,
+ 1, 0, 4, 128, 12, 0,
+ 85, 160, 5, 0, 0, 3,
+ 1, 0, 7, 224, 1, 0,
+ 255, 128, 1, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 3, 224, 0, 0, 228, 144,
+ 9, 0, 238, 160, 9, 0,
+ 228, 160, 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DMask;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DMask s1 1
+//
+
+ ps_2_0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s1
+ texld r1, t0, s0
+ mul r1, r1, c0.x
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+// approximately 7 instruction slots used (2 texture, 5 arithmetic)
+#endif
+
+const BYTE RGBAShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 54, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 171, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 164, 0, 0, 0, 88, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 120, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 77,
+ 97, 115, 107, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 49, 48, 46, 49,
+ 0, 171, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DMask;
+// sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DWhite s1 1
+// s2DMask s2 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r2, t0, s1
+ texld r0, r0, s2
+ add r0.xyz, r1, -r2
+ add r0.xyz, r0, c1.x
+ mul r0.xyz, r0, c0.x
+ mul r0.xyz, r0.w, r0
+ mov r0.w, r0.y
+ mov oC0, r0
+
+// approximately 11 instruction slots used (3 texture, 8 arithmetic)
+#endif
+
+const BYTE ComponentPass1ShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 66, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 219, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 212, 0, 0, 0, 108, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 124, 0,
+ 0, 0, 0, 0, 0, 0,
+ 140, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 160, 0, 0, 0,
+ 3, 0, 2, 0, 1, 0,
+ 0, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 0,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 77, 97, 115, 107, 0,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 87, 104, 105, 116, 101,
+ 0, 171, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 49, 48, 46, 49,
+ 0, 171, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 2, 8, 228, 160,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 1, 0, 228, 128,
+ 2, 0, 228, 129, 2, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 255, 128,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 0, 0, 85, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DMask;
+// sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DWhite s1 1
+// s2DMask s2 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s1
+ texld r2, t0, s0
+ texld r0, r0, s2
+ add r0.x, -r1.y, r2.y
+ add r2.w, r0.x, c1.x
+ mul r1, r2, c0.x
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+// approximately 10 instruction slots used (3 texture, 7 arithmetic)
+#endif
+
+const BYTE ComponentPass2ShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 66, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 219, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 212, 0, 0, 0, 108, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 124, 0,
+ 0, 0, 0, 0, 0, 0,
+ 140, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 160, 0, 0, 0,
+ 3, 0, 2, 0, 1, 0,
+ 0, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 0, 0, 196, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 76, 97, 121, 101, 114,
+ 79, 112, 97, 99, 105, 116,
+ 121, 0, 171, 171, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 0,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 77, 97, 115, 107, 0,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 50,
+ 68, 87, 104, 105, 116, 101,
+ 0, 171, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 49, 48, 46, 49,
+ 0, 171, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 2, 8, 228, 160,
+ 2, 0, 0, 3, 0, 0,
+ 1, 128, 1, 0, 85, 129,
+ 2, 0, 85, 128, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 0, 0, 0, 128, 1, 0,
+ 0, 160, 5, 0, 0, 3,
+ 1, 0, 15, 128, 2, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// sampler2D s2D;
+// sampler2D s2DMask;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------- ----- ----
+// fLayerOpacity c0 1
+// s2D s0 1
+// s2DMask s1 1
+//
+
+ ps_2_0
+ def c1, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r0, r0, s1
+ mov r1.w, c1.x
+ mul r1, r1, c0.x
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+// approximately 8 instruction slots used (2 texture, 6 arithmetic)
+#endif
+
+const BYTE RGBShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 54, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 171, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 164, 0, 0, 0, 88, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 104, 0,
+ 0, 0, 0, 0, 0, 0,
+ 120, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 148, 0, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 77,
+ 97, 115, 107, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 49, 48, 46, 49,
+ 0, 171, 81, 0, 0, 5,
+ 1, 0, 15, 160, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 1, 8,
+ 228, 160, 1, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 0, 160, 5, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 0, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 255, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 0, 0, 228, 128, 255, 255,
+ 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float fLayerOpacity;
+// row_major float3x3 mYuvColorMatrix;
+// sampler2D s2DCb;
+// sampler2D s2DCr;
+// sampler2D s2DMask;
+// sampler2D s2DY;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// --------------- ----- ----
+// fLayerOpacity c0 1
+// mYuvColorMatrix c1 3
+// s2DY s0 1
+// s2DCb s1 1
+// s2DCr s2 1
+// s2DMask s3 1
+//
+
+ ps_2_0
+ def c4, -0.0627499968, -0.50195998, 1, 0
+ dcl t0.xy
+ dcl t1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ dcl_2d s3
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r1, t0, s0
+ texld r2, t0, s1
+ texld r3, t0, s2
+ texld r0, r0, s3
+ mov r4.w, c4.z
+ add r0.x, r1.w, c4.x
+ add r0.y, r2.w, c4.y
+ add r0.z, r3.w, c4.y
+ dp3 r4.x, c1, r0
+ dp3 r4.y, c2, r0
+ dp3 r4.z, c3, r0
+ mul r1, r4, c0.x
+ mul r0, r0.w, r1
+ mov oC0, r0
+
+// approximately 16 instruction slots used (4 texture, 12 arithmetic)
+#endif
+
+const BYTE YCbCrShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 90, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 59, 1,
+ 0, 0, 0, 2, 255, 255,
+ 6, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 52, 1, 0, 0, 148, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 0, 0, 0, 2, 0,
+ 1, 0, 3, 0, 6, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 0, 0, 220, 0, 0, 0,
+ 0, 0, 0, 0, 236, 0,
+ 0, 0, 3, 0, 2, 0,
+ 1, 0, 0, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 1, 0, 0, 3, 0,
+ 3, 0, 1, 0, 0, 0,
+ 12, 1, 0, 0, 0, 0,
+ 0, 0, 28, 1, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 36, 1, 0, 0,
+ 0, 0, 0, 0, 102, 76,
+ 97, 121, 101, 114, 79, 112,
+ 97, 99, 105, 116, 121, 0,
+ 171, 171, 0, 0, 3, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 109, 89, 117, 118, 67, 111,
+ 108, 111, 114, 77, 97, 116,
+ 114, 105, 120, 0, 2, 0,
+ 3, 0, 3, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 67,
+ 98, 0, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 67,
+ 114, 0, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 77,
+ 97, 115, 107, 0, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 50, 68, 89,
+ 0, 171, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 49, 48, 46, 49,
+ 0, 171, 81, 0, 0, 5,
+ 4, 0, 15, 160, 18, 131,
+ 128, 189, 115, 128, 0, 191,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 3, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 1, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 1, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 2, 8,
+ 15, 160, 31, 0, 0, 2,
+ 0, 0, 0, 144, 3, 8,
+ 15, 160, 6, 0, 0, 2,
+ 0, 0, 8, 128, 1, 0,
+ 170, 176, 5, 0, 0, 3,
+ 0, 0, 3, 128, 0, 0,
+ 255, 128, 1, 0, 228, 176,
+ 66, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 2, 0, 15, 128,
+ 0, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 3, 0, 15, 128, 0, 0,
+ 228, 176, 2, 8, 228, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 3, 8, 228, 160, 1, 0,
+ 0, 2, 4, 0, 8, 128,
+ 4, 0, 170, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 1, 0, 255, 128, 4, 0,
+ 0, 160, 2, 0, 0, 3,
+ 0, 0, 2, 128, 2, 0,
+ 255, 128, 4, 0, 85, 160,
+ 2, 0, 0, 3, 0, 0,
+ 4, 128, 3, 0, 255, 128,
+ 4, 0, 85, 160, 8, 0,
+ 0, 3, 4, 0, 1, 128,
+ 1, 0, 228, 160, 0, 0,
+ 228, 128, 8, 0, 0, 3,
+ 4, 0, 2, 128, 2, 0,
+ 228, 160, 0, 0, 228, 128,
+ 8, 0, 0, 3, 4, 0,
+ 4, 128, 3, 0, 228, 160,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 4, 0, 228, 128, 0, 0,
+ 0, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 255, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 10.1
+//
+// Parameters:
+//
+// float4 fLayerColor;
+// sampler2D s2DMask;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fLayerColor c0 1
+// s2DMask s0 1
+//
+
+ ps_2_0
+ dcl t1.xyz
+ dcl_2d s0
+ rcp r0.w, t1.z
+ mul r0.xy, r0.w, t1
+ texld r0, r0, s0
+ mul r0, r0.w, c0
+ mov oC0, r0
+
+// approximately 5 instruction slots used (1 texture, 4 arithmetic)
+#endif
+
+const BYTE SolidColorShaderPSMask[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 43, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 127, 0,
+ 0, 0, 0, 2, 255, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 120, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 0, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 0, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 102, 76, 97, 121,
+ 101, 114, 67, 111, 108, 111,
+ 114, 0, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 50, 68, 77, 97, 115,
+ 107, 0, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 49, 48, 46, 49, 0, 171,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 6, 0, 0, 2, 0, 0,
+ 8, 128, 1, 0, 170, 176,
+ 5, 0, 0, 3, 0, 0,
+ 3, 128, 0, 0, 255, 128,
+ 1, 0, 228, 176, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 255, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
new file mode 100644
index 000000000..1f3a812f3
--- /dev/null
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
@@ -0,0 +1,242 @@
+float4x4 mLayerTransform;
+float4 vRenderTargetOffset;
+float4x4 mProjection;
+
+typedef float4 rect;
+rect vTextureCoords;
+rect vLayerQuad;
+rect vMaskQuad;
+
+texture tex0;
+sampler s2D;
+sampler s2DWhite;
+sampler s2DY;
+sampler s2DCb;
+sampler s2DCr;
+sampler s2DMask;
+
+
+float fLayerOpacity;
+float4 fLayerColor;
+row_major float3x3 mYuvColorMatrix : register(ps, c1);
+
+struct VS_INPUT {
+ float4 vPosition : POSITION;
+};
+
+struct VS_OUTPUT {
+ float4 vPosition : POSITION;
+ float2 vTexCoords : TEXCOORD0;
+};
+
+struct VS_OUTPUT_MASK {
+ float4 vPosition : POSITION;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+};
+
+VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT outp;
+ outp.vPosition = aVertex.vPosition;
+
+ // We use 4 component floats to uniquely describe a rectangle, by the structure
+ // of x, y, width, height. This allows us to easily generate the 4 corners
+ // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+ // stream source for our LayerQuad vertex shader. We do this by doing:
+ // Xout = x + Xin * width
+ // Yout = y + Yin * height
+ float2 position = vLayerQuad.xy;
+ float2 size = vLayerQuad.zw;
+ outp.vPosition.x = position.x + outp.vPosition.x * size.x;
+ outp.vPosition.y = position.y + outp.vPosition.y * size.y;
+
+ outp.vPosition = mul(mLayerTransform, outp.vPosition);
+ outp.vPosition.xyz /= outp.vPosition.w;
+ outp.vPosition = outp.vPosition - vRenderTargetOffset;
+ outp.vPosition.xyz *= outp.vPosition.w;
+
+ // adjust our vertices to match d3d9's pixel coordinate system
+ // which has pixel centers at integer locations
+ outp.vPosition.xy -= 0.5;
+
+ outp.vPosition = mul(mProjection, outp.vPosition);
+
+ position = vTextureCoords.xy;
+ size = vTextureCoords.zw;
+ outp.vTexCoords.x = position.x + aVertex.vPosition.x * size.x;
+ outp.vTexCoords.y = position.y + aVertex.vPosition.y * size.y;
+
+ return outp;
+}
+
+VS_OUTPUT_MASK LayerQuadVSMask(const VS_INPUT aVertex)
+{
+ VS_OUTPUT_MASK outp;
+ float4 position = float4(0, 0, 0, 1);
+
+ // We use 4 component floats to uniquely describe a rectangle, by the structure
+ // of x, y, width, height. This allows us to easily generate the 4 corners
+ // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+ // stream source for our LayerQuad vertex shader. We do this by doing:
+ // Xout = x + Xin * width
+ // Yout = y + Yin * height
+ float2 size = vLayerQuad.zw;
+ position.x = vLayerQuad.x + aVertex.vPosition.x * size.x;
+ position.y = vLayerQuad.y + aVertex.vPosition.y * size.y;
+
+ position = mul(mLayerTransform, position);
+ outp.vPosition.w = position.w;
+ outp.vPosition.xyz = position.xyz / position.w;
+ outp.vPosition = outp.vPosition - vRenderTargetOffset;
+ outp.vPosition.xyz *= outp.vPosition.w;
+
+ // adjust our vertices to match d3d9's pixel coordinate system
+ // which has pixel centers at integer locations
+ outp.vPosition.xy -= 0.5;
+
+ outp.vPosition = mul(mProjection, outp.vPosition);
+
+ // calculate the position on the mask texture
+ outp.vMaskCoords.x = (position.x - vMaskQuad.x) / vMaskQuad.z;
+ outp.vMaskCoords.y = (position.y - vMaskQuad.y) / vMaskQuad.w;
+ // correct for perspective correct interpolation, see comment in D3D11 shader
+ outp.vMaskCoords.z = 1;
+ outp.vMaskCoords *= position.w;
+
+ size = vTextureCoords.zw;
+ outp.vTexCoords.x = vTextureCoords.x + aVertex.vPosition.x * size.x;
+ outp.vTexCoords.y = vTextureCoords.y + aVertex.vPosition.y * size.y;
+
+ return outp;
+}
+
+float4 ComponentPass1Shader(const VS_OUTPUT aVertex) : COLOR
+{
+ float4 src = tex2D(s2D, aVertex.vTexCoords);
+ float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+ alphas.a = alphas.g;
+ return alphas * fLayerOpacity;
+}
+
+float4 ComponentPass2Shader(const VS_OUTPUT aVertex) : COLOR
+{
+ float4 src = tex2D(s2D, aVertex.vTexCoords);
+ float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+ src.a = alphas.g;
+ return src * fLayerOpacity;
+}
+
+float4 RGBAShader(const VS_OUTPUT aVertex) : COLOR
+{
+ return tex2D(s2D, aVertex.vTexCoords) * fLayerOpacity;
+}
+
+float4 RGBShader(const VS_OUTPUT aVertex) : COLOR
+{
+ float4 result;
+ result = tex2D(s2D, aVertex.vTexCoords);
+ result.a = 1.0;
+ return result * fLayerOpacity;
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+float4 YCbCrShader(const VS_OUTPUT aVertex) : COLOR
+{
+ float3 yuv;
+ float4 color;
+
+ yuv.x = tex2D(s2DY, aVertex.vTexCoords).a - 0.06275;
+ yuv.y = tex2D(s2DCb, aVertex.vTexCoords).a - 0.50196;
+ yuv.z = tex2D(s2DCr, aVertex.vTexCoords).a - 0.50196;
+
+ color.rgb = mul(mYuvColorMatrix, yuv);
+ color.a = 1.0f;
+
+ return color * fLayerOpacity;
+}
+
+float4 SolidColorShader(const VS_OUTPUT aVertex) : COLOR
+{
+ return fLayerColor;
+}
+
+float4 ComponentPass1ShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float4 src = tex2D(s2D, aVertex.vTexCoords);
+ float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+ alphas.a = alphas.g;
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return alphas * fLayerOpacity * mask;
+}
+
+float4 ComponentPass2ShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float4 src = tex2D(s2D, aVertex.vTexCoords);
+ float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+ src.a = alphas.g;
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return src * fLayerOpacity * mask;
+}
+
+float4 RGBAShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return tex2D(s2D, aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+float4 RGBShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float4 result;
+ result = tex2D(s2D, aVertex.vTexCoords);
+ result.a = 1.0;
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return result * fLayerOpacity * mask;
+}
+
+float4 YCbCrShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float3 yuv;
+ float4 color;
+
+ yuv.x = tex2D(s2DY, aVertex.vTexCoords).a - 0.06275;
+ yuv.y = tex2D(s2DCb, aVertex.vTexCoords).a - 0.50196;
+ yuv.z = tex2D(s2DCr, aVertex.vTexCoords).a - 0.50196;
+
+ color.rgb = mul((float3x3)mYuvColorMatrix, yuv);
+ color.a = 1.0f;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return color * fLayerOpacity * mask;
+}
+
+float4 SolidColorShaderMask(const VS_OUTPUT_MASK aVertex) : COLOR
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tex2D(s2DMask, maskCoords).a;
+ return fLayerColor * mask;
+}
diff --git a/gfx/layers/d3d9/Nv3DVUtils.cpp b/gfx/layers/d3d9/Nv3DVUtils.cpp
new file mode 100644
index 000000000..72638228f
--- /dev/null
+++ b/gfx/layers/d3d9/Nv3DVUtils.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+#include <initguid.h>
+#include "Nv3DVUtils.h"
+
+DEFINE_GUID(CLSID_NV3DVStreaming,
+0xf7747266, 0x777d, 0x4f61, 0xa1, 0x75, 0xdd, 0x5a, 0xdf, 0x1e, 0x37, 0xdf);
+
+DEFINE_GUID(IID_INV3DVStreaming,
+0xf98f9bb2, 0xb914, 0x4d44, 0x98, 0xfa, 0x6e, 0x37, 0x85, 0x16, 0x98, 0x55);
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Constructor and Destructor
+ */
+Nv3DVUtils::Nv3DVUtils()
+ : m3DVStreaming (nullptr)
+{
+}
+
+Nv3DVUtils::~Nv3DVUtils()
+{
+ UnInitialize();
+}
+
+
+// Silence spurious warnings!
+#if defined(WARNING) || defined WARN_IF_FALSE
+#error We shouldn't be redefining these!
+#endif
+// Uncomment these to enable spurious warnings.
+//#define WARNING(str) NS_WARNING(str)
+//#define WARN_IF_FALSE(b, str) NS_WARNING_ASSERTION(b, str)
+#define WARNING(str)
+#define WARN_IF_FALSE(b, str)
+
+/**
+ * Initializes the Nv3DVUtils object.
+ */
+void
+Nv3DVUtils::Initialize()
+{
+ /*
+ * Detect if 3D Streaming object is already loaded. Do nothing in that case.
+ */
+ if (m3DVStreaming) {
+ WARNING("Nv3DVStreaming COM object already instantiated.\n");
+ return;
+ }
+
+ /*
+ * Create the COM object. If we fail at any stage, just return
+ */
+ HRESULT hr = CoCreateInstance(CLSID_NV3DVStreaming, nullptr, CLSCTX_INPROC_SERVER, IID_INV3DVStreaming, (void**)(getter_AddRefs(m3DVStreaming)));
+ if (FAILED(hr) || !m3DVStreaming) {
+ WARNING("Nv3DVStreaming CoCreateInstance failed (disabled).");
+ return;
+ }
+
+ /*
+ * Initialize the object. Note that m3DVStreaming cannot be nullptr at this point.
+ */
+ bool bRetVal = m3DVStreaming->Nv3DVInitialize();
+
+ if (!bRetVal) {
+ WARNING("Nv3DVStreaming Nv3DVInitialize failed!");
+ return;
+ }
+}
+
+/**
+ * Release resources used by the COM Object, and then release
+ * the COM Object (nsRefPtr gets released by setting to nullptr)
+ *
+ */
+void
+Nv3DVUtils::UnInitialize()
+{
+ if (m3DVStreaming) {
+ m3DVStreaming->Nv3DVRelease();
+ }
+}
+
+/**
+ * Sets the device info, along with any other initialization that is needed after device creation
+ * Pass the D3D9 device pointer is an IUnknown input argument.
+ */
+void
+Nv3DVUtils::SetDeviceInfo(IUnknown *devUnknown)
+{
+ if (!devUnknown) {
+ WARNING("D3D Device Pointer (IUnknown) is nullptr.\n");
+ return;
+ }
+
+ if (!m3DVStreaming) {
+ return;
+ }
+
+ bool rv = m3DVStreaming->Nv3DVSetDevice(devUnknown);
+ if (!rv) {
+ WARNING("Nv3DVStreaming Nv3DVControl failed!");
+ return;
+ }
+
+ rv = m3DVStreaming->Nv3DVControl(NV_STEREO_MODE_RIGHT_LEFT, true, FIREFOX_3DV_APP_HANDLE);
+ WARN_IF_FALSE(rv, "Nv3DVStreaming Nv3DVControl failed!");
+}
+
+/*
+ * Send Stereo Control Information. Used mainly to re-route
+ * calls from ImageLayerD3D9 to the 3DV COM object
+ */
+void
+Nv3DVUtils::SendNv3DVControl(Nv_Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle)
+{
+ if (!m3DVStreaming)
+ return;
+
+ DebugOnly<bool> rv = m3DVStreaming->Nv3DVControl(eStereoMode, bEnableStereo, dw3DVAppHandle);
+ WARN_IF_FALSE(rv, "Nv3DVStreaming Nv3DVControl failed!");
+}
+
+/*
+ * Send Stereo Metadata. Used mainly to re-route calls
+ * from ImageLayerD3D9 to the 3DV COM object
+ */
+void
+Nv3DVUtils::SendNv3DVMetaData(unsigned int dwWidth, unsigned int dwHeight, HANDLE hSrcLuma, HANDLE hDst)
+{
+ if (!m3DVStreaming)
+ return;
+
+ DebugOnly<bool> rv = m3DVStreaming->Nv3DVMetaData((DWORD)dwWidth, (DWORD)dwHeight, hSrcLuma, hDst);
+ WARN_IF_FALSE(rv, "Nv3DVStreaming Nv3DVMetaData failed!");
+}
+
+} /* namespace layers */
+} /* namespace mozilla */
diff --git a/gfx/layers/d3d9/Nv3DVUtils.h b/gfx/layers/d3d9/Nv3DVUtils.h
new file mode 100644
index 000000000..0712dd888
--- /dev/null
+++ b/gfx/layers/d3d9/Nv3DVUtils.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_NV3DVUTILS_H
+#define GFX_NV3DVUTILS_H
+
+#include "Layers.h"
+#include <windows.h>
+#include <d3d9.h>
+
+namespace mozilla {
+namespace layers {
+
+#define FIREFOX_3DV_APP_HANDLE 0xECB992B6
+
+enum Nv_Stereo_Mode {
+ NV_STEREO_MODE_LEFT_RIGHT = 0,
+ NV_STEREO_MODE_RIGHT_LEFT = 1,
+ NV_STEREO_MODE_TOP_BOTTOM = 2,
+ NV_STEREO_MODE_BOTTOM_TOP = 3,
+ NV_STEREO_MODE_MONO = 4,
+ NV_STEREO_MODE_LAST = 5
+};
+
+class INv3DVStreaming : public IUnknown {
+
+public:
+ virtual bool Nv3DVInitialize() = 0;
+ virtual bool Nv3DVRelease() = 0;
+ virtual bool Nv3DVSetDevice(IUnknown* pDevice) = 0;
+ virtual bool Nv3DVControl(Nv_Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle) = 0;
+ virtual bool Nv3DVMetaData(DWORD dwWidth, DWORD dwHeight, HANDLE hSrcLuma, HANDLE hDst) = 0;
+};
+
+/*
+ * Nv3DVUtils class
+ */
+class Nv3DVUtils {
+
+public:
+ Nv3DVUtils();
+ ~Nv3DVUtils();
+
+ /*
+ * Initializes the Nv3DVUtils object.
+ */
+ void Initialize();
+
+ /*
+ * Release any resources if needed
+ *
+ */
+ void UnInitialize();
+
+ /*
+ * Sets the device info, along with any other initialization that is needed after device creation
+ * Pass the D3D9 device pointer is an IUnknown input argument
+ */
+ void SetDeviceInfo(IUnknown *devUnknown);
+
+ /*
+ * Send Stereo Control Information. Used mainly to re-route
+ * calls from ImageLayerD3D9 to the 3DV COM object
+ */
+ void SendNv3DVControl(Nv_Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle);
+
+ /*
+ * Send Stereo Metadata. Used mainly to re-route calls
+ * from ImageLayerD3D9 to the 3DV COM object
+ */
+ void SendNv3DVMetaData(unsigned int dwWidth, unsigned int dwHeight, HANDLE hSrcLuma, HANDLE hDst);
+
+private:
+
+ /* Nv3DVStreaming interface pointer */
+ RefPtr<INv3DVStreaming> m3DVStreaming;
+
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_NV3DVUTILS_H */
diff --git a/gfx/layers/d3d9/ReadbackLayerD3D9.h b/gfx/layers/d3d9/ReadbackLayerD3D9.h
new file mode 100644
index 000000000..5d3a7fd1e
--- /dev/null
+++ b/gfx/layers/d3d9/ReadbackLayerD3D9.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_READBACKLAYERD3D9_H
+#define GFX_READBACKLAYERD3D9_H
+
+#include "LayerManagerD3D9.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackLayerD3D9 :
+ public ReadbackLayer,
+ public LayerD3D9
+{
+public:
+ ReadbackLayerD3D9(LayerManagerD3D9 *aManager)
+ : ReadbackLayer(aManager, nullptr),
+ LayerD3D9(aManager)
+ {
+ mImplData = static_cast<LayerD3D9*>(this);
+ }
+
+ virtual Layer* GetLayer() { return this; }
+ virtual void RenderLayer() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKLAYERD3D9_H */
diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp
new file mode 100644
index 000000000..9828450d1
--- /dev/null
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -0,0 +1,1218 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureD3D9.h"
+#include "CompositorD3D9.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "Effects.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "GeckoProfiler.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+static uint32_t GetRequiredTilesD3D9(uint32_t aSize, uint32_t aMaxSize)
+{
+ uint32_t requiredTiles = aSize / aMaxSize;
+ if (aSize % aMaxSize) {
+ requiredTiles++;
+ }
+ return requiredTiles;
+}
+
+TextureSourceD3D9::~TextureSourceD3D9()
+{
+ MOZ_ASSERT(!mCreatingDeviceManager ||
+ mCreatingDeviceManager->IsInTextureHostList(this),
+ "Inconsistency in list of texture hosts.");
+ // Remove ourselves from the list of d3d9 texture hosts.
+ if (mPreviousHost) {
+ MOZ_ASSERT(mPreviousHost->mNextHost == this);
+ mPreviousHost->mNextHost = mNextHost;
+ } else if (mCreatingDeviceManager) {
+ mCreatingDeviceManager->RemoveTextureListHead(this);
+ }
+ if (mNextHost) {
+ MOZ_ASSERT(mNextHost->mPreviousHost == this);
+ mNextHost->mPreviousHost = mPreviousHost;
+ }
+}
+
+already_AddRefed<TextureHost>
+CreateTextureHostD3D9(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorD3D9: {
+ result = new TextureHostD3D9(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+ result = new DXGITextureHostD3D9(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
+ result = new DXGIYCbCrTextureHostD3D9(aFlags, aDesc.get_SurfaceDescriptorDXGIYCbCr());
+ break;
+ }
+ default: {
+ NS_WARNING("Unsupported SurfaceDescriptor type");
+ }
+ }
+ return result.forget();
+}
+
+static SurfaceFormat
+D3D9FormatToSurfaceFormat(_D3DFORMAT format)
+{
+ switch (format) {
+ case D3DFMT_X8R8G8B8:
+ return SurfaceFormat::B8G8R8X8;
+ case D3DFMT_A8R8G8B8:
+ return SurfaceFormat::B8G8R8A8;
+ case D3DFMT_A8:
+ return SurfaceFormat::A8;
+ default:
+ NS_ERROR("Bad texture format");
+ }
+ return SurfaceFormat::UNKNOWN;
+}
+
+static _D3DFORMAT
+SurfaceFormatToD3D9Format(SurfaceFormat format)
+{
+ switch (format) {
+ case SurfaceFormat::B8G8R8X8:
+ return D3DFMT_X8R8G8B8;
+ case SurfaceFormat::B8G8R8A8:
+ return D3DFMT_A8R8G8B8;
+ case SurfaceFormat::A8:
+ return D3DFMT_A8;
+ default:
+ NS_ERROR("Bad texture format");
+ }
+ return D3DFMT_A8R8G8B8;
+}
+
+CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DTexture9* aTexture,
+ SurfaceInitMode aInit,
+ const gfx::IntRect& aRect)
+ : CompositingRenderTarget(aRect.TopLeft())
+ , mInitMode(aInit)
+{
+ MOZ_COUNT_CTOR(CompositingRenderTargetD3D9);
+ MOZ_ASSERT(aTexture);
+
+ mTexture = aTexture;
+ mTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface));
+ NS_ASSERTION(mSurface, "Couldn't create surface for texture");
+ TextureSourceD3D9::SetSize(aRect.Size());
+
+ if (aInit == INIT_MODE_CLEAR) {
+ ClearOnBind();
+ }
+}
+
+CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DSurface9* aSurface,
+ SurfaceInitMode aInit,
+ const gfx::IntRect& aRect)
+ : CompositingRenderTarget(aRect.TopLeft())
+ , mSurface(aSurface)
+ , mInitMode(aInit)
+{
+ MOZ_COUNT_CTOR(CompositingRenderTargetD3D9);
+ MOZ_ASSERT(mSurface);
+ TextureSourceD3D9::SetSize(aRect.Size());
+
+ if (aInit == INIT_MODE_CLEAR) {
+ ClearOnBind();
+ }
+}
+
+CompositingRenderTargetD3D9::~CompositingRenderTargetD3D9()
+{
+ MOZ_COUNT_DTOR(CompositingRenderTargetD3D9);
+}
+
+void
+CompositingRenderTargetD3D9::BindRenderTarget(IDirect3DDevice9* aDevice)
+{
+ aDevice->SetRenderTarget(0, mSurface);
+ if (mClearOnBind) {
+ aDevice->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0);
+ mClearOnBind = false;
+ }
+}
+
+IntSize
+CompositingRenderTargetD3D9::GetSize() const
+{
+ return TextureSourceD3D9::GetSize();
+}
+
+/**
+ * Helper method for DataToTexture.
+ * The last three params are out params.
+ * Returns the created texture, or null if we fail.
+ */
+already_AddRefed<IDirect3DTexture9>
+TextureSourceD3D9::InitTextures(DeviceManagerD3D9* aDeviceManager,
+ const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ RefPtr<IDirect3DSurface9>& aSurface,
+ D3DLOCKED_RECT& aLockedRect)
+{
+ if (!aDeviceManager) {
+ return nullptr;
+ }
+ RefPtr<IDirect3DTexture9> result;
+ // D3D9Ex doesn't support managed textures and we don't want the hassle even
+ // if we don't have Ex. We could use dynamic textures
+ // here but since Images are immutable that probably isn't such a great
+ // idea.
+ result = aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_DEFAULT, this);
+ if (!result) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> tmpTexture =
+ aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_SYSTEMMEM, this);
+ if (!tmpTexture) {
+ return nullptr;
+ }
+
+ tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface));
+
+ HRESULT hr = aSurface->LockRect(&aLockedRect, nullptr, 0);
+ if (FAILED(hr) || !aLockedRect.pBits) {
+ gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr);
+ return nullptr;
+ }
+
+ return result.forget();
+}
+
+/**
+ * Helper method for DataToTexture.
+ */
+static void
+FinishTextures(DeviceManagerD3D9* aDeviceManager,
+ IDirect3DTexture9* aTexture,
+ IDirect3DSurface9* aSurface)
+{
+ if (!aDeviceManager) {
+ return;
+ }
+
+ aSurface->UnlockRect();
+ RefPtr<IDirect3DSurface9> dstSurface;
+ aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+ aDeviceManager->device()->UpdateSurface(aSurface, nullptr, dstSurface,
+ nullptr);
+}
+
+already_AddRefed<IDirect3DTexture9>
+TextureSourceD3D9::DataToTexture(DeviceManagerD3D9* aDeviceManager,
+ unsigned char *aData,
+ int aStride,
+ const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ uint32_t aBPP)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+ RefPtr<IDirect3DSurface9> surface;
+ D3DLOCKED_RECT lockedRect;
+ RefPtr<IDirect3DTexture9> texture = InitTextures(aDeviceManager, aSize, aFormat,
+ surface, lockedRect);
+ if (!texture) {
+ return nullptr;
+ }
+
+ uint32_t width = aSize.width * aBPP;
+
+ for (int y = 0; y < aSize.height; y++) {
+ memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y,
+ aData + aStride * y,
+ width);
+ }
+
+ FinishTextures(aDeviceManager, texture, surface);
+
+ return texture.forget();
+}
+
+already_AddRefed<IDirect3DTexture9>
+TextureSourceD3D9::TextureToTexture(DeviceManagerD3D9* aDeviceManager,
+ IDirect3DTexture9* aTexture,
+ const IntSize& aSize,
+ _D3DFORMAT aFormat)
+{
+ if (!aDeviceManager) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture =
+ aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_DEFAULT, this);
+ if (!texture) {
+ return nullptr;
+ }
+
+ HRESULT hr = aDeviceManager->device()->UpdateTexture(aTexture, texture);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ return texture.forget();
+}
+
+DataTextureSourceD3D9::DataTextureSourceD3D9(gfx::SurfaceFormat aFormat,
+ CompositorD3D9* aCompositor,
+ TextureFlags aFlags,
+ StereoMode aStereoMode)
+ : mCompositor(aCompositor)
+ , mFormat(aFormat)
+ , mCurrentTile(0)
+ , mFlags(aFlags)
+ , mIsTiled(false)
+ , mIterating(false)
+ , mAllowTextureUploads(true)
+{
+ mStereoMode = aStereoMode;
+ MOZ_COUNT_CTOR(DataTextureSourceD3D9);
+}
+
+DataTextureSourceD3D9::DataTextureSourceD3D9(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ CompositorD3D9* aCompositor,
+ IDirect3DTexture9* aTexture,
+ TextureFlags aFlags)
+ : mCompositor(aCompositor)
+ , mFormat(aFormat)
+ , mCurrentTile(0)
+ , mFlags(aFlags)
+ , mIsTiled(false)
+ , mIterating(false)
+ , mAllowTextureUploads(false)
+{
+ mSize = aSize;
+ mTexture = aTexture;
+ mStereoMode = StereoMode::MONO;
+ MOZ_COUNT_CTOR(DataTextureSourceD3D9);
+}
+
+DataTextureSourceD3D9::~DataTextureSourceD3D9()
+{
+ MOZ_COUNT_DTOR(DataTextureSourceD3D9);
+}
+
+IDirect3DTexture9*
+DataTextureSourceD3D9::GetD3D9Texture()
+{
+ return mIterating ? mTileTextures[mCurrentTile]
+ : mTexture;
+}
+
+bool
+DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+ // Right now we only support full surface update. If aDestRegion is provided,
+ // It will be ignored. Incremental update with a source offset is only used
+ // on Mac so it is not clear that we ever will need to support it for D3D.
+ MOZ_ASSERT(!aSrcOffset);
+
+ MOZ_ASSERT(mAllowTextureUploads);
+ if (!mAllowTextureUploads) {
+ return false;
+ }
+
+ if (!mCompositor || !mCompositor->device()) {
+ NS_WARNING("No D3D device to update the texture.");
+ return false;
+ }
+
+ uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+ RefPtr<DeviceManagerD3D9> deviceManager = DeviceManagerD3D9::Get();
+
+ mSize = aSurface->GetSize();
+ mFormat = aSurface->GetFormat();
+
+ _D3DFORMAT format = D3DFMT_A8R8G8B8;
+ switch (mFormat) {
+ case SurfaceFormat::B8G8R8X8:
+ format = D3DFMT_X8R8G8B8;
+ bpp = 4;
+ break;
+ case SurfaceFormat::B8G8R8A8:
+ format = D3DFMT_A8R8G8B8;
+ bpp = 4;
+ break;
+ case SurfaceFormat::A8:
+ format = D3DFMT_A8;
+ bpp = 1;
+ break;
+ default:
+ NS_WARNING("Bad image format");
+ return false;
+ }
+
+ int32_t maxSize = mCompositor->GetMaxTextureSize();
+ if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
+ (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+ mIsTiled = false;
+
+ if (mTexture) {
+ D3DSURFACE_DESC currentDesc;
+ mTexture->GetLevelDesc(0, &currentDesc);
+
+ // Make sure there's no size mismatch, if there is, recreate.
+ if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height ||
+ currentDesc.Format != format) {
+ mTexture = nullptr;
+ // Make sure we upload the whole surface.
+ aDestRegion = nullptr;
+ }
+ }
+
+ if (!mTexture) {
+ // TODO Improve: Reallocating this texture is costly enough
+ // that it causes us to skip frames on scrolling
+ // important pages like Facebook.
+ mTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_DEFAULT, this);
+ mIsTiled = false;
+ if (!mTexture) {
+ Reset();
+ return false;
+ }
+
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ aDestRegion = nullptr;
+ }
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ gfxCriticalError() << "Failed to map surface.";
+ Reset();
+ return false;
+ }
+
+ nsIntRegion regionToUpdate = aDestRegion ? *aDestRegion : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height));
+
+ RefPtr<IDirect3DTexture9> srcTexture;
+ RefPtr<IDirect3DSurface9> srcSurface;
+
+ if (mFormat == SurfaceFormat::A8) {
+ // A8 doesn't appear to work with CreateOffscreenPlainSurface
+ srcTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_SYSTEMMEM, this);
+ if (!srcTexture) {
+ aSurface->Unmap();
+ return false;
+ }
+ srcTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+ } else {
+ HRESULT hr = mCompositor->device()->CreateOffscreenPlainSurface(mSize.width, mSize.height, format, D3DPOOL_SYSTEMMEM, getter_AddRefs(srcSurface), nullptr);
+ if (FAILED(hr)) {
+ aSurface->Unmap();
+ return false;
+ }
+ }
+
+ RefPtr<IDirect3DSurface9> destSurface;
+ mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
+
+ D3DLOCKED_RECT rect;
+ HRESULT hr = srcSurface->LockRect(&rect, nullptr, 0);
+ if (FAILED(hr) || !rect.pBits) {
+ gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr);
+ return false;
+ }
+
+ for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) {
+ const nsIntRect& iterRect = iter.Get();
+ uint8_t* src = map.mData + map.mStride * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x;
+ uint8_t* dest = reinterpret_cast<uint8_t*>(rect.pBits) + rect.Pitch * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x;
+
+ for (int y = 0; y < iterRect.height; y++) {
+ memcpy(dest + rect.Pitch * y,
+ src + map.mStride * y,
+ iterRect.width * bpp);
+ }
+ }
+
+ srcSurface->UnlockRect();
+ aSurface->Unmap();
+
+ for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) {
+ const nsIntRect& iterRect = iter.Get();
+
+ RECT updateRect;
+ updateRect.left = iterRect.x;
+ updateRect.top = iterRect.y;
+ updateRect.right = iterRect.XMost();
+ updateRect.bottom = iterRect.YMost();
+ POINT point = { updateRect.left, updateRect.top };
+
+ mCompositor->device()->UpdateSurface(srcSurface, &updateRect, destSurface, &point);
+ }
+ } else {
+ mIsTiled = true;
+ uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) *
+ GetRequiredTilesD3D9(mSize.height, maxSize);
+ mTileTextures.resize(tileCount);
+ mTexture = nullptr;
+
+ for (uint32_t i = 0; i < tileCount; i++) {
+ IntRect tileRect = GetTileRect(i);
+ unsigned char* data = aSurface->GetData() +
+ tileRect.y * aSurface->Stride() +
+ tileRect.x * bpp;
+ mTileTextures[i] = DataToTexture(deviceManager,
+ data,
+ aSurface->Stride(),
+ IntSize(tileRect.width, tileRect.height),
+ format,
+ bpp);
+ if (!mTileTextures[i]) {
+ NS_WARNING("Could not upload texture");
+ Reset();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static CompositorD3D9* AssertD3D9Compositor(Compositor* aCompositor)
+{
+ CompositorD3D9* compositor = aCompositor ? aCompositor->AsCompositorD3D9()
+ : nullptr;
+ if (!compositor) {
+ // We probably had a device reset and this D3D9 texture was already sent but
+ // we are now falling back to a basic compositor. That can happen if a video
+ // is playing while the device reset occurs and it's not too bad if we miss a
+ // few frames.
+ gfxCriticalNote << "[D3D9] Attempt to set an incompatible compositor";
+ }
+ return compositor;
+}
+
+void
+DataTextureSourceD3D9::SetCompositor(Compositor* aCompositor)
+{
+ CompositorD3D9* d3dCompositor = AssertD3D9Compositor(aCompositor);
+ if (!d3dCompositor) {
+ Reset();
+ return;
+ }
+ if (mCompositor && mCompositor != d3dCompositor) {
+ Reset();
+ }
+ mCompositor = d3dCompositor;
+}
+
+void
+DataTextureSourceD3D9::Reset()
+{
+ mSize.width = 0;
+ mSize.height = 0;
+ mIsTiled = false;
+ mTexture = nullptr;
+ mTileTextures.clear();
+}
+
+IntRect
+DataTextureSourceD3D9::GetTileRect(uint32_t aTileIndex) const
+{
+ uint32_t maxSize = mCompositor->GetMaxTextureSize();
+ uint32_t horizontalTiles = GetRequiredTilesD3D9(mSize.width, maxSize);
+ uint32_t verticalTiles = GetRequiredTilesD3D9(mSize.height, maxSize);
+
+ uint32_t verticalTile = aTileIndex / horizontalTiles;
+ uint32_t horizontalTile = aTileIndex % horizontalTiles;
+
+ return IntRect(horizontalTile * maxSize,
+ verticalTile * maxSize,
+ horizontalTile < (horizontalTiles - 1) ? maxSize : mSize.width % maxSize,
+ verticalTile < (verticalTiles - 1) ? maxSize : mSize.height % maxSize);
+}
+
+IntRect
+DataTextureSourceD3D9::GetTileRect()
+{
+ return GetTileRect(mCurrentTile);
+}
+
+
+D3D9TextureData::D3D9TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture)
+: mTexture(aTexture)
+, mSize(aSize)
+, mFormat(aFormat)
+, mNeedsClear(false)
+, mNeedsClearWhite(false)
+{
+ MOZ_COUNT_CTOR(D3D9TextureData);
+}
+
+D3D9TextureData::~D3D9TextureData()
+{
+ MOZ_COUNT_DTOR(D3D9TextureData);
+}
+
+D3D9TextureData*
+D3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureAllocationFlags aAllocFlags)
+{
+ _D3DFORMAT format = SurfaceFormatToD3D9Format(aFormat);
+ RefPtr<DeviceManagerD3D9> deviceManager = DeviceManagerD3D9::Get();
+ RefPtr<IDirect3DTexture9> d3d9Texture = deviceManager ? deviceManager->CreateTexture(aSize, format,
+ D3DPOOL_SYSTEMMEM,
+ nullptr)
+ : nullptr;
+ if (!d3d9Texture) {
+ NS_WARNING("Could not create a d3d9 texture");
+ return nullptr;
+ }
+ D3D9TextureData* data = new D3D9TextureData(aSize, aFormat, d3d9Texture);
+
+ data->mNeedsClear = aAllocFlags & ALLOC_CLEAR_BUFFER;
+ data->mNeedsClearWhite = aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE;
+
+ return data;
+}
+
+TextureData*
+D3D9TextureData::CreateSimilar(LayersIPCChannel*, LayersBackend, TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
+{
+ return D3D9TextureData::Create(mSize, mFormat, aAllocFlags);
+}
+
+void
+D3D9TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasIntermediateBuffer = true;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+ aInfo.hasSynchronization = false;
+}
+
+bool
+D3D9TextureData::Lock(OpenMode aMode)
+{
+ if (!DeviceManagerD3D9::GetDevice()) {
+ // If the device has failed then we should not lock the surface,
+ // even if we could.
+ mD3D9Surface = nullptr;
+ return false;
+ }
+
+ if (!mD3D9Surface) {
+ HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(mD3D9Surface));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to get texture surface level.");
+ return false;
+ }
+ }
+ return true;
+}
+void
+D3D9TextureData::Unlock()
+{
+ if (mLockRect) {
+ mD3D9Surface->UnlockRect();
+ mLockRect = false;
+ }
+}
+
+bool
+D3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ mTexture->AddRef(); // Release in TextureHostD3D9::TextureHostD3D9
+ aOutDescriptor = SurfaceDescriptorD3D9(reinterpret_cast<uintptr_t>(mTexture.get()));
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+D3D9TextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mD3D9Surface);
+ if (!mD3D9Surface) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt;
+ if (ContentForFormat(mFormat) == gfxContentType::COLOR) {
+ RefPtr<gfxASurface> surface = new gfxWindowsSurface(mD3D9Surface);
+ if (!surface || surface->CairoStatus()) {
+ NS_WARNING("Could not create gfxASurface for d3d9 surface");
+ return nullptr;
+ }
+ dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface, mSize);
+
+ if (!dt) {
+ return nullptr;
+ }
+ } else {
+ // gfxWindowsSurface don't support transparency so we can't use the d3d9
+ // windows surface optimization.
+ // Instead we have to use a gfxImageSurface and fallback for font drawing.
+ D3DLOCKED_RECT rect;
+ HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
+ if (FAILED(hr) || !rect.pBits) {
+ gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 (BDT) " << hexa(hr);
+ return nullptr;
+ }
+ dt = gfxPlatform::CreateDrawTargetForData((uint8_t*)rect.pBits, mSize,
+ rect.Pitch, mFormat);
+ if (!dt) {
+ return nullptr;
+ }
+
+ mLockRect = true;
+ }
+
+ if (mNeedsClear) {
+ dt->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mNeedsClear = false;
+ }
+ if (mNeedsClearWhite) {
+ dt->FillRect(Rect(0, 0, mSize.width, mSize.height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ mNeedsClearWhite = false;
+ }
+
+ return dt.forget();
+}
+
+bool
+D3D9TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ MOZ_ASSERT(mD3D9Surface);
+
+ // gfxWindowsSurface don't support transparency so we can't use the d3d9
+ // windows surface optimization.
+ // Instead we have to use a gfxImageSurface and fallback for font drawing.
+ D3DLOCKED_RECT rect;
+ HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
+ if (FAILED(hr) || !rect.pBits) {
+ gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 (UFS) " << hexa(hr);
+ return false;
+ }
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (D3D9).";
+ mD3D9Surface->UnlockRect();
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (D3D9).";
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy((uint8_t*)rect.pBits + rect.Pitch * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ mD3D9Surface->UnlockRect();
+
+ return true;
+}
+
+DXGID3D9TextureData::DXGID3D9TextureData(gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture, HANDLE aHandle,
+ IDirect3DDevice9* aDevice)
+: mDevice(aDevice)
+, mTexture(aTexture)
+, mFormat(aFormat)
+, mHandle(aHandle)
+{
+ MOZ_COUNT_CTOR(DXGID3D9TextureData);
+}
+
+DXGID3D9TextureData::~DXGID3D9TextureData()
+{
+ gfxWindowsPlatform::sD3D9SharedTextures -= mDesc.Width * mDesc.Height * 4;
+ MOZ_COUNT_DTOR(DXGID3D9TextureData);
+}
+
+// static
+DXGID3D9TextureData*
+DXGID3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags,
+ IDirect3DDevice9* aDevice)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+ MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8A8);
+ if (aFormat != gfx::SurfaceFormat::B8G8R8A8) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HANDLE shareHandle = nullptr;
+ HRESULT hr = aDevice->CreateTexture(aSize.width, aSize.height,
+ 1,
+ D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(texture),
+ &shareHandle);
+ if (FAILED(hr) || !shareHandle) {
+ return nullptr;
+ }
+
+ D3DSURFACE_DESC surfaceDesc;
+ hr = texture->GetLevelDesc(0, &surfaceDesc);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ DXGID3D9TextureData* data = new DXGID3D9TextureData(aFormat, texture, shareHandle, aDevice);
+ data->mDesc = surfaceDesc;
+
+ gfxWindowsPlatform::sD3D9SharedTextures += aSize.width * aSize.height * 4;
+ return data;
+}
+
+void
+DXGID3D9TextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = GetSize();
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+}
+
+already_AddRefed<IDirect3DSurface9>
+DXGID3D9TextureData::GetD3D9Surface() const
+{
+ RefPtr<IDirect3DSurface9> textureSurface;
+ HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(textureSurface));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ return textureSurface.forget();
+}
+
+bool
+DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorD3D10((WindowsHandle)(mHandle), mFormat, GetSize());
+ return true;
+}
+
+
+TextureHostD3D9::TextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorD3D9& aDescriptor)
+ : TextureHost(aFlags)
+ , mFormat(SurfaceFormat::UNKNOWN)
+ , mIsLocked(false)
+{
+ mTexture = reinterpret_cast<IDirect3DTexture9*>(aDescriptor.texture());
+ MOZ_ASSERT(mTexture);
+ mTexture->Release(); // see AddRef in TextureClientD3D9::ToSurfaceDescriptor
+ MOZ_ASSERT(mTexture);
+ D3DSURFACE_DESC desc;
+ HRESULT hr = mTexture->GetLevelDesc(0, &desc);
+ if (!FAILED(hr)) {
+ mFormat = D3D9FormatToSurfaceFormat(desc.Format);
+ mSize.width = desc.Width;
+ mSize.height = desc.Height;
+ }
+}
+
+bool
+DataTextureSourceD3D9::UpdateFromTexture(IDirect3DTexture9* aTexture,
+ const nsIntRegion* aRegion)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+ MOZ_ASSERT(aTexture);
+
+ D3DSURFACE_DESC desc;
+ HRESULT hr = aTexture->GetLevelDesc(0, &desc);
+ if (FAILED(hr)) {
+ return false;
+ } else {
+ // If we changed the compositor, the size might have been reset to zero
+ // Otherwise the texture size must not change.
+ MOZ_ASSERT(mFormat == D3D9FormatToSurfaceFormat(desc.Format));
+ MOZ_ASSERT(!mSize.width || mSize.width == desc.Width);
+ MOZ_ASSERT(!mSize.height || mSize.height == desc.Height);
+ mSize = IntSize(desc.Width, desc.Height);
+ }
+
+ RefPtr<DeviceManagerD3D9> dm = DeviceManagerD3D9::Get();
+ if (!dm || !dm->device()) {
+ return false;
+ }
+
+ if (!mTexture) {
+ mTexture = dm->CreateTexture(mSize, SurfaceFormatToD3D9Format(mFormat),
+ D3DPOOL_DEFAULT, this);
+ if (!mTexture) {
+ NS_WARNING("Failed to create a texture");
+ return false;
+ }
+ }
+
+ RefPtr<IDirect3DSurface9> srcSurface;
+ RefPtr<IDirect3DSurface9> dstSurface;
+
+ hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+ if (FAILED(hr)) {
+ return false;
+ }
+ hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (aRegion) {
+ for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& iterRect = iter.Get();
+ RECT rect;
+ rect.left = iterRect.x;
+ rect.top = iterRect.y;
+ rect.right = iterRect.XMost();
+ rect.bottom = iterRect.YMost();
+
+ POINT point;
+ point.x = iterRect.x;
+ point.y = iterRect.y;
+ hr = dm->device()->UpdateSurface(srcSurface, &rect, dstSurface, &point);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed Update the surface");
+ return false;
+ }
+ }
+ } else {
+ hr = dm->device()->UpdateSurface(srcSurface, nullptr, dstSurface, nullptr);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed Update the surface");
+ return false;
+ }
+ }
+ mIsTiled = false;
+ return true;
+}
+
+void
+TextureHostD3D9::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return;
+ }
+
+ const nsIntRegion* regionToUpdate = aRegion;
+ if (!mTextureSource) {
+ mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor,
+ nullptr, mFlags);
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ // Update the full region the first time for component alpha textures.
+ regionToUpdate = nullptr;
+ }
+ }
+
+ if (!mTextureSource->UpdateFromTexture(mTexture, regionToUpdate)) {
+ gfxCriticalNote << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed";
+ }
+
+ ReadUnlock();
+}
+
+IDirect3DDevice9*
+TextureHostD3D9::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+ return mCompositor ? mCompositor->device() : nullptr;
+}
+
+void
+TextureHostD3D9::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertD3D9Compositor(aCompositor);
+ if (!mCompositor) {
+ mTextureSource = nullptr;
+ return;
+ }
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(aCompositor);
+ }
+}
+
+Compositor*
+TextureHostD3D9::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+TextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ MOZ_ASSERT(mTextureSource);
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+bool
+TextureHostD3D9::Lock()
+{
+ MOZ_ASSERT(!mIsLocked);
+ // XXX - Currently if a TextureHostD3D9 is created but Update is never called,
+ // it will not have a TextureSource although it could since it has a valid
+ // D3D9 texture.
+ mIsLocked = !!mTextureSource;
+ return mIsLocked;
+}
+
+void
+TextureHostD3D9::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+void
+TextureHostD3D9::DeallocateDeviceData()
+{
+ mTextureSource = nullptr;
+}
+
+DXGITextureHostD3D9::DXGITextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor)
+ : TextureHost(aFlags)
+ , mHandle(aDescriptor.handle())
+ , mFormat(aDescriptor.format())
+ , mSize(aDescriptor.size())
+ , mIsLocked(false)
+{
+ MOZ_ASSERT(mHandle);
+ OpenSharedHandle();
+}
+
+IDirect3DDevice9*
+DXGITextureHostD3D9::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+ return mCompositor ? mCompositor->device() : nullptr;
+}
+
+void
+DXGITextureHostD3D9::OpenSharedHandle()
+{
+ MOZ_ASSERT(!mTextureSource);
+
+ if (!GetDevice()) {
+ return;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HRESULT hr = GetDevice()->CreateTexture(mSize.width, mSize.height, 1,
+ D3DUSAGE_RENDERTARGET,
+ SurfaceFormatToD3D9Format(mFormat),
+ D3DPOOL_DEFAULT,
+ getter_AddRefs(texture),
+ (HANDLE*)&mHandle);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture");
+ return;
+ }
+
+ mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor, texture);
+
+ return;
+}
+
+bool
+DXGITextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ MOZ_ASSERT(mTextureSource);
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+bool
+DXGITextureHostD3D9::Lock()
+{
+ MOZ_ASSERT(!mIsLocked);
+
+ if (!mCompositor) {
+ NS_WARNING("no suitable compositor");
+ return false;
+ }
+
+ if (!GetDevice()) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ OpenSharedHandle();
+ }
+ mIsLocked = !!mTextureSource;
+ return mIsLocked;
+}
+
+void
+DXGITextureHostD3D9::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+void
+DXGITextureHostD3D9::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertD3D9Compositor(aCompositor);
+ if (!mCompositor) {
+ mTextureSource = nullptr;
+ }
+}
+
+Compositor*
+DXGITextureHostD3D9::GetCompositor()
+{
+ return mCompositor;
+}
+
+void
+DXGITextureHostD3D9::DeallocateDeviceData()
+{
+ mTextureSource = nullptr;
+}
+
+DXGIYCbCrTextureHostD3D9::DXGIYCbCrTextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor)
+ : TextureHost(aFlags)
+ , mSize(aDescriptor.size())
+ , mSizeY(aDescriptor.sizeY())
+ , mSizeCbCr(aDescriptor.sizeCbCr())
+ , mIsLocked(false)
+{
+ mHandles[0] = reinterpret_cast<HANDLE>(aDescriptor.handleY());
+ mHandles[1] = reinterpret_cast<HANDLE>(aDescriptor.handleCb());
+ mHandles[2] = reinterpret_cast<HANDLE>(aDescriptor.handleCr());
+}
+
+IDirect3DDevice9*
+DXGIYCbCrTextureHostD3D9::GetDevice()
+{
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+ return mCompositor ? mCompositor->device() : nullptr;
+}
+
+void
+DXGIYCbCrTextureHostD3D9::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertD3D9Compositor(aCompositor);
+ if (!mCompositor) {
+ mTextureSources[0] = nullptr;
+ mTextureSources[1] = nullptr;
+ mTextureSources[2] = nullptr;
+ }
+}
+
+Compositor*
+DXGIYCbCrTextureHostD3D9::GetCompositor()
+{
+ return mCompositor;
+}
+
+bool
+DXGIYCbCrTextureHostD3D9::Lock()
+{
+ if (!mCompositor) {
+ NS_WARNING("no suitable compositor");
+ return false;
+ }
+
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+ if (!mTextureSources[0]) {
+ if (!mHandles[0]) {
+ return false;
+ }
+
+ if (FAILED(GetDevice()->CreateTexture(mSizeY.width, mSizeY.height,
+ 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT,
+ getter_AddRefs(mTextures[0]), &mHandles[0]))) {
+ return false;
+ }
+
+ if (FAILED(GetDevice()->CreateTexture(mSizeCbCr.width, mSizeCbCr.height,
+ 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT,
+ getter_AddRefs(mTextures[1]), &mHandles[1]))) {
+ return false;
+ }
+
+ if (FAILED(GetDevice()->CreateTexture(mSizeCbCr.width, mSizeCbCr.height,
+ 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT,
+ getter_AddRefs(mTextures[2]), &mHandles[2]))) {
+ return false;
+ }
+
+ mTextureSources[0] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[0]);
+ mTextureSources[1] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[1]);
+ mTextureSources[2] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[2]);
+ mTextureSources[0]->SetNextSibling(mTextureSources[1]);
+ mTextureSources[1]->SetNextSibling(mTextureSources[2]);
+ }
+
+ mIsLocked = true;
+ return mIsLocked;
+}
+
+void
+DXGIYCbCrTextureHostD3D9::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+bool
+DXGIYCbCrTextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSources[0] && mTextureSources[1] && mTextureSources[2]);
+ aTexture = mTextureSources[0].get();
+ return !!aTexture;
+}
+
+}
+}
diff --git a/gfx/layers/d3d9/TextureD3D9.h b/gfx/layers/d3d9/TextureD3D9.h
new file mode 100644
index 000000000..67d7a13a6
--- /dev/null
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURED3D9_H
+#define MOZILLA_GFX_TEXTURED3D9_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "gfxWindowsPlatform.h"
+#include "d3d9.h"
+#include <vector>
+#include "DeviceManagerD3D9.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorD3D9;
+
+class TextureSourceD3D9
+{
+ friend class DeviceManagerD3D9;
+
+public:
+ TextureSourceD3D9()
+ : mPreviousHost(nullptr)
+ , mNextHost(nullptr)
+ , mCreatingDeviceManager(nullptr)
+ {}
+ virtual ~TextureSourceD3D9();
+
+ virtual IDirect3DTexture9* GetD3D9Texture() { return mTexture; }
+
+ StereoMode GetStereoMode() const { return mStereoMode; };
+
+ // Release all texture memory resources held by the texture host.
+ virtual void ReleaseTextureResources()
+ {
+ mTexture = nullptr;
+ }
+
+protected:
+ virtual gfx::IntSize GetSize() const { return mSize; }
+ void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }
+
+ // Helper methods for creating and copying textures.
+ already_AddRefed<IDirect3DTexture9> InitTextures(
+ DeviceManagerD3D9* aDeviceManager,
+ const gfx::IntSize &aSize,
+ _D3DFORMAT aFormat,
+ RefPtr<IDirect3DSurface9>& aSurface,
+ D3DLOCKED_RECT& aLockedRect);
+
+ already_AddRefed<IDirect3DTexture9> DataToTexture(
+ DeviceManagerD3D9* aDeviceManager,
+ unsigned char *aData,
+ int aStride,
+ const gfx::IntSize &aSize,
+ _D3DFORMAT aFormat,
+ uint32_t aBPP);
+
+ // aTexture should be in SYSTEMMEM, returns a texture in the default
+ // pool (that is, in video memory).
+ already_AddRefed<IDirect3DTexture9> TextureToTexture(
+ DeviceManagerD3D9* aDeviceManager,
+ IDirect3DTexture9* aTexture,
+ const gfx::IntSize& aSize,
+ _D3DFORMAT aFormat);
+
+ gfx::IntSize mSize;
+
+ // Linked list of all objects holding d3d9 textures.
+ TextureSourceD3D9* mPreviousHost;
+ TextureSourceD3D9* mNextHost;
+ // The device manager that created our textures.
+ RefPtr<DeviceManagerD3D9> mCreatingDeviceManager;
+
+ StereoMode mStereoMode;
+ RefPtr<IDirect3DTexture9> mTexture;
+};
+
+/**
+ * A TextureSource that implements the DataTextureSource interface.
+ * it can be used without a TextureHost and is able to upload texture data
+ * from a gfx::DataSourceSurface.
+ */
+class DataTextureSourceD3D9 : public DataTextureSource
+ , public TextureSourceD3D9
+ , public BigImageIterator
+{
+public:
+ /// Constructor allowing the texture to perform texture uploads.
+ ///
+ /// The texture can be used as an actual DataTextureSource.
+ DataTextureSourceD3D9(gfx::SurfaceFormat aFormat,
+ CompositorD3D9* aCompositor,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ StereoMode aStereoMode = StereoMode::MONO);
+
+ /// Constructor for textures created around DXGI shared handles, disallowing
+ /// texture uploads.
+ ///
+ /// The texture CANNOT be used as a DataTextureSource.
+ DataTextureSourceD3D9(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ CompositorD3D9* aCompositor,
+ IDirect3DTexture9* aTexture,
+ TextureFlags aFlags = TextureFlags::DEFAULT);
+
+ virtual ~DataTextureSourceD3D9();
+
+ virtual const char* Name() const override { return "DataTextureSourceD3D9"; }
+
+ // DataTextureSource
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ // TextureSource
+
+ virtual TextureSourceD3D9* AsSourceD3D9() override { return this; }
+
+ virtual IDirect3DTexture9* GetD3D9Texture() override;
+
+ // Returns nullptr if this texture was created by a DXGI TextureHost.
+ virtual DataTextureSource* AsDataTextureSource() override { return mAllowTextureUploads ? this : nullptr; }
+
+ virtual void DeallocateDeviceData() override { mTexture = nullptr; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ // BigImageIterator
+
+ virtual BigImageIterator* AsBigImageIterator() override { return mIsTiled ? this : nullptr; }
+
+ virtual size_t GetTileCount() override { return mTileTextures.size(); }
+
+ virtual bool NextTile() override { return (++mCurrentTile < mTileTextures.size()); }
+
+ virtual gfx::IntRect GetTileRect() override;
+
+ virtual void EndBigImageIteration() override { mIterating = false; }
+
+ virtual void BeginBigImageIteration() override
+ {
+ mIterating = true;
+ mCurrentTile = 0;
+ }
+
+ /**
+ * Copy the content of aTexture using the GPU.
+ */
+ bool UpdateFromTexture(IDirect3DTexture9* aTexture, const nsIntRegion* aRegion);
+
+protected:
+ gfx::IntRect GetTileRect(uint32_t aTileIndex) const;
+
+ void Reset();
+
+ std::vector< RefPtr<IDirect3DTexture9> > mTileTextures;
+ RefPtr<CompositorD3D9> mCompositor;
+ gfx::SurfaceFormat mFormat;
+ uint32_t mCurrentTile;
+ TextureFlags mFlags;
+ bool mIsTiled;
+ bool mIterating;
+ bool mAllowTextureUploads;
+};
+
+/**
+ * Needs a D3D9 context on the client side.
+ * The corresponding TextureHost is TextureHostD3D9.
+ */
+class D3D9TextureData : public TextureData
+{
+public:
+ ~D3D9TextureData();
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const override;
+
+ static D3D9TextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override {}
+
+protected:
+ D3D9TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture);
+
+ RefPtr<IDirect3DTexture9> mTexture;
+ RefPtr<IDirect3DSurface9> mD3D9Surface;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ bool mNeedsClear;
+ bool mNeedsClearWhite;
+ bool mLockRect;
+};
+
+/**
+ * Wraps a D3D9 texture, shared with the compositor though DXGI.
+ * At the moment it is only used with D3D11 compositing, and the corresponding
+ * TextureHost is DXGITextureHostD3D11.
+ */
+class DXGID3D9TextureData : public TextureData
+{
+public:
+ static DXGID3D9TextureData*
+ Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureFlags aFlags, IDirect3DDevice9* aDevice);
+
+ ~DXGID3D9TextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override {}
+
+ IDirect3DDevice9* GetD3D9Device() { return mDevice; }
+ IDirect3DTexture9* GetD3D9Texture() { return mTexture; }
+ HANDLE GetShareHandle() const { return mHandle; }
+ already_AddRefed<IDirect3DSurface9> GetD3D9Surface() const;
+
+ const D3DSURFACE_DESC& GetDesc() const
+ {
+ return mDesc;
+ }
+
+ gfx::IntSize GetSize() const { return gfx::IntSize(mDesc.Width, mDesc.Height); }
+
+protected:
+ DXGID3D9TextureData(gfx::SurfaceFormat aFormat,
+ IDirect3DTexture9* aTexture, HANDLE aHandle,
+ IDirect3DDevice9* aDevice);
+
+ RefPtr<IDirect3DDevice9> mDevice;
+ RefPtr<IDirect3DTexture9> mTexture;
+ gfx::SurfaceFormat mFormat;
+ HANDLE mHandle;
+ D3DSURFACE_DESC mDesc;
+};
+
+class TextureHostD3D9 : public TextureHost
+{
+public:
+ TextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorD3D9& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+ virtual bool HasIntermediateBuffer() const override { return true; }
+
+protected:
+ TextureHostD3D9(TextureFlags aFlags);
+ IDirect3DDevice9* GetDevice();
+
+ virtual void UpdatedInternal(const nsIntRegion* aRegion) override;
+
+ RefPtr<DataTextureSourceD3D9> mTextureSource;
+ RefPtr<IDirect3DTexture9> mTexture;
+ RefPtr<CompositorD3D9> mCompositor;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ bool mIsLocked;
+};
+
+class DXGITextureHostD3D9 : public TextureHost
+{
+public:
+ DXGITextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorD3D10& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // TODO: cf bug 872568
+ }
+
+protected:
+ void OpenSharedHandle();
+ IDirect3DDevice9* GetDevice();
+
+ RefPtr<DataTextureSourceD3D9> mTextureSource;
+ RefPtr<CompositorD3D9> mCompositor;
+ WindowsHandle mHandle;
+ gfx::SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ bool mIsLocked;
+};
+
+class DXGIYCbCrTextureHostD3D9 : public TextureHost
+{
+public:
+ DXGIYCbCrTextureHostD3D9(TextureFlags aFlags,
+ const SurfaceDescriptorDXGIYCbCr& aDescriptor);
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return gfx::SurfaceFormat::YUV; }
+
+ // Bug 1305906 fixes YUVColorSpace handling
+ virtual YUVColorSpace GetYUVColorSpace() const override { return YUVColorSpace::BT601; }
+
+ virtual bool Lock() override;
+ virtual void Unlock() override;
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr;
+ }
+
+ protected:
+ IDirect3DDevice9* GetDevice();
+
+ HANDLE mHandles[3];
+ RefPtr<IDirect3DTexture9> mTextures[3];
+ RefPtr<DataTextureSourceD3D9> mTextureSources[3];
+
+ RefPtr<CompositorD3D9> mCompositor;
+ gfx::IntSize mSize;
+ gfx::IntSize mSizeY;
+ gfx::IntSize mSizeCbCr;
+ bool mIsLocked;
+ };
+
+class CompositingRenderTargetD3D9 : public CompositingRenderTarget,
+ public TextureSourceD3D9
+{
+public:
+ CompositingRenderTargetD3D9(IDirect3DTexture9* aTexture,
+ SurfaceInitMode aInit,
+ const gfx::IntRect& aRect);
+ // use for rendering to the main window, cannot be rendered as a texture
+ CompositingRenderTargetD3D9(IDirect3DSurface9* aSurface,
+ SurfaceInitMode aInit,
+ const gfx::IntRect& aRect);
+ virtual ~CompositingRenderTargetD3D9();
+
+ virtual const char* Name() const override { return "CompositingRenderTargetD3D9"; }
+
+ virtual TextureSourceD3D9* AsSourceD3D9() override
+ {
+ MOZ_ASSERT(mTexture,
+ "No texture, can't be indirectly rendered. Is this the screen backbuffer?");
+ return this;
+ }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ void BindRenderTarget(IDirect3DDevice9* aDevice);
+
+ IDirect3DSurface9* GetD3D9Surface() const { return mSurface; }
+
+private:
+ friend class CompositorD3D9;
+
+ RefPtr<IDirect3DSurface9> mSurface;
+ SurfaceInitMode mInitMode;
+};
+
+}
+}
+
+#endif /* MOZILLA_GFX_TEXTURED3D9_H */
diff --git a/gfx/layers/d3d9/genshaders.sh b/gfx/layers/d3d9/genshaders.sh
new file mode 100644
index 000000000..f85762450
--- /dev/null
+++ b/gfx/layers/d3d9/genshaders.sh
@@ -0,0 +1,35 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+tempfile=tmpShaderHeader
+rm LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ELayerQuadVS -nologo -Fh$tempfile -VnLayerQuadVS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ERGBAShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBAShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass1ShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass2ShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EYCbCrShader -nologo -Tps_2_0 -Fh$tempfile -VnYCbCrShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ESolidColorShader -nologo -Tps_2_0 -Fh$tempfile -VnSolidColorShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ELayerQuadVSMask -nologo -Fh$tempfile -VnLayerQuadVSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ERGBAShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnRGBAShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1ShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass1ShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2ShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass2ShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ERGBShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnRGBShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EYCbCrShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnYCbCrShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -ESolidColorShaderMask -nologo -Tps_2_0 -Fh$tempfile -VnSolidColorShaderPSMask
+cat $tempfile >> LayerManagerD3D9Shaders.h
+rm $tempfile
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp
new file mode 100644
index 000000000..f5971dd38
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerChild.h"
+
+#include "InputData.h" // for InputData
+#include "mozilla/dom/TabParent.h" // for TabParent
+#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper
+#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerChild::APZCTreeManagerChild()
+ : mCompositorSession(nullptr)
+{
+}
+
+void
+APZCTreeManagerChild::SetCompositorSession(RemoteCompositorSession* aSession)
+{
+ // Exactly one of mCompositorSession and aSession must be null (i.e. either
+ // we're setting mCompositorSession or we're clearing it).
+ MOZ_ASSERT(!mCompositorSession ^ !aSession);
+ mCompositorSession = aSession;
+}
+
+nsEventStatus
+APZCTreeManagerChild::ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ MultiTouchInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveMultiTouchInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case MOUSE_INPUT: {
+ MouseInput& event = aEvent.AsMouseInput();
+ MouseInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveMouseInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput& event = aEvent.AsPanGestureInput();
+ PanGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceivePanGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput& event = aEvent.AsPinchGestureInput();
+ PinchGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceivePinchGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput& event = aEvent.AsTapGestureInput();
+ TapGestureInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveTapGestureInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ ScrollWheelInput processedEvent;
+
+ nsEventStatus res;
+ SendReceiveScrollWheelInputEvent(event,
+ &res,
+ &processedEvent,
+ aOutTargetGuid,
+ aOutInputBlockId);
+
+ event = processedEvent;
+ return res;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
+ return nsEventStatus_eConsumeNoDefault;
+ }
+ }
+}
+
+void
+APZCTreeManagerChild::ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags)
+{
+ SendZoomToRect(aGuid, aRect, aFlags);
+}
+
+void
+APZCTreeManagerChild::ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault)
+{
+ SendContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void
+APZCTreeManagerChild::SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ SendSetTargetAPZC(aInputBlockId, aTargets);
+}
+
+void
+APZCTreeManagerChild::UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ SendUpdateZoomConstraints(aGuid, aConstraints);
+}
+
+void
+APZCTreeManagerChild::CancelAnimation(const ScrollableLayerGuid &aGuid)
+{
+ SendCancelAnimation(aGuid);
+}
+
+void
+APZCTreeManagerChild::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ SendAdjustScrollForSurfaceShift(aShift);
+}
+
+void
+APZCTreeManagerChild::SetDPI(float aDpiValue)
+{
+ SendSetDPI(aDpiValue);
+}
+
+void
+APZCTreeManagerChild::SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues)
+{
+ SendSetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void
+APZCTreeManagerChild::StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ SendStartScrollbarDrag(aGuid, aDragMetrics);
+}
+
+void
+APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled)
+{
+ SendSetLongTapEnabled(aTapGestureEnabled);
+}
+
+void
+APZCTreeManagerChild::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
+{
+ SendProcessTouchVelocity(aTimestampMs, aSpeedY);
+}
+
+void
+APZCTreeManagerChild::UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage)
+{
+ SendUpdateWheelTransaction(aRefPoint, aEventMessage);
+}
+
+void APZCTreeManagerChild::TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ SendTransformEventRefPoint(*aRefPoint, aRefPoint, aOutTargetGuid);
+}
+
+bool
+APZCTreeManagerChild::RecvHandleTap(const TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mCompositorSession &&
+ mCompositorSession->RootLayerTreeId() == aGuid.mLayersId &&
+ mCompositorSession->GetContentController()) {
+ mCompositorSession->GetContentController()->HandleTap(aType, aPoint,
+ aModifiers, aGuid, aInputBlockId);
+ return true;
+ }
+ dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+ return true;
+}
+
+bool
+APZCTreeManagerChild::RecvNotifyPinchGesture(const PinchGestureType& aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers)
+{
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We want to handle it in this process regardless of what the target guid
+ // of the pinch is. This may change in the future.
+ if (mCompositorSession &&
+ mCompositorSession->GetWidget()) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mCompositorSession->GetWidget());
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h
new file mode 100644
index 000000000..3e7a2f260
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerChild_h
+#define mozilla_layers_APZCTreeManagerChild_h
+
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/PAPZCTreeManagerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class RemoteCompositorSession;
+
+class APZCTreeManagerChild
+ : public IAPZCTreeManager
+ , public PAPZCTreeManagerChild
+{
+public:
+ APZCTreeManagerChild();
+
+ void SetCompositorSession(RemoteCompositorSession* aSession);
+
+ nsEventStatus
+ ReceiveInputEvent(
+ InputData& aEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ void
+ ZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ void
+ ContentReceivedInputBlock(
+ uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ void
+ SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ void
+ UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ void
+ CancelAnimation(const ScrollableLayerGuid &aGuid) override;
+
+ void
+ AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ void
+ SetDPI(float aDpiValue) override;
+
+ void
+ SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ void
+ StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ void
+ SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ void
+ ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
+
+ void
+ TransformEventRefPoint(
+ LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void
+ UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) override;
+
+protected:
+ bool RecvHandleTap(const TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ bool RecvNotifyPinchGesture(const PinchGestureType& aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers) override;
+
+ virtual
+ ~APZCTreeManagerChild() { }
+
+private:
+ MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerChild_h
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.cpp b/gfx/layers/ipc/APZCTreeManagerParent.cpp
new file mode 100644
index 000000000..33cd6ffff
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerParent.h"
+
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerParent::APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager)
+ : mLayersId(aLayersId)
+ , mTreeManager(aAPZCTreeManager)
+{
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+}
+
+APZCTreeManagerParent::~APZCTreeManagerParent()
+{
+}
+
+void
+APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager)
+{
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+ mTreeManager = aAPZCTreeManager;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MultiTouchInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ MultiTouchInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MouseInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ MouseInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PanGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ PanGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PinchGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ PinchGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ TapGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ TapGestureInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent,
+ nsEventStatus* aOutStatus,
+ ScrollWheelInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId)
+{
+ ScrollWheelInput event = aEvent;
+
+ *aOutStatus = mTreeManager->ReceiveInputEvent(
+ event,
+ aOutTargetGuid,
+ aOutInputBlockId);
+ *aOutEvent = event;
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t& aFlags)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
+ return false;
+ }
+
+ mTreeManager->ZoomToRect(aGuid, aRect, aFlags);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId,
+ const bool& aPreventDefault)
+{
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableMethod<uint64_t, bool>(mTreeManager,
+ &IAPZCTreeManager::ContentReceivedInputBlock,
+ aInputBlockId,
+ aPreventDefault));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets)
+{
+ for (size_t i = 0; i < aTargets.Length(); i++) {
+ if (aTargets[i].mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvSetTargetAPZC; dropping message...");
+ return false;
+ }
+ }
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod
+ <uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>
+ (mTreeManager, &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvUpdateZoomConstraints; dropping message...");
+ return false;
+ }
+
+ mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvCancelAnimation(const ScrollableLayerGuid& aGuid)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvCancelAnimation; dropping message...");
+ return false;
+ }
+
+ mTreeManager->CancelAnimation(aGuid);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift)
+{
+ mTreeManager->AdjustScrollForSurfaceShift(aShift);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue)
+{
+ mTreeManager->SetDPI(aDpiValue);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId,
+ nsTArray<TouchBehaviorFlags>&& aValues)
+{
+ APZThreadUtils::RunOnControllerThread(NewRunnableMethod
+ <uint64_t,
+ StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>
+ (mTreeManager,
+ &IAPZCTreeManager::SetAllowedTouchBehavior,
+ aInputBlockId, Move(aValues)));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics)
+{
+ if (aGuid.mLayersId != mLayersId) {
+ // Guard against bad data from hijacked child processes
+ NS_ERROR("Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
+ return false;
+ }
+
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+ mTreeManager,
+ &IAPZCTreeManager::StartScrollbarDrag,
+ aGuid, aDragMetrics));
+
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aTapGestureEnabled)
+{
+ mTreeManager->SetLongTapEnabled(aTapGestureEnabled);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvProcessTouchVelocity(
+ const uint32_t& aTimestampMs,
+ const float& aSpeedY)
+{
+ mTreeManager->ProcessTouchVelocity(aTimestampMs, aSpeedY);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint,
+ const EventMessage& aEventMessage)
+{
+ mTreeManager->UpdateWheelTransaction(aRefPoint, aEventMessage);
+ return true;
+}
+
+bool
+APZCTreeManagerParent::RecvTransformEventRefPoint(
+ const LayoutDeviceIntPoint& aRefPoint,
+ LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid)
+{
+ LayoutDeviceIntPoint refPoint = aRefPoint;
+ mTreeManager->TransformEventRefPoint(&refPoint, aOutTargetGuid);
+ *aOutRefPoint = refPoint;
+
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.h b/gfx/layers/ipc/APZCTreeManagerParent.h
new file mode 100644
index 000000000..f2d3aec57
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerParent_h
+#define mozilla_layers_APZCTreeManagerParent_h
+
+#include "mozilla/layers/PAPZCTreeManagerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZCTreeManager;
+
+class APZCTreeManagerParent
+ : public PAPZCTreeManagerParent
+{
+public:
+
+ explicit APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager);
+ virtual ~APZCTreeManagerParent();
+
+ uint64_t LayersId() const { return mLayersId; }
+
+ /**
+ * Called when the layer tree that this protocol is connected to
+ * is adopted by another compositor, and we need to switch APZCTreeManagers.
+ */
+ void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager);
+
+ bool
+ RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MultiTouchInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent,
+ nsEventStatus* aOutStatus,
+ MouseInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PanGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ PinchGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent,
+ nsEventStatus* aOutStatus,
+ TapGestureInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent,
+ nsEventStatus* aOutStatus,
+ ScrollWheelInput* aOutEvent,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId) override;
+
+ bool
+ RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t& aFlags) override;
+
+ bool
+ RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId,
+ const bool& aPreventDefault) override;
+
+ bool
+ RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ bool
+ RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints) override;
+
+ bool
+ RecvCancelAnimation(const ScrollableLayerGuid& aGuid) override;
+
+ bool
+ RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
+
+ bool
+ RecvSetDPI(const float& aDpiValue) override;
+
+ bool
+ RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId,
+ nsTArray<TouchBehaviorFlags>&& aValues) override;
+
+ bool
+ RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ bool
+ RecvSetLongTapEnabled(const bool& aTapGestureEnabled) override;
+
+ bool
+ RecvProcessTouchVelocity(
+ const uint32_t& aTimestampMs,
+ const float& aSpeedY) override;
+
+ bool
+ RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint,
+ const EventMessage& aEventMessage) override;
+
+ bool
+ RecvTransformEventRefPoint(
+ const LayoutDeviceIntPoint& aRefPoint,
+ LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid) override;
+
+ void
+ ActorDestroy(ActorDestroyReason aWhy) override { }
+
+private:
+ uint64_t mLayersId;
+ RefPtr<APZCTreeManager> mTreeManager;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerParent_h
diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp
new file mode 100644
index 000000000..2dd24d4cd
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/GeckoContentController.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+APZChild::APZChild(RefPtr<GeckoContentController> aController)
+ : mController(aController)
+{
+ MOZ_ASSERT(mController);
+}
+
+APZChild::~APZChild()
+{
+ if (mController) {
+ mController->Destroy();
+ mController = nullptr;
+ }
+}
+
+bool
+APZChild::RecvRequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->RequestContentRepaint(aFrameMetrics);
+ return true;
+}
+
+bool
+APZChild::RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent)
+{
+ mController->UpdateOverscrollVelocity(aX, aY, aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent)
+{
+ mController->UpdateOverscrollOffset(aX, aY, aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvSetScrollingRootContent(const bool& aIsRootContent)
+{
+ mController->SetScrollingRootContent(aIsRootContent);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ mController->NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ const APZStateChange& aChange,
+ const int& aArg)
+{
+ mController->NotifyAPZStateChange(aGuid, aChange, aArg);
+ return true;
+}
+
+bool
+APZChild::RecvNotifyFlushComplete()
+{
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->NotifyFlushComplete();
+ return true;
+}
+
+bool
+APZChild::RecvDestroy()
+{
+ // mController->Destroy will be called in the destructor
+ PAPZChild::Send__delete__(this);
+ return true;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h
new file mode 100644
index 000000000..57865ee6d
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZChild_h
+#define mozilla_layers_APZChild_h
+
+#include "mozilla/layers/PAPZChild.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class GeckoContentController;
+
+/**
+ * APZChild implements PAPZChild and is used to remote a GeckoContentController
+ * that lives in a different process than where APZ lives.
+ */
+class APZChild final : public PAPZChild
+{
+public:
+ explicit APZChild(RefPtr<GeckoContentController> aController);
+ ~APZChild();
+
+ bool RecvRequestContentRepaint(const FrameMetrics& frame) override;
+
+ bool RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent) override;
+
+ bool RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent) override;
+
+ bool RecvSetScrollingRootContent(const bool& aIsRootContent) override;
+
+ bool RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ bool RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ const APZStateChange& aChange,
+ const int& aArg) override;
+
+ bool RecvNotifyFlushComplete() override;
+
+ bool RecvDestroy() override;
+
+private:
+ RefPtr<GeckoContentController> mController;
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_APZChild_h
diff --git a/gfx/layers/ipc/CompositableForwarder.cpp b/gfx/layers/ipc/CompositableForwarder.cpp
new file mode 100644
index 000000000..20f2f61ae
--- /dev/null
+++ b/gfx/layers/ipc/CompositableForwarder.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableForwarder.h"
+#include "mozilla/layers/CompositableChild.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+CompositableForwarder::Destroy(CompositableChild* aCompositable)
+{
+ AssertInForwarderThread();
+
+ if (!aCompositable->CanSend()) {
+ return;
+ }
+
+ if (!DestroyInTransaction(aCompositable, false)) {
+ aCompositable->SendDestroy();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h
new file mode 100644
index 000000000..b31754b5a
--- /dev/null
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+#define MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Rect.h"
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ImageContainer;
+class SurfaceDescriptor;
+class SurfaceDescriptorTiles;
+class ThebesBufferData;
+class PTextureChild;
+
+/**
+ * A transaction is a set of changes that happenned on the content side, that
+ * should be sent to the compositor side.
+ * CompositableForwarder is an interface to manage a transaction of
+ * compositable objetcs.
+ *
+ * ShadowLayerForwarder is an example of a CompositableForwarder (that can
+ * additionally forward modifications of the Layer tree).
+ * ImageBridgeChild is another CompositableForwarder.
+ *
+ * CompositableForwarder implements KnowsCompositor for simplicity as all
+ * implementations of CompositableForwarder currently also implement KnowsCompositor.
+ * This dependency could be split if we add new use cases.
+ */
+class CompositableForwarder : public KnowsCompositor
+{
+public:
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer = nullptr) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side what TiledLayerBuffer to
+ * use for the next composition.
+ */
+ virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by
+ * aCompositable and aIdentifier has been updated to aThebesBuffer.
+ */
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) = 0;
+
+ virtual void Destroy(CompositableChild* aCompositable);
+
+ virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0;
+ virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side to remove the texture
+ * from the CompositableHost.
+ * This function does not delete the TextureHost corresponding to the
+ * TextureClient passed in parameter.
+ * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
+ * the transaction becomes synchronous.
+ */
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) = 0;
+
+ struct TimedTextureClient {
+ TimedTextureClient()
+ : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
+
+ TextureClient* mTextureClient;
+ TimeStamp mTimeStamp;
+ nsIntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ /**
+ * Tell the CompositableHost on the compositor side what textures to use for
+ * the next composition.
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) = 0;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) = 0;
+
+ virtual void UpdateFwdTransactionId() = 0;
+ virtual uint64_t GetFwdTransactionId() = 0;
+
+ virtual bool InForwarderThread() = 0;
+
+ void AssertInForwarderThread() {
+ MOZ_ASSERT(InForwarderThread());
+ }
+
+protected:
+ nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
+ nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositableTransactionParent.cpp b/gfx/layers/ipc/CompositableTransactionParent.cpp
new file mode 100644
index 000000000..135011101
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableTransactionParent.h"
+#include "CompositableHost.h" // for CompositableParent, etc
+#include "CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "GLContext.h" // for GLContext
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ContentHost.h" // for ContentHostBase
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/TiledContentHost.h"
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledLayerBuffer;
+class Compositor;
+
+// This function can in some cases fail and return false without it being a bug.
+// This can theoretically happen if the ImageBridge sends frames before
+// we created the layer tree. Since we can't enforce that the layer
+// tree is already created before ImageBridge operates, there isn't much
+// we can do about it, but in practice it is very rare.
+// Typically when a tab with a video is dragged from a window to another,
+// there can be a short time when the video is still sending frames
+// asynchonously while the layer tree is not reconstructed. It's not a
+// big deal.
+// Note that Layers transactions do not need to call this because they always
+// schedule the composition, in LayerManagerComposite::EndTransaction.
+static bool
+ScheduleComposition(CompositableHost* aCompositable)
+{
+ uint64_t id = aCompositable->GetCompositorID();
+ if (!id) {
+ return false;
+ }
+ CompositorBridgeParent* cp = CompositorBridgeParent::GetCompositorBridgeParent(id);
+ if (!cp) {
+ return false;
+ }
+ cp->ScheduleComposition();
+ return true;
+}
+
+bool
+CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation& aEdit,
+ EditReplyVector& replyv)
+{
+ // Ignore all operations on compositables created on stale compositors. We
+ // return true because the child is unable to handle errors.
+ CompositableHost* compositable = CompositableHost::FromIPDLActor(aEdit.compositableParent());
+ if (compositable->GetCompositor() && !compositable->GetCompositor()->IsValid()) {
+ return true;
+ }
+
+ switch (aEdit.detail().type()) {
+ case CompositableOperationDetail::TOpPaintTextureRegion: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
+
+ const OpPaintTextureRegion& op = aEdit.detail().get_OpPaintTextureRegion();
+ Layer* layer = compositable->GetLayer();
+ if (!layer || layer->GetType() != Layer::TYPE_PAINTED) {
+ return false;
+ }
+ PaintedLayerComposite* thebes = static_cast<PaintedLayerComposite*>(layer);
+
+ const ThebesBufferData& bufferData = op.bufferData();
+
+ RenderTraceInvalidateStart(thebes, "FF00FF", op.updatedRegion().GetBounds());
+
+ nsIntRegion frontUpdatedRegion;
+ if (!compositable->UpdateThebes(bufferData,
+ op.updatedRegion(),
+ thebes->GetValidRegion(),
+ &frontUpdatedRegion))
+ {
+ return false;
+ }
+ replyv.push_back(
+ OpContentBufferSwap(aEdit.compositableParent(), nullptr, frontUpdatedRegion));
+
+ RenderTraceInvalidateEnd(thebes, "FF00FF");
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTiledLayerBuffer: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
+ const OpUseTiledLayerBuffer& op = aEdit.detail().get_OpUseTiledLayerBuffer();
+ TiledContentHost* tiledHost = compositable->AsTiledContentHost();
+
+ NS_ASSERTION(tiledHost, "The compositable is not tiled");
+
+ const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
+
+ bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc);
+
+ const InfallibleTArray<TileDescriptor>& tileDescriptors = tileDesc.tiles();
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ continue;
+ }
+ const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(texturedDesc.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+ texture = TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+ if (!success) {
+ return false;
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpRemoveTexture: {
+ const OpRemoveTexture& op = aEdit.detail().get_OpRemoveTexture();
+
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
+
+ MOZ_ASSERT(tex.get());
+ compositable->RemoveTextureHost(tex);
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTexture: {
+ const OpUseTexture& op = aEdit.detail().get_OpUseTexture();
+
+ AutoTArray<CompositableHost::TimedTexture,4> textures;
+ for (auto& timedTexture : op.textures()) {
+ CompositableHost::TimedTexture* t = textures.AppendElement();
+ t->mTexture =
+ TextureHost::AsTextureHost(timedTexture.textureParent());
+ MOZ_ASSERT(t->mTexture);
+ t->mTimeStamp = timedTexture.timeStamp();
+ t->mPictureRect = timedTexture.picture();
+ t->mFrameID = timedTexture.frameID();
+ t->mProducerID = timedTexture.producerID();
+ t->mTexture->DeserializeReadLock(timedTexture.sharedLock(), this);
+ }
+ if (textures.Length() > 0) {
+ compositable->UseTextureHost(textures);
+
+ for (auto& timedTexture : op.textures()) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(timedTexture.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+
+ if (UsesImageBridge() && compositable->GetLayer()) {
+ ScheduleComposition(compositable);
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpUseComponentAlphaTextures: {
+ const OpUseComponentAlphaTextures& op = aEdit.detail().get_OpUseComponentAlphaTextures();
+ RefPtr<TextureHost> texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent());
+ RefPtr<TextureHost> texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent());
+ texOnBlack->DeserializeReadLock(op.sharedLockBlack(), this);
+ texOnWhite->DeserializeReadLock(op.sharedLockWhite(), this);
+
+ MOZ_ASSERT(texOnBlack && texOnWhite);
+ compositable->UseComponentAlphaTextures(texOnBlack, texOnWhite);
+
+ if (texOnBlack) {
+ texOnBlack->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnBlack->NumCompositableRefs() > 0);
+ }
+
+ if (texOnWhite) {
+ texOnWhite->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnWhite->NumCompositableRefs() > 0);
+ }
+
+ if (UsesImageBridge()) {
+ ScheduleComposition(compositable);
+ }
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "bad type");
+ }
+ }
+
+ return true;
+}
+
+void
+CompositableParentManager::DestroyActor(const OpDestroy& aOp)
+{
+ switch (aOp.type()) {
+ case OpDestroy::TPTextureParent: {
+ auto actor = aOp.get_PTextureParent();
+ TextureHost::ReceivedDestroy(actor);
+ break;
+ }
+ case OpDestroy::TPCompositableParent: {
+ auto actor = aOp.get_PCompositableParent();
+ CompositableHost::ReceivedDestroy(actor);
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "unsupported type");
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h
new file mode 100644
index 000000000..ca676c115
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+
+#include <vector> // for vector
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
+
+// Since PCompositble has two potential manager protocols, we can't just call
+// the Manager() method usually generated when there's one manager protocol,
+// so both manager protocols implement this and we keep a reference to them
+// through this interface.
+class CompositableParentManager : public HostIPCAllocator
+{
+public:
+ CompositableParentManager() {}
+
+ void DestroyActor(const OpDestroy& aOp);
+
+ void UpdateFwdTransactionId(uint64_t aTransactionId)
+ {
+ MOZ_ASSERT(mFwdTransactionId < aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+protected:
+ /**
+ * Handle the IPDL messages that affect PCompositable actors.
+ */
+ bool ReceiveCompositableUpdate(const CompositableOperation& aEdit,
+ EditReplyVector& replyv);
+
+ uint64_t mFwdTransactionId = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp
new file mode 100644
index 000000000..945adafc1
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorBench.h"
+
+#ifdef MOZ_COMPOSITOR_BENCH
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/TimeStamp.h"
+#include "gfxPrefs.h"
+#include <math.h>
+#include "GeckoProfiler.h"
+
+#define TEST_STEPS 1000
+#define DURATION_THRESHOLD 30
+#define THRESHOLD_ABORT_COUNT 5
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static float SimplePseudoRandom(int aStep, int aCount) {
+ srand(aStep * 1000 + aCount);
+ return static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
+}
+
+class BenchTest {
+public:
+ BenchTest(const char* aTestName)
+ : mTestName(aTestName)
+ {}
+
+ virtual ~BenchTest() {}
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {}
+ virtual void Teardown(Compositor* aCompositor) {}
+ virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) = 0;
+
+ const char* ToString() { return mTestName; }
+private:
+ const char* mTestName;
+};
+
+static void
+DrawFrameTrivialQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+ (int)(i / aScreenRect.height),
+ 1, 1);
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+static void
+DrawFrameStressQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects)
+{
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+ aScreenRect.height * SimplePseudoRandom(i, 1),
+ aScreenRect.width * SimplePseudoRandom(i, 2),
+ aScreenRect.height * SimplePseudoRandom(i, 3));
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+ transform2d = transform2d.PreRotate(SimplePseudoRandom(i, 4) * 70.f);
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+class EffectSolidColorBench : public BenchTest {
+public:
+ EffectSolidColorBench()
+ : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ float tmp;
+ float red = modff(aStep * 0.03f, &tmp);
+ EffectChain effects;
+ effects.mPrimaryEffect =
+ new EffectSolidColor(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+
+ const gfx::Rect& rect = aScreenRect;
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix4x4 transform;
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+};
+
+class EffectSolidColorTrivialBench : public BenchTest {
+public:
+ EffectSolidColorTrivialBench()
+ : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class EffectSolidColorStressBench : public BenchTest {
+public:
+ EffectSolidColorStressBench()
+ : BenchTest("EffectSolidColorStressBench (10s various EffectSolidColor)")
+ {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(gfx::Color(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class UploadBench : public BenchTest {
+public:
+ UploadBench()
+ : BenchTest("Upload Bench (10s 256x256 upload)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ int w = 256;
+ int h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ mTexture->Update(mSurface);
+ }
+ }
+};
+
+class TrivialTexturedQuadBench : public BenchTest {
+public:
+ TrivialTexturedQuadBench()
+ : BenchTest("Trvial Textured Quad (10s 256x256 quads)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+public:
+ StressTexturedQuadBench()
+ : BenchTest("Stress Textured Quad (10s 256x256 quads)")
+ {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ virtual already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect)
+{
+ std::vector<BenchTest*> tests;
+
+ tests.push_back(new EffectSolidColorBench());
+ tests.push_back(new UploadBench());
+ tests.push_back(new EffectSolidColorTrivialBench());
+ tests.push_back(new EffectSolidColorStressBench());
+ tests.push_back(new TrivialTexturedQuadBench());
+ tests.push_back(new StressTexturedQuadBench());
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ BenchTest* test = tests[i];
+ std::vector<TimeDuration> results;
+ int testsOverThreshold = 0;
+ PROFILER_MARKER(test->ToString());
+ for (size_t j = 0; j < TEST_STEPS; j++) {
+ test->Setup(aCompositor, j);
+
+ TimeStamp start = TimeStamp::Now();
+ IntRect screenRect(aScreenRect.x, aScreenRect.y,
+ aScreenRect.width, aScreenRect.height);
+ aCompositor->BeginFrame(
+ IntRect(screenRect.x, screenRect.y,
+ screenRect.width, screenRect.height),
+ nullptr, aScreenRect, nullptr, nullptr);
+
+ test->DrawFrame(aCompositor, aScreenRect, j);
+
+ aCompositor->EndFrame();
+ results.push_back(TimeStamp::Now() - start);
+
+ if (results[j].ToMilliseconds() > DURATION_THRESHOLD) {
+ testsOverThreshold++;
+ if (testsOverThreshold == THRESHOLD_ABORT_COUNT) {
+ test->Teardown(aCompositor);
+ break;
+ }
+ } else {
+ testsOverThreshold = 0;
+ }
+ test->Teardown(aCompositor);
+ }
+
+ printf_stderr("%s\n", test->ToString());
+ printf_stderr("Run step, Time (ms)\n");
+ for (size_t j = 0; j < results.size(); j++) {
+ printf_stderr("%i,%f\n", j, (float)results[j].ToMilliseconds());
+ }
+ printf_stderr("\n", test->ToString());
+ }
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ delete tests[i];
+ }
+}
+
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect)
+{
+ static bool sRanBenchmark = false;
+ bool wantBenchmark = gfxPrefs::LayersBenchEnabled();
+ if (wantBenchmark && !sRanBenchmark) {
+ RunCompositorBench(aCompositor, aScreenRect);
+ }
+ sRanBenchmark = wantBenchmark;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
diff --git a/gfx/layers/ipc/CompositorBench.h b/gfx/layers/ipc/CompositorBench.h
new file mode 100644
index 000000000..04c994495
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBench_h
+#define mozilla_layers_CompositorBench_h
+
+#include "mozilla/gfx/Rect.h" // for Rect
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+// Uncomment this line to rebuild with compositor bench.
+// #define MOZ_COMPOSITOR_BENCH
+
+#ifdef MOZ_COMPOSITOR_BENCH
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect);
+#else
+static inline void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect) {}
+#endif
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
+
diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp
new file mode 100644
index 000000000..f623b10b8
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -0,0 +1,1150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include <stddef.h> // for size_t
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "base/message_loop.h" // for MessageLoop
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "gfxPrefs.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/layers/TextureClient.h"// for TextureClient
+#include "mozilla/layers/TextureClientPool.h"// for TextureClientPool
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsAutoPtr.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsIObserver.h" // for nsIObserver
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop, etc
+#include "FrameLayerBuilder.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#if defined(XP_WIN)
+#include "WinUtils.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
+#include "VsyncSource.h"
+
+using mozilla::layers::LayerTransactionChild;
+using mozilla::dom::TabChildBase;
+using mozilla::Unused;
+using mozilla::gfx::GPUProcessManager;
+
+namespace mozilla {
+namespace layers {
+
+static int sShmemCreationCounter = 0;
+
+static void ResetShmemCounter()
+{
+ sShmemCreationCounter = 0;
+}
+
+static void ShmemAllocated(CompositorBridgeChild* aProtocol)
+{
+ sShmemCreationCounter++;
+ if (sShmemCreationCounter > 256) {
+ aProtocol->SendSyncWithCompositor();
+ ResetShmemCounter();
+ MOZ_PERFORMANCE_WARNING("gfx", "The number of shmem allocations is too damn high!");
+ }
+}
+
+static StaticRefPtr<CompositorBridgeChild> sCompositorBridge;
+
+Atomic<int32_t> KnowsCompositor::sSerialCounter(0);
+
+CompositorBridgeChild::CompositorBridgeChild(LayerManager *aLayerManager)
+ : mLayerManager(aLayerManager)
+ , mCanSend(false)
+ , mFwdTransactionId(0)
+ , mMessageLoop(MessageLoop::current())
+ , mSectionAllocator(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorBridgeChild::~CompositorBridgeChild()
+{
+ if (mCanSend) {
+ gfxCriticalError() << "CompositorBridgeChild was not deinitialized";
+ }
+}
+
+bool
+CompositorBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+static void DeferredDestroyCompositor(RefPtr<CompositorBridgeParent> aCompositorBridgeParent,
+ RefPtr<CompositorBridgeChild> aCompositorBridgeChild)
+{
+ aCompositorBridgeChild->Close();
+
+ if (sCompositorBridge == aCompositorBridgeChild) {
+ sCompositorBridge = nullptr;
+ }
+}
+
+void
+CompositorBridgeChild::Destroy()
+{
+ // This must not be called from the destructor!
+ mTexturesWaitingRecycled.Clear();
+
+ if (!mCanSend) {
+ return;
+ }
+
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Destroy();
+ }
+
+ if (mSectionAllocator) {
+ delete mSectionAllocator;
+ mSectionAllocator = nullptr;
+ }
+
+ // Destroying the layer manager may cause all sorts of things to happen, so
+ // let's make sure there is still a reference to keep this alive whatever
+ // happens.
+ RefPtr<CompositorBridgeChild> selfRef = this;
+
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ }
+
+ AutoTArray<PLayerTransactionChild*, 16> transactions;
+ ManagedPLayerTransactionChild(transactions);
+ for (int i = transactions.Length() - 1; i >= 0; --i) {
+ RefPtr<LayerTransactionChild> layers =
+ static_cast<LayerTransactionChild*>(transactions[i]);
+ layers->Destroy();
+ }
+
+ const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
+ for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<TextureClient> texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
+
+ if (texture) {
+ texture->Destroy();
+ }
+ }
+
+ SendWillClose();
+ mCanSend = false;
+
+ // We no longer care about unexpected shutdowns, in the remote process case.
+ mProcessToken = 0;
+
+ // The call just made to SendWillClose can result in IPC from the
+ // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the destruction
+ // of shared memory). We need to ensure this gets processed by the
+ // CompositorBridgeChild before it gets destroyed. It suffices to ensure that
+ // events already in the MessageLoop get processed before the
+ // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to
+ // handle compositor desctruction.
+
+ // From now on we can't send any message message.
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef));
+}
+
+// static
+void
+CompositorBridgeChild::ShutDown()
+{
+ if (sCompositorBridge) {
+ sCompositorBridge->Destroy();
+ do {
+ NS_ProcessNextEvent(nullptr, true);
+ } while (sCompositorBridge);
+ }
+}
+
+bool
+CompositorBridgeChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId,
+ FrameMetrics& aFrame)
+{
+ SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
+ if (data) {
+ data->CopyFrameMetrics(&aFrame);
+ return true;
+ }
+ return false;
+}
+
+/* static */ bool
+CompositorBridgeChild::InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ // There's only one compositor per child process.
+ MOZ_ASSERT(!sCompositorBridge);
+
+ RefPtr<CompositorBridgeChild> child(new CompositorBridgeChild(nullptr));
+ if (!aEndpoint.Bind(child)) {
+ NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
+ return false;
+ }
+ child->InitIPDL();
+
+ // We release this ref in DeferredDestroyCompositor.
+ sCompositorBridge = child;
+ return true;
+}
+
+/* static */ bool
+CompositorBridgeChild::ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) {
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get
+ // a false result and a MsgDropped processing error. This is okay.
+ old->Destroy();
+ }
+
+ return InitForContent(Move(aEndpoint));
+}
+
+CompositorBridgeParent*
+CompositorBridgeChild::InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurface,
+ const gfx::IntSize& aSurfaceSize)
+{
+ TimeDuration vsyncRate =
+ gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
+
+ mCompositorBridgeParent =
+ new CompositorBridgeParent(aScale, vsyncRate, aUseExternalSurface, aSurfaceSize);
+
+ bool ok = Open(mCompositorBridgeParent->GetIPCChannel(),
+ CompositorThreadHolder::Loop(),
+ ipc::ChildSide);
+ MOZ_RELEASE_ASSERT(ok);
+
+ InitIPDL();
+ mCompositorBridgeParent->InitSameProcess(aWidget, aLayerTreeId, aUseAPZ);
+ return mCompositorBridgeParent;
+}
+
+/* static */ RefPtr<CompositorBridgeChild>
+CompositorBridgeChild::CreateRemote(const uint64_t& aProcessToken,
+ LayerManager* aLayerManager,
+ Endpoint<PCompositorBridgeChild>&& aEndpoint)
+{
+ RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager);
+ if (!aEndpoint.Bind(child)) {
+ return nullptr;
+ }
+ child->InitIPDL();
+ child->mProcessToken = aProcessToken;
+ return child;
+}
+
+void
+CompositorBridgeChild::InitIPDL()
+{
+ mCanSend = true;
+ AddRef();
+}
+
+void
+CompositorBridgeChild::DeallocPCompositorBridgeChild()
+{
+ Release();
+}
+
+/*static*/ CompositorBridgeChild*
+CompositorBridgeChild::Get()
+{
+ // This is only expected to be used in child processes.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ return sCompositorBridge;
+}
+
+// static
+bool
+CompositorBridgeChild::ChildProcessHasCompositorBridge()
+{
+ return sCompositorBridge != nullptr;
+}
+
+PLayerTransactionChild*
+CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier*,
+ bool*)
+{
+ LayerTransactionChild* c = new LayerTransactionChild(aId);
+ c->AddIPDLReference();
+ return c;
+}
+
+bool
+CompositorBridgeChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor)
+{
+ uint64_t childId = static_cast<LayerTransactionChild*>(actor)->GetId();
+
+ for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<SharedFrameMetricsData>& data = iter.Data();
+ if (data->GetLayersId() == childId) {
+ iter.Remove();
+ }
+ }
+ static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvInvalidateLayers(const uint64_t& aLayersId)
+{
+ if (mLayerManager) {
+ MOZ_ASSERT(aLayersId == 0);
+ FrameLayerBuilder::InvalidateAllLayers(mLayerManager);
+ } else if (aLayersId != 0) {
+ if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
+ child->InvalidateLayers();
+ }
+ }
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId,
+ const TextureFactoryIdentifier& aNewIdentifier)
+{
+ if (mLayerManager) {
+ // This case is handled directly by nsBaseWidget.
+ MOZ_ASSERT(aLayersId == 0);
+ } else if (aLayersId != 0) {
+ if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
+ child->CompositorUpdated(aNewIdentifier);
+
+ // If we still get device reset here, something must wrong when creating
+ // d3d11 devices.
+ if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ gfxCriticalError() << "Unexpected reset device processing when \
+ updating compositor.";
+ }
+ }
+ if (!mCanSend) {
+ return true;
+ }
+ SendAcknowledgeCompositorUpdate(aLayersId);
+ }
+ return true;
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+static void CalculatePluginClip(const LayoutDeviceIntRect& aBounds,
+ const nsTArray<LayoutDeviceIntRect>& aPluginClipRects,
+ const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<LayoutDeviceIntRect>& aResult,
+ LayoutDeviceIntRect& aVisibleBounds,
+ bool& aPluginIsVisible)
+{
+ aPluginIsVisible = true;
+ LayoutDeviceIntRegion contentVisibleRegion;
+ // aPluginClipRects (plugin widget origin) - contains *visible* rects
+ for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) {
+ LayoutDeviceIntRect rect = aPluginClipRects[idx];
+ // shift to content origin
+ rect.MoveBy(aBounds.x, aBounds.y);
+ // accumulate visible rects
+ contentVisibleRegion.OrWith(rect);
+ }
+ // apply layers clip (window origin)
+ LayoutDeviceIntRegion region = aParentLayerVisibleRegion;
+ region.MoveBy(-aContentOffset.x, -aContentOffset.y);
+ contentVisibleRegion.AndWith(region);
+ if (contentVisibleRegion.IsEmpty()) {
+ aPluginIsVisible = false;
+ return;
+ }
+ // shift to plugin widget origin
+ contentVisibleRegion.MoveBy(-aBounds.x, -aBounds.y);
+ for (auto iter = contentVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& rect = iter.Get();
+ aResult.AppendElement(rect);
+ aVisibleBounds.UnionRect(aVisibleBounds, rect);
+ }
+}
+#endif
+
+bool
+CompositorBridgeChild::RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins)
+{
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ NS_NOTREACHED("CompositorBridgeChild::RecvUpdatePluginConfigurations calls "
+ "unexpected on this platform.");
+ return false;
+#else
+ // Now that we are on the main thread, update plugin widget config.
+ // This should happen a little before we paint to the screen assuming
+ // the main thread is running freely.
+ DebugOnly<nsresult> rv;
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Tracks visible plugins we update, so we can hide any plugins we don't.
+ nsTArray<uintptr_t> visiblePluginIds;
+ nsIWidget* parent = nullptr;
+ for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
+ nsIWidget* widget =
+ nsIWidget::LookupRegisteredPluginWindow(aPlugins[pluginsIdx].windowId());
+ if (!widget) {
+ NS_WARNING("Unexpected, plugin id not found!");
+ continue;
+ }
+ if (!parent) {
+ parent = widget->GetParent();
+ }
+ bool isVisible = aPlugins[pluginsIdx].visible();
+ if (widget && !widget->Destroyed()) {
+ LayoutDeviceIntRect bounds;
+ LayoutDeviceIntRect visibleBounds;
+ // If the plugin is visible update it's geometry.
+ if (isVisible) {
+ // Set bounds (content origin)
+ bounds = aPlugins[pluginsIdx].bounds();
+ nsTArray<LayoutDeviceIntRect> rectsOut;
+ // This call may change the value of isVisible
+ CalculatePluginClip(bounds, aPlugins[pluginsIdx].clip(),
+ aContentOffset,
+ aParentLayerVisibleRegion,
+ rectsOut, visibleBounds, isVisible);
+ // content clipping region (widget origin)
+ rv = widget->SetWindowClipRegion(rectsOut, false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+ // This will trigger a browser window paint event for areas uncovered
+ // by a child window move, and will call invalidate on the plugin
+ // parent window which the browser owns. The latter gets picked up in
+ // our OnPaint handler and forwarded over to the plugin process async.
+ rv = widget->Resize(aContentOffset.x + bounds.x,
+ aContentOffset.y + bounds.y,
+ bounds.width, bounds.height, true);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+ }
+
+ rv = widget->Enable(isVisible);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+
+ // visible state - updated after clipping, prior to invalidating
+ rv = widget->Show(isVisible);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+
+ // Handle invalidation, this can be costly, avoid if it is not needed.
+ if (isVisible) {
+ // invalidate region (widget origin)
+#if defined(XP_WIN)
+ // Work around for flash's crummy sandbox. See bug 762948. This call
+ // digs down into the window hirearchy, invalidating regions on
+ // windows owned by other processes.
+ mozilla::widget::WinUtils::InvalidatePluginAsWorkaround(
+ widget, visibleBounds);
+#else
+ rv = widget->Invalidate(visibleBounds);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+#endif
+ visiblePluginIds.AppendElement(aPlugins[pluginsIdx].windowId());
+ }
+ }
+ }
+ // Any plugins we didn't update need to be hidden, as they are
+ // not associated with visible content.
+ nsIWidget::UpdateRegisteredPluginWindowVisibility((uintptr_t)parent, visiblePluginIds);
+ if (!mCanSend) {
+ return true;
+ }
+ SendRemotePluginsReady();
+ return true;
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+#if defined(XP_WIN)
+static void
+ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis, MessageLoop* aLoop)
+{
+ aLoop->PostTask(NewNonOwningRunnableMethod(
+ aThis, &CompositorBridgeChild::SendAllPluginsCaptured));
+}
+#endif
+
+bool
+CompositorBridgeChild::RecvCaptureAllPlugins(const uintptr_t& aParentWidget)
+{
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+ nsIWidget::CaptureRegisteredPlugins(aParentWidget);
+
+ // Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild loop,
+ // to make sure that the image updates on that thread have been processed.
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableFunction(&ScheduleSendAllPluginsCaptured, this,
+ MessageLoop::current()));
+ return true;
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected.");
+ return false;
+#endif
+}
+
+bool
+CompositorBridgeChild::RecvHideAllPlugins(const uintptr_t& aParentWidget)
+{
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ NS_NOTREACHED("CompositorBridgeChild::RecvHideAllPlugins calls "
+ "unexpected on this platform.");
+ return false;
+#else
+ MOZ_ASSERT(NS_IsMainThread());
+ nsTArray<uintptr_t> list;
+ nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list);
+ if (!mCanSend) {
+ return true;
+ }
+ SendRemotePluginsReady();
+ return true;
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+bool
+CompositorBridgeChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ // Hold a reference to keep texture pools alive. See bug 1387799
+ AutoTArray<RefPtr<TextureClientPool>,2> texturePools = mTexturePools;
+
+ if (mLayerManager) {
+ MOZ_ASSERT(aId == 0);
+ RefPtr<ClientLayerManager> m = mLayerManager->AsClientLayerManager();
+ MOZ_ASSERT(m);
+ m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ } else if (aId != 0) {
+ RefPtr<dom::TabChild> child = dom::TabChild::GetFrom(aId);
+ if (child) {
+ child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ }
+
+ for (size_t i = 0; i < texturePools.Length(); i++) {
+ texturePools[i]->ReturnDeferredClients();
+ }
+
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvOverfill(const uint32_t &aOverfill)
+{
+ for (size_t i = 0; i < mOverfillObservers.Length(); i++) {
+ mOverfillObservers[i]->RunOverfillCallback(aOverfill);
+ }
+ mOverfillObservers.Clear();
+ return true;
+}
+
+void
+CompositorBridgeChild::AddOverfillObserver(ClientLayerManager* aLayerManager)
+{
+ MOZ_ASSERT(aLayerManager);
+ mOverfillObservers.AppendElement(aLayerManager);
+}
+
+bool
+CompositorBridgeChild::RecvClearCachedResources(const uint64_t& aId)
+{
+ dom::TabChild* child = dom::TabChild::GetFrom(aId);
+ if (child) {
+ child->ClearCachedResources();
+ }
+ return true;
+}
+
+void
+CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (aWhy == AbnormalShutdown) {
+ // If the parent side runs into a problem then the actor will be destroyed.
+ // There is nothing we can do in the child side, here sets mCanSend as false.
+ gfxCriticalNote << "Receive IPC close with reason=AbnormalShutdown";
+ }
+
+ mCanSend = false;
+
+ if (mProcessToken && XRE_IsParentProcess()) {
+ GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+}
+
+bool
+CompositorBridgeChild::RecvSharedCompositorFrameMetrics(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId)
+{
+ SharedFrameMetricsData* data = new SharedFrameMetricsData(
+ metrics, handle, aLayersId, aAPZCId);
+ mFrameMetricsTable.Put(data->GetViewID(), data);
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics(
+ const ViewID& aId,
+ const uint32_t& aAPZCId)
+{
+ SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
+ // The SharedFrameMetricsData may have been removed previously if
+ // a SharedFrameMetricsData with the same ViewID but later APZCId had
+ // been store and over wrote it.
+ if (data && (data->GetAPZCId() == aAPZCId)) {
+ mFrameMetricsTable.Remove(aId);
+ }
+ return true;
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::SharedFrameMetricsData(
+ const ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId)
+ : mMutex(nullptr)
+ , mLayersId(aLayersId)
+ , mAPZCId(aAPZCId)
+{
+ mBuffer = new ipc::SharedMemoryBasic;
+ mBuffer->SetHandle(metrics);
+ mBuffer->Map(sizeof(FrameMetrics));
+ mMutex = new CrossProcessMutex(handle);
+ MOZ_COUNT_CTOR(SharedFrameMetricsData);
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::~SharedFrameMetricsData()
+{
+ // When the hash table deletes the class, delete
+ // the shared memory and mutex.
+ delete mMutex;
+ mBuffer = nullptr;
+ MOZ_COUNT_DTOR(SharedFrameMetricsData);
+}
+
+void
+CompositorBridgeChild::SharedFrameMetricsData::CopyFrameMetrics(FrameMetrics* aFrame)
+{
+ FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ mMutex->Lock();
+ *aFrame = *frame;
+ mMutex->Unlock();
+}
+
+FrameMetrics::ViewID
+CompositorBridgeChild::SharedFrameMetricsData::GetViewID()
+{
+ FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ // Not locking to read of mScrollId since it should not change after being
+ // initially set.
+ return frame->GetScrollId();
+}
+
+uint64_t
+CompositorBridgeChild::SharedFrameMetricsData::GetLayersId() const
+{
+ return mLayersId;
+}
+
+uint32_t
+CompositorBridgeChild::SharedFrameMetricsData::GetAPZCId()
+{
+ return mAPZCId;
+}
+
+
+bool
+CompositorBridgeChild::RecvRemotePaintIsReady()
+{
+ // Used on the content thread, this bounces the message to the
+ // TabParent (via the TabChild) if the notification was previously requested.
+ // XPCOM gives a soup of compiler errors when trying to do_QueryReference
+ // so I'm using static_cast<>
+ MOZ_LAYERS_LOG(("[RemoteGfx] CompositorBridgeChild received RemotePaintIsReady"));
+ RefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild));
+ if (!iTabChildBase) {
+ MOZ_LAYERS_LOG(("[RemoteGfx] Note: TabChild was released before RemotePaintIsReady. "
+ "MozAfterRemotePaint will not be sent to listener."));
+ return true;
+ }
+ TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get());
+ TabChild* tabChild = static_cast<TabChild*>(tabChildBase);
+ MOZ_ASSERT(tabChild);
+ Unused << tabChild->SendRemotePaintIsReady();
+ mWeakTabChild = nullptr;
+ return true;
+}
+
+
+void
+CompositorBridgeChild::RequestNotifyAfterRemotePaint(TabChild* aTabChild)
+{
+ MOZ_ASSERT(aTabChild, "NULL TabChild not allowed in CompositorBridgeChild::RequestNotifyAfterRemotePaint");
+ mWeakTabChild = do_GetWeakReference( static_cast<dom::TabChildBase*>(aTabChild) );
+ if (!mCanSend) {
+ return;
+ }
+ Unused << SendRequestNotifyAfterRemotePaint();
+}
+
+void
+CompositorBridgeChild::CancelNotifyAfterRemotePaint(TabChild* aTabChild)
+{
+ RefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild));
+ if (!iTabChildBase) {
+ return;
+ }
+ TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get());
+ TabChild* tabChild = static_cast<TabChild*>(tabChildBase);
+ if (tabChild == aTabChild) {
+ mWeakTabChild = nullptr;
+ }
+}
+
+bool
+CompositorBridgeChild::SendWillClose()
+{
+ MOZ_RELEASE_ASSERT(mCanSend);
+ return PCompositorBridgeChild::SendWillClose();
+}
+
+bool
+CompositorBridgeChild::SendPause()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendPause();
+}
+
+bool
+CompositorBridgeChild::SendResume()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendResume();
+}
+
+bool
+CompositorBridgeChild::SendNotifyChildCreated(const uint64_t& id)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyChildCreated(id);
+}
+
+bool
+CompositorBridgeChild::SendAdoptChild(const uint64_t& id)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAdoptChild(id);
+}
+
+bool
+CompositorBridgeChild::SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect);
+}
+
+bool
+CompositorBridgeChild::SendFlushRendering()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendFlushRendering();
+}
+
+bool
+CompositorBridgeChild::SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize, startIndex);
+}
+
+bool
+CompositorBridgeChild::SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex, intervals);
+}
+
+bool
+CompositorBridgeChild::SendNotifyRegionInvalidated(const nsIntRegion& region)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyRegionInvalidated(region);
+}
+
+bool
+CompositorBridgeChild::SendRequestNotifyAfterRemotePaint()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
+}
+
+bool
+CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
+ uint32_t aPresShellId)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
+ aPresShellId);
+}
+
+bool
+CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+}
+
+bool
+CompositorBridgeChild::SendAllPluginsCaptured()
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAllPluginsCaptured();
+}
+
+PTextureChild*
+CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t&,
+ const uint64_t& aSerial)
+{
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+bool
+CompositorBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
+{
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+CompositorBridgeChild::RecvObserveLayerUpdate(const uint64_t& aLayersId,
+ const uint64_t& aEpoch,
+ const bool& aActive)
+{
+ // This message is sent via the window compositor, not the tab compositor -
+ // however it still has a layers id.
+ MOZ_ASSERT(aLayersId);
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (RefPtr<dom::TabParent> tab = dom::TabParent::GetTabParentFromLayersId(aLayersId)) {
+ tab->LayerTreeUpdate(aEpoch, aActive);
+ }
+ return true;
+}
+
+void
+CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
+{
+ if (!aClient) {
+ return;
+ }
+
+ if (!(aClient->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient);
+}
+
+void
+CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ if (aFwdTransactionId < client->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+void
+CompositorBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+TextureClientPool*
+CompositorBridgeChild::GetTexturePool(KnowsCompositor* aAllocator,
+ SurfaceFormat aFormat,
+ TextureFlags aFlags)
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ if (mTexturePools[i]->GetBackend() == aAllocator->GetCompositorBackendType() &&
+ mTexturePools[i]->GetMaxTextureSize() == aAllocator->GetMaxTextureSize() &&
+ mTexturePools[i]->GetFormat() == aFormat &&
+ mTexturePools[i]->GetFlags() == aFlags) {
+ return mTexturePools[i];
+ }
+ }
+
+ mTexturePools.AppendElement(
+ new TextureClientPool(aAllocator->GetCompositorBackendType(),
+ aAllocator->GetMaxTextureSize(),
+ aFormat,
+ gfx::gfxVars::TileSize(),
+ aFlags,
+ gfxPrefs::LayersTilePoolShrinkTimeout(),
+ gfxPrefs::LayersTilePoolClearTimeout(),
+ gfxPrefs::LayersTileInitialPoolSize(),
+ gfxPrefs::LayersTilePoolUnusedSize(),
+ this));
+
+ return mTexturePools.LastElement();
+}
+
+void
+CompositorBridgeChild::HandleMemoryPressure()
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+void
+CompositorBridgeChild::ClearTexturePool()
+{
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+FixedSizeSmallShmemSectionAllocator*
+CompositorBridgeChild::GetTileLockAllocator()
+{
+ MOZ_ASSERT(IPCOpen());
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!mSectionAllocator) {
+ mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this);
+ }
+ return mSectionAllocator;
+}
+
+
+PTextureChild*
+CompositorBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ return PCompositorBridgeChild::SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, 0 /* FIXME? */, aSerial);
+}
+
+bool
+CompositorBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::DeallocShmem(aShmem);
+}
+
+widget::PCompositorWidgetChild*
+CompositorBridgeChild::AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData)
+{
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool
+CompositorBridgeChild::DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor)
+{
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+RefPtr<IAPZCTreeManager>
+CompositorBridgeChild::GetAPZCTreeManager(uint64_t aLayerTreeId)
+{
+ bool apzEnabled = false;
+ Unused << SendAsyncPanZoomEnabled(aLayerTreeId, &apzEnabled);
+
+ if (!apzEnabled) {
+ return nullptr;
+ }
+
+ PAPZCTreeManagerChild* child = SendPAPZCTreeManagerConstructor(aLayerTreeId);
+ if (!child) {
+ return nullptr;
+ }
+ APZCTreeManagerChild* parent = static_cast<APZCTreeManagerChild*>(child);
+
+ return RefPtr<IAPZCTreeManager>(parent);
+}
+
+PAPZCTreeManagerChild*
+CompositorBridgeChild::AllocPAPZCTreeManagerChild(const uint64_t& aLayersId)
+{
+ APZCTreeManagerChild* child = new APZCTreeManagerChild();
+ child->AddRef();
+ return child;
+}
+
+PAPZChild*
+CompositorBridgeChild::AllocPAPZChild(const uint64_t& aLayersId)
+{
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool
+CompositorBridgeChild::DeallocPAPZChild(PAPZChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+CompositorBridgeChild::DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor)
+{
+ APZCTreeManagerChild* parent = static_cast<APZCTreeManagerChild*>(aActor);
+ parent->Release();
+ return true;
+}
+
+void
+CompositorBridgeChild::ProcessingError(Result aCode, const char* aReason)
+{
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode);
+ }
+}
+
+void
+CompositorBridgeChild::WillEndTransaction()
+{
+ ResetShmemCounter();
+}
+
+void
+CompositorBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+} // namespace layers
+} // namespace mozilla
+
diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h
new file mode 100644
index 000000000..60d1a146d
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeChild_h
+#define mozilla_layers_CompositorBridgeChild_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PCompositorBridgeChild.h"
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "nsClassHashtable.h" // for nsClassHashtable
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsHashKeys.h" // for nsUint64HashKey
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+
+namespace dom {
+class TabChild;
+} // namespace dom
+
+namespace widget {
+class CompositorWidget;
+} // namespace widget
+
+namespace layers {
+
+using mozilla::dom::TabChild;
+
+class IAPZCTreeManager;
+class APZCTreeManagerChild;
+class ClientLayerManager;
+class CompositorBridgeParent;
+class TextureClient;
+class TextureClientPool;
+struct FrameMetrics;
+
+class CompositorBridgeChild final : public PCompositorBridgeChild,
+ public TextureForwarder
+{
+ typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override);
+
+ explicit CompositorBridgeChild(LayerManager *aLayerManager);
+
+ void Destroy();
+
+ /**
+ * Lookup the FrameMetrics shared by the compositor process with the
+ * associated FrameMetrics::ViewID. The returned FrameMetrics is used
+ * in progressive paint calculations.
+ */
+ bool LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, FrameMetrics&);
+
+ /**
+ * Initialize the singleton compositor bridge for a content process.
+ */
+ static bool InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
+ static bool ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint);
+
+ static RefPtr<CompositorBridgeChild> CreateRemote(
+ const uint64_t& aProcessToken,
+ LayerManager* aLayerManager,
+ Endpoint<PCompositorBridgeChild>&& aEndpoint);
+
+ /**
+ * Initialize the CompositorBridgeChild, create CompositorBridgeParent, and
+ * open a same-process connection.
+ */
+ CompositorBridgeParent* InitSameProcess(
+ widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ CSSToLayoutDeviceScale aScale,
+ bool aUseAPZ,
+ bool aUseExternalSurface,
+ const gfx::IntSize& aSurfaceSize);
+
+ static CompositorBridgeChild* Get();
+
+ static bool ChildProcessHasCompositorBridge();
+
+ void AddOverfillObserver(ClientLayerManager* aLayerManager);
+
+ virtual bool
+ RecvClearCachedResources(const uint64_t& id) override;
+
+ virtual bool
+ RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd) override;
+
+ virtual bool
+ RecvInvalidateLayers(const uint64_t& aLayersId) override;
+
+ virtual bool
+ RecvCompositorUpdated(const uint64_t& aLayersId,
+ const TextureFactoryIdentifier& aNewIdentifier) override;
+
+ virtual bool
+ RecvOverfill(const uint32_t &aOverfill) override;
+
+ virtual bool
+ RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins) override;
+
+ virtual bool
+ RecvCaptureAllPlugins(const uintptr_t& aParentWidget) override;
+
+ virtual bool
+ RecvHideAllPlugins(const uintptr_t& aParentWidget) override;
+
+ virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+
+ virtual bool DeallocPTextureChild(PTextureChild* actor) override;
+
+ virtual bool
+ RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
+ virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+ /**
+ * Request that the parent tell us when graphics are ready on GPU.
+ * When we get that message, we bounce it to the TabParent via
+ * the TabChild
+ * @param tabChild The object to bounce the note to. Non-NULL.
+ */
+ void RequestNotifyAfterRemotePaint(TabChild* aTabChild);
+
+ void CancelNotifyAfterRemotePaint(TabChild* aTabChild);
+
+ // Beware that these methods don't override their super-class equivalent (which
+ // are not virtual), they just overload them.
+ // All of these Send* methods just add a sanity check (that it is not too late
+ // send a message) and forward the call to the super-class's equivalent method.
+ // This means that it is correct to call directly the super-class methods, but
+ // you won't get the extra safety provided here.
+ bool SendWillClose();
+ bool SendPause();
+ bool SendResume();
+ bool SendNotifyChildCreated(const uint64_t& id);
+ bool SendAdoptChild(const uint64_t& id);
+ bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect);
+ bool SendFlushRendering();
+ bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
+ bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex);
+ bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals);
+ bool SendNotifyRegionInvalidated(const nsIntRegion& region);
+ bool SendRequestNotifyAfterRemotePaint();
+ bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
+ bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const mozilla::CSSIntRegion& aRegion);
+ bool SendAllPluginsCaptured();
+ bool IsSameProcess() const override;
+
+ virtual bool IPCOpen() const override { return mCanSend; }
+
+ static void ShutDown();
+
+ void UpdateFwdTransactionId() { ++mFwdTransactionId; }
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set.
+ * Host side's usage is checked via CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
+
+ TextureClientPool* GetTexturePool(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags);
+ void ClearTexturePool();
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override;
+
+ void HandleMemoryPressure();
+
+ virtual MessageLoop* GetMessageLoop() const override { return mMessageLoop; }
+
+ virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PCompositorWidgetChild* AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData) override;
+ bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor) override;
+
+ RefPtr<IAPZCTreeManager> GetAPZCTreeManager(uint64_t aLayerTreeId);
+
+ PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const uint64_t& aLayersId) override;
+ bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor) override;
+
+ PAPZChild* AllocPAPZChild(const uint64_t& aLayersId) override;
+ bool DeallocPAPZChild(PAPZChild* aActor) override;
+
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ void WillEndTransaction();
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeChild();
+
+ void InitIPDL();
+ void DeallocPCompositorBridgeChild() override;
+
+ virtual PLayerTransactionChild*
+ AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool* aSuccess) override;
+
+ virtual bool DeallocPLayerTransactionChild(PLayerTransactionChild *aChild) override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvSharedCompositorFrameMetrics(const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId) override;
+
+ virtual bool RecvReleaseSharedCompositorFrameMetrics(const ViewID& aId,
+ const uint32_t& aAPZCId) override;
+
+ virtual bool
+ RecvRemotePaintIsReady() override;
+
+ bool RecvObserveLayerUpdate(const uint64_t& aLayersId,
+ const uint64_t& aEpoch,
+ const bool& aActive) override;
+
+ // Class used to store the shared FrameMetrics, mutex, and APZCId in a hash table
+ class SharedFrameMetricsData {
+ public:
+ SharedFrameMetricsData(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle,
+ const uint64_t& aLayersId,
+ const uint32_t& aAPZCId);
+
+ ~SharedFrameMetricsData();
+
+ void CopyFrameMetrics(FrameMetrics* aFrame);
+ FrameMetrics::ViewID GetViewID();
+ uint64_t GetLayersId() const;
+ uint32_t GetAPZCId();
+
+ private:
+ // Pointer to the class that allows access to the shared memory that contains
+ // the shared FrameMetrics
+ RefPtr<mozilla::ipc::SharedMemoryBasic> mBuffer;
+ CrossProcessMutex* mMutex;
+ uint64_t mLayersId;
+ // Unique ID of the APZC that is sharing the FrameMetrics
+ uint32_t mAPZCId;
+ };
+
+ RefPtr<LayerManager> mLayerManager;
+ // When not multi-process, hold a reference to the CompositorBridgeParent to keep it
+ // alive. This reference should be null in multi-process.
+ RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+
+ // The ViewID of the FrameMetrics is used as the key for this hash table.
+ // While this should be safe to use since the ViewID is unique
+ nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
+
+ // Weakly hold the TabChild that made a request to be alerted when
+ // the transaction has been received.
+ nsWeakPtr mWeakTabChild; // type is TabChild
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild);
+
+ // When we receive overfill numbers, notify these client layer managers
+ AutoTArray<ClientLayerManager*,0> mOverfillObservers;
+
+ // True until the beginning of the two-step shutdown sequence of this actor.
+ bool mCanSend;
+
+ /**
+ * Transaction id of ShadowLayerForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
+
+ MessageLoop* mMessageLoop;
+
+ AutoTArray<RefPtr<TextureClientPool>,2> mTexturePools;
+
+ uint64_t mProcessToken;
+
+ FixedSizeSmallShmemSectionAllocator* mSectionAllocator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBrigedChild_h
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
new file mode 100644
index 000000000..6728e8841
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -0,0 +1,2441 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "VRManager.h" // for VRManager
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_WIDGET_GTK
+#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#ifdef XP_WIN
+#include "mozilla/layers/CompositorD3D11.h"
+#include "mozilla/layers/CompositorD3D9.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h"
+#endif
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace std;
+
+using base::ProcessId;
+using base::Thread;
+
+ProcessId
+CompositorBridgeParentBase::GetChildProcessId()
+{
+ return OtherPid();
+}
+
+void
+CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(
+ OpNotifyNotUsed(textureId, aTransactionId));
+}
+
+void
+CompositorBridgeParentBase::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ Unused << SendParentAsyncMessages(aMessage);
+}
+
+bool
+CompositorBridgeParentBase::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem)
+{
+ PCompositorBridgeParent::DeallocShmem(aShmem);
+}
+
+base::ProcessId
+CompositorBridgeParentBase::RemotePid()
+{
+ return OtherPid();
+}
+
+bool
+CompositorBridgeParentBase::StartSharingMetrics(ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId)
+{
+ return PCompositorBridgeParent::SendSharedCompositorFrameMetrics(
+ aHandle, aMutexHandle, aLayersId, aApzcId);
+}
+
+bool
+CompositorBridgeParentBase::StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId)
+{
+ return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics(
+ aScrollId, aApzcId);
+}
+
+CompositorBridgeParent::LayerTreeState::LayerTreeState()
+ : mApzcTreeManagerParent(nullptr)
+ , mParent(nullptr)
+ , mLayerManager(nullptr)
+ , mCrossProcessParent(nullptr)
+ , mLayerTree(nullptr)
+ , mUpdatedPluginDataAvailable(false)
+ , mPendingCompositorUpdates(0)
+{
+}
+
+CompositorBridgeParent::LayerTreeState::~LayerTreeState()
+{
+ if (mController) {
+ mController->Destroy();
+ }
+}
+
+typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+static LayerTreeMap sIndirectLayerTrees;
+static StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+static void EnsureLayerTreeMapReady()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sIndirectLayerTreesLock) {
+ sIndirectLayerTreesLock = new Monitor("IndirectLayerTree");
+ mozilla::ClearOnShutdown(&sIndirectLayerTreesLock);
+ }
+}
+
+template <typename Lambda>
+inline void
+CompositorBridgeParent::ForEachIndirectLayerTree(const Lambda& aCallback)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); it++) {
+ LayerTreeState* state = &it->second;
+ if (state->mParent == this) {
+ aCallback(state, it->first);
+ }
+ }
+}
+
+/**
+ * A global map referencing each compositor by ID.
+ *
+ * This map is used by the ImageBridge protocol to trigger
+ * compositions without having to keep references to the
+ * compositor
+ */
+typedef map<uint64_t,CompositorBridgeParent*> CompositorMap;
+static StaticAutoPtr<CompositorMap> sCompositorMap;
+
+void
+CompositorBridgeParent::Setup()
+{
+ EnsureLayerTreeMapReady();
+
+ MOZ_ASSERT(!sCompositorMap);
+ sCompositorMap = new CompositorMap;
+}
+
+void
+CompositorBridgeParent::Shutdown()
+{
+ MOZ_ASSERT(sCompositorMap);
+ MOZ_ASSERT(sCompositorMap->empty());
+ sCompositorMap = nullptr;
+}
+
+void
+CompositorBridgeParent::FinishShutdown()
+{
+ // TODO: this should be empty by now...
+ sIndirectLayerTrees.clear();
+}
+
+static void SetThreadPriority()
+{
+ hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
+}
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+static int32_t
+CalculateCompositionFrameRate()
+{
+ // Used when layout.frame_rate is -1. Needs to be kept in sync with
+ // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
+ // TODO: This should actually return the vsync rate.
+ const int32_t defaultFrameRate = 60;
+ int32_t compositionFrameRatePref = gfxPrefs::LayersCompositionFrameRate();
+ if (compositionFrameRatePref < 0) {
+ // Use the same frame rate for composition as for layout.
+ int32_t layoutFrameRatePref = gfxPrefs::LayoutFrameRate();
+ if (layoutFrameRatePref < 0) {
+ // TODO: The main thread frame scheduling code consults the actual
+ // monitor refresh rate in this case. We should do the same.
+ return defaultFrameRate;
+ }
+ return layoutFrameRatePref;
+ }
+ return compositionFrameRatePref;
+}
+#endif
+
+CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
+ : mMutex("CompositorVsyncScheduler.Observer.Mutex")
+ , mOwner(aOwner)
+{
+}
+
+CompositorVsyncScheduler::Observer::~Observer()
+{
+ MOZ_ASSERT(!mOwner);
+}
+
+bool
+CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+ MutexAutoLock lock(mMutex);
+ if (!mOwner) {
+ return false;
+ }
+ return mOwner->NotifyVsync(aVsyncTimestamp);
+}
+
+void
+CompositorVsyncScheduler::Observer::Destroy()
+{
+ MutexAutoLock lock(mMutex);
+ mOwner = nullptr;
+}
+
+CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
+ widget::CompositorWidget* aWidget)
+ : mCompositorBridgeParent(aCompositorBridgeParent)
+ , mLastCompose(TimeStamp::Now())
+ , mIsObservingVsync(false)
+ , mNeedsComposite(0)
+ , mVsyncNotificationsSkipped(0)
+ , mWidget(aWidget)
+ , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
+ , mCurrentCompositeTask(nullptr)
+ , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
+ , mSetNeedsCompositeTask(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU);
+ mVsyncObserver = new Observer(this);
+
+ // mAsapScheduling is set on the main thread during init,
+ // but is only accessed after on the compositor thread.
+ mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
+ gfxPlatform::IsInLayoutAsapMode();
+}
+
+CompositorVsyncScheduler::~CompositorVsyncScheduler()
+{
+ MOZ_ASSERT(!mIsObservingVsync);
+ MOZ_ASSERT(!mVsyncObserver);
+ // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners
+ mCompositorBridgeParent = nullptr;
+}
+
+void
+CompositorVsyncScheduler::Destroy()
+{
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ UnobserveVsync();
+ mVsyncObserver->Destroy();
+ mVsyncObserver = nullptr;
+
+ CancelCurrentSetNeedsCompositeTask();
+ CancelCurrentCompositeTask();
+}
+
+void
+CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp)
+{
+ // can be called from the compositor or vsync thread
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) {
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<TimeStamp>(this, &CompositorVsyncScheduler::Composite,
+ aCompositeTimestamp);
+ mCurrentCompositeTask = task;
+ ScheduleTask(task.forget(), 0);
+ }
+}
+
+void
+CompositorVsyncScheduler::ScheduleComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mAsapScheduling) {
+ // Used only for performance testing purposes
+ PostCompositeTask(TimeStamp::Now());
+#ifdef MOZ_WIDGET_ANDROID
+ } else if (mNeedsComposite >= 2 && mIsObservingVsync) {
+ // uh-oh, we already requested a composite at least twice so far, and a
+ // composite hasn't happened yet. It is possible that the vsync observation
+ // is blocked on the main thread, so let's just composite ASAP and not
+ // wait for the vsync. Note that this should only ever happen on Fennec
+ // because there content runs in the same process as the compositor, and so
+ // content can actually block the main thread in this process.
+ PostCompositeTask(TimeStamp::Now());
+#endif
+ } else {
+ SetNeedsComposite();
+ }
+}
+
+void
+CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ if (mSetNeedsCompositeTask) {
+ mSetNeedsCompositeTask->Cancel();
+ mSetNeedsCompositeTask = nullptr;
+ }
+ mNeedsComposite = 0;
+}
+
+/**
+ * TODO Potential performance heuristics:
+ * If a composite takes 17 ms, do we composite ASAP or wait until next vsync?
+ * If a layer transaction comes after vsync, do we composite ASAP or wait until
+ * next vsync?
+ * How many skipped vsync events until we stop listening to vsync events?
+ */
+void
+CompositorVsyncScheduler::SetNeedsComposite()
+{
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite);
+ mSetNeedsCompositeTask = task;
+ ScheduleTask(task.forget(), 0);
+ return;
+ } else {
+ MonitorAutoLock lock(mSetNeedsCompositeMonitor);
+ mSetNeedsCompositeTask = nullptr;
+ }
+
+ mNeedsComposite++;
+ if (!mIsObservingVsync && mNeedsComposite) {
+ ObserveVsync();
+ }
+}
+
+bool
+CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
+{
+ // Called from the vsync dispatch thread. When in the GPU Process, that's
+ // the same as the compositor thread.
+ MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(!NS_IsMainThread());
+ PostCompositeTask(aVsyncTimestamp);
+ return true;
+}
+
+void
+CompositorVsyncScheduler::CancelCurrentCompositeTask()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask) {
+ mCurrentCompositeTask->Cancel();
+ mCurrentCompositeTask = nullptr;
+ }
+}
+
+void
+CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ {
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ mCurrentCompositeTask = nullptr;
+ }
+
+ if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
+ // We can sometimes get vsync timestamps that are in the past
+ // compared to the last compose with force composites.
+ // In those cases, wait until the next vsync;
+ return;
+ }
+
+ MOZ_ASSERT(mCompositorBridgeParent);
+ if (!mAsapScheduling && mCompositorBridgeParent->IsPendingComposite()) {
+ // If previous composite is still on going, finish it and does a next
+ // composite in a next vsync.
+ mCompositorBridgeParent->FinishPendingComposite();
+ return;
+ }
+
+ DispatchTouchEvents(aVsyncTimestamp);
+ DispatchVREvents(aVsyncTimestamp);
+
+ if (mNeedsComposite || mAsapScheduling) {
+ mNeedsComposite = 0;
+ mLastCompose = aVsyncTimestamp;
+ ComposeToTarget(nullptr);
+ mVsyncNotificationsSkipped = 0;
+
+ TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
+ compositeFrameTotal.ToMilliseconds());
+ } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
+ UnobserveVsync();
+ }
+}
+
+void
+CompositorVsyncScheduler::OnForceComposeToTarget()
+{
+ /**
+ * bug 1138502 - There are cases such as during long-running window resizing events
+ * where we receive many sync RecvFlushComposites. We also get vsync notifications which
+ * will increment mVsyncNotificationsSkipped because a composite just occurred. After
+ * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
+ * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
+ * force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
+ * free and this oscillating behavior causes a performance hit. In order to avoid this problem,
+ * we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
+ */
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mVsyncNotificationsSkipped = 0;
+}
+
+void
+CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ OnForceComposeToTarget();
+ mLastCompose = TimeStamp::Now();
+ ComposeToTarget(aTarget, aRect);
+}
+
+bool
+CompositorVsyncScheduler::NeedsComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mNeedsComposite;
+}
+
+void
+CompositorVsyncScheduler::ObserveVsync()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(mVsyncObserver);
+ mIsObservingVsync = true;
+}
+
+void
+CompositorVsyncScheduler::UnobserveVsync()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(nullptr);
+ mIsObservingVsync = false;
+}
+
+void
+CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
+{
+}
+
+void
+CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ VRManager* vm = VRManager::Get();
+ vm->NotifyVsync(aVsyncTimestamp);
+}
+
+void
+CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
+ int aTime)
+{
+ MOZ_ASSERT(CompositorThreadHolder::Loop());
+ MOZ_ASSERT(aTime >= 0);
+ CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime);
+}
+
+void
+CompositorVsyncScheduler::ResumeComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLastCompose = TimeStamp::Now();
+ ComposeToTarget(nullptr);
+}
+
+void
+CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(mCompositorBridgeParent);
+ mCompositorBridgeParent->CompositeToTarget(aTarget, aRect);
+}
+
+static inline MessageLoop*
+CompositorLoop()
+{
+ return CompositorThreadHolder::Loop();
+}
+
+CompositorBridgeParent::CompositorBridgeParent(CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize)
+ : mWidget(nullptr)
+ , mScale(aScale)
+ , mVsyncRate(aVsyncRate)
+ , mIsTesting(false)
+ , mPendingTransaction(0)
+ , mPaused(false)
+ , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
+ , mEGLSurfaceSize(aSurfaceSize)
+ , mPauseCompositionMonitor("PauseCompositionMonitor")
+ , mResumeCompositionMonitor("ResumeCompositionMonitor")
+ , mResetCompositorMonitor("ResetCompositorMonitor")
+ , mRootLayerTreeID(0)
+ , mOverrideComposeReadiness(false)
+ , mForceCompositionTask(nullptr)
+ , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
+ , mCompositorScheduler(nullptr)
+ , mPaintTime(TimeDuration::Forever())
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ , mLastPluginUpdateLayerTreeId(0)
+ , mDeferPluginWindows(false)
+ , mPluginWindowsHidden(false)
+#endif
+{
+ // Always run destructor on the main thread
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ bool aUseAPZ)
+{
+ mWidget = aWidget;
+ mRootLayerTreeID = aLayerTreeId;
+ if (aUseAPZ) {
+ mApzcTreeManager = new APZCTreeManager();
+ }
+
+ // IPDL initialization. mSelfRef is cleared in DeferredDestroy.
+ SetOtherProcessId(base::GetCurrentProcId());
+ mSelfRef = this;
+
+ Initialize();
+}
+
+bool
+CompositorBridgeParent::Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ return false;
+ }
+ mSelfRef = this;
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvInitialize(const uint64_t& aRootLayerTreeId)
+{
+ mRootLayerTreeID = aRootLayerTreeId;
+
+ Initialize();
+ return true;
+}
+
+void
+CompositorBridgeParent::Initialize()
+{
+ MOZ_ASSERT(CompositorThread(),
+ "The compositor thread must be Initialized before instanciating a CompositorBridgeParent.");
+
+ mCompositorID = 0;
+ // FIXME: This holds on the the fact that right now the only thing that
+ // can destroy this instance is initialized on the compositor thread after
+ // this task has been processed.
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableFunction(&AddCompositor,
+ this, &mCompositorID));
+
+ CompositorLoop()->PostTask(NewRunnableFunction(SetThreadPriority));
+
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
+ }
+
+ LayerScope::SetPixelScale(mScale.scale);
+
+ mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+}
+
+bool
+CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier)
+{
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ ResetCompositorTask(aBackendHints, &newIdentifier);
+
+ if (newIdentifier) {
+ *aResult = true;
+ *aOutIdentifier = newIdentifier.value();
+ } else {
+ *aResult = false;
+ }
+
+ return true;
+}
+
+uint64_t
+CompositorBridgeParent::RootLayerTreeId()
+{
+ MOZ_ASSERT(mRootLayerTreeID);
+ return mRootLayerTreeID;
+}
+
+CompositorBridgeParent::~CompositorBridgeParent()
+{
+ InfallibleTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ // We expect all textures to be destroyed by now.
+ MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+}
+
+void
+CompositorBridgeParent::ForceIsFirstPaint()
+{
+ mCompositionManager->ForceIsFirstPaint();
+}
+
+void
+CompositorBridgeParent::StopAndClearResources()
+{
+ if (mForceCompositionTask) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ }
+
+ mPaused = true;
+
+ // Ensure that the layer manager is destroyed before CompositorBridgeChild.
+ if (mLayerManager) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([this] (LayerTreeState* lts, uint64_t) -> void {
+ mLayerManager->ClearCachedResources(lts->mRoot);
+ lts->mLayerManager = nullptr;
+ lts->mParent = nullptr;
+ });
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ mCompositionManager = nullptr;
+ }
+
+ if (mCompositor) {
+ mCompositor->DetachWidget();
+ mCompositor->Destroy();
+ mCompositor = nullptr;
+ }
+
+ // This must be destroyed now since it accesses the widget.
+ if (mCompositorScheduler) {
+ mCompositorScheduler->Destroy();
+ mCompositorScheduler = nullptr;
+ }
+
+ // After this point, it is no longer legal to access the widget.
+ mWidget = nullptr;
+}
+
+bool
+CompositorBridgeParent::RecvWillClose()
+{
+ StopAndClearResources();
+ return true;
+}
+
+void CompositorBridgeParent::DeferredDestroy()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(mCompositorThreadHolder);
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+bool
+CompositorBridgeParent::RecvPause()
+{
+ PauseComposition();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvResume()
+{
+ ResumeComposition();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect)
+{
+ RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
+ MOZ_ASSERT(target);
+ if (!target) {
+ // We kill the content process rather than have it continue with an invalid
+ // snapshot, that may be too harsh and we could decide to return some sort
+ // of error to the child process and let it deal with it...
+ return false;
+ }
+ ForceComposeToTarget(target, &aRect);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvFlushRendering()
+{
+ if (mCompositorScheduler->NeedsComposite())
+ {
+ CancelCurrentCompositeTask();
+ ForceComposeToTarget(nullptr);
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvForcePresent()
+{
+ // During the shutdown sequence mLayerManager may be null
+ if (mLayerManager) {
+ mLayerManager->ForcePresent();
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
+{
+ if (mLayerManager) {
+ mLayerManager->AddInvalidRegion(aRegion);
+ }
+ return true;
+}
+
+void
+CompositorBridgeParent::Invalidate()
+{
+ if (mLayerManager && mLayerManager->GetRoot()) {
+ mLayerManager->AddInvalidRegion(
+ mLayerManager->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ }
+}
+
+bool
+CompositorBridgeParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
+{
+ if (mLayerManager) {
+ *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
+ } else {
+ *aOutStartIndex = 0;
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex,
+ InfallibleTArray<float>* intervals)
+{
+ if (mLayerManager) {
+ mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
+ }
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId)
+{
+ ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+ return true;
+}
+
+void
+CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const Maybe<uint32_t>& aPresShellId)
+{
+ if (mLayerManager) {
+ mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId);
+
+ // We need to recomposite to update the minimap.
+ ScheduleComposition();
+ }
+}
+
+bool
+CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ if (mLayerManager) {
+ mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion);
+
+ // We need to recomposite to update the minimap.
+ ScheduleComposition();
+ }
+ return true;
+}
+
+void
+CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
+{
+ StopAndClearResources();
+
+ RemoveCompositor(mCompositorID);
+
+ mCompositionManager = nullptr;
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ClearTree();
+ mApzcTreeManager = nullptr;
+ }
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mRootLayerTreeID);
+ }
+
+ // There are chances that the ref count reaches zero on the main thread shortly
+ // after this function returns while some ipdl code still needs to run on
+ // this thread.
+ // We must keep the compositor parent alive untill the code handling message
+ // reception is finished on this thread.
+ mSelfRef = this;
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy));
+}
+
+void
+CompositorBridgeParent::ScheduleRenderOnCompositorThread()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ScheduleComposition));
+}
+
+void
+CompositorBridgeParent::InvalidateOnCompositorThread()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::Invalidate));
+}
+
+void
+CompositorBridgeParent::PauseComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "PauseComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mPauseCompositionMonitor);
+
+ if (!mPaused) {
+ mPaused = true;
+
+ mCompositor->Pause();
+
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+
+ // if anyone's waiting to make sure that composition really got paused, tell them
+ lock.NotifyAll();
+}
+
+void
+CompositorBridgeParent::ResumeComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "ResumeComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ if (!mCompositor->Resume()) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We can't get a surface. This could be because the activity changed between
+ // the time resume was scheduled and now.
+ __android_log_print(ANDROID_LOG_INFO, "CompositorBridgeParent", "Unable to renew compositor surface; remaining in paused state");
+#endif
+ lock.NotifyAll();
+ return;
+ }
+
+ mPaused = false;
+
+ Invalidate();
+ mCompositorScheduler->ResumeComposition();
+
+ // if anyone's waiting to make sure that composition really got resumed, tell them
+ lock.NotifyAll();
+}
+
+void
+CompositorBridgeParent::ForceComposition()
+{
+ // Cancel the orientation changed state to force composition
+ mForceCompositionTask = nullptr;
+ ScheduleRenderOnCompositorThread();
+}
+
+void
+CompositorBridgeParent::CancelCurrentCompositeTask()
+{
+ mCompositorScheduler->CancelCurrentCompositeTask();
+}
+
+void
+CompositorBridgeParent::SetEGLSurfaceSize(int width, int height)
+{
+ NS_ASSERTION(mUseExternalSurfaceSize, "Compositor created without UseExternalSurfaceSize provided");
+ mEGLSurfaceSize.SizeTo(width, height);
+ if (mCompositor) {
+ mCompositor->SetDestinationSurfaceSize(gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height));
+ }
+}
+
+void
+CompositorBridgeParent::ResumeCompositionAndResize(int width, int height)
+{
+ SetEGLSurfaceSize(width, height);
+ ResumeComposition();
+}
+
+/*
+ * This will execute a pause synchronously, waiting to make sure that the compositor
+ * really is paused.
+ */
+void
+CompositorBridgeParent::SchedulePauseOnCompositorThread()
+{
+ MonitorAutoLock lock(mPauseCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::PauseComposition));
+
+ // Wait until the pause has actually been processed by the compositor thread
+ lock.Wait();
+}
+
+bool
+CompositorBridgeParent::ScheduleResumeOnCompositorThread()
+{
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ResumeComposition));
+
+ // Wait until the resume has actually been processed by the compositor thread
+ lock.Wait();
+
+ return !mPaused;
+}
+
+bool
+CompositorBridgeParent::ScheduleResumeOnCompositorThread(int width, int height)
+{
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod
+ <int, int>(this,
+ &CompositorBridgeParent::ResumeCompositionAndResize,
+ width, height));
+
+ // Wait until the resume has actually been processed by the compositor thread
+ lock.Wait();
+
+ return !mPaused;
+}
+
+void
+CompositorBridgeParent::ScheduleTask(already_AddRefed<CancelableRunnable> task, int time)
+{
+ if (time == 0) {
+ MessageLoop::current()->PostTask(Move(task));
+ } else {
+ MessageLoop::current()->PostDelayedTask(Move(task), time);
+ }
+}
+
+void
+CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime)
+{
+ // We get a lot of paint timings for things with empty transactions.
+ if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) {
+ return;
+ }
+
+ mLayerManager->SetPaintTime(aPaintTime);
+}
+
+void
+CompositorBridgeParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, bool aHitTestUpdate)
+{
+ if (!aIsRepeatTransaction &&
+ mLayerManager &&
+ mLayerManager->GetRoot()) {
+ // Process plugin data here to give time for them to update before the next
+ // composition.
+ bool pluginsUpdatedFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this, nullptr,
+ &pluginsUpdatedFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If plugins haven't been updated, stop waiting.
+ if (!pluginsUpdatedFlag) {
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ }
+#endif
+
+ if (mApzcTreeManager && aHitTestUpdate) {
+ mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID,
+ mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
+ }
+
+ mLayerManager->NotifyShadowTreeTransaction();
+ }
+ if (aScheduleComposite) {
+ ScheduleComposition();
+ }
+}
+
+void
+CompositorBridgeParent::ScheduleComposition()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ mCompositorScheduler->ScheduleComposition();
+}
+
+// Go down the composite layer tree, setting properties to match their
+// content-side counterparts.
+/* static */ void
+CompositorBridgeParent::SetShadowProperties(Layer* aLayer)
+{
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [] (Layer *layer)
+ {
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowProperties(maskLayer);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ SetShadowProperties(layer->GetAncestorMaskLayerAt(i));
+ }
+
+ // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
+ LayerComposite* layerComposite = layer->AsLayerComposite();
+ // Set the layerComposite's base transform to the layer's base transform.
+ layerComposite->SetShadowBaseTransform(layer->GetBaseTransform());
+ layerComposite->SetShadowTransformSetByAnimation(false);
+ layerComposite->SetShadowVisibleRegion(layer->GetVisibleRegion());
+ layerComposite->SetShadowClipRect(layer->GetClipRect());
+ layerComposite->SetShadowOpacity(layer->GetOpacity());
+ layerComposite->SetShadowOpacitySetByAnimation(false);
+ }
+ );
+}
+
+void
+CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
+{
+ profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
+ PROFILER_LABEL("CompositorBridgeParent", "Composite",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "Composite can only be called on the compositor thread");
+ TimeStamp start = TimeStamp::Now();
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeDuration scheduleDelta = TimeStamp::Now() - mCompositorScheduler->GetExpectedComposeStartTime();
+ if (scheduleDelta > TimeDuration::FromMilliseconds(2) ||
+ scheduleDelta < TimeDuration::FromMilliseconds(-2)) {
+ printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n",
+ scheduleDelta.ToMilliseconds());
+ }
+#endif
+
+ if (!CanComposite()) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(start, end);
+ return;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (!mWaitForPluginsUntil.IsNull() &&
+ mWaitForPluginsUntil > start) {
+ mHaveBlockedForPlugins = true;
+ ScheduleComposition();
+ return;
+ }
+#endif
+
+ /*
+ * AutoResolveRefLayers handles two tasks related to Windows and Linux
+ * plugin window management:
+ * 1) calculating if we have remote content in the view. If we do not have
+ * remote content, all plugin windows for this CompositorBridgeParent (window)
+ * can be hidden since we do not support plugins in chrome when running
+ * under e10s.
+ * 2) Updating plugin position, size, and clip. We do this here while the
+ * remote layer tree is hooked up to to chrome layer tree. This is needed
+ * since plugin clipping can depend on chrome (for example, due to tab modal
+ * prompts). Updates in step 2 are applied via an async ipc message sent
+ * to the main thread.
+ */
+ bool hasRemoteContent = false;
+ bool updatePluginsFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this,
+ &hasRemoteContent,
+ &updatePluginsFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // We do not support plugins in local content. When switching tabs
+ // to local pages, hide every plugin associated with the window.
+ if (!hasRemoteContent && gfxVars::BrowserTabsRemoteAutostart() &&
+ mCachedPluginData.Length()) {
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ mCachedPluginData.Clear();
+ }
+#endif
+
+ if (aTarget) {
+ mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
+ } else {
+ mLayerManager->BeginTransaction();
+ }
+
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ if (mForceCompositionTask && !mOverrideComposeReadiness) {
+ if (mCompositionManager->ReadyForCompose()) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ } else {
+ return;
+ }
+ }
+
+ mCompositionManager->ComputeRotation();
+
+ TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
+ bool requestNextFrame = mCompositionManager->TransformShadowTree(time, mVsyncRate);
+ if (requestNextFrame) {
+ ScheduleComposition();
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If we have visible windowed plugins then we need to wait for content (and
+ // then the plugins) to have been updated by the active animation.
+ if (!mPluginWindowsHidden && mCachedPluginData.Length()) {
+ mWaitForPluginsUntil = mCompositorScheduler->GetLastComposeTime() + (mVsyncRate * 2);
+ }
+#endif
+ }
+
+ RenderTraceLayers(mLayerManager->GetRoot(), "0000");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::DumpHostLayers()) {
+ printf_stderr("Painting --- compositing layer tree:\n");
+ mLayerManager->Dump(/* aSorted = */ true);
+ }
+#endif
+ mLayerManager->SetDebugOverlayWantsNextFrame(false);
+ mLayerManager->EndTransaction(time);
+
+ if (!aTarget) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(start, end);
+ }
+
+ // We're not really taking advantage of the stored composite-again-time here.
+ // We might be able to skip the next few composites altogether. However,
+ // that's a bit complex to implement and we'll get most of the advantage
+ // by skipping compositing when we detect there's nothing invalid. This is why
+ // we do "composite until" rather than "composite again at".
+ if (!mCompositor->GetCompositeUntilTime().IsNull() ||
+ mLayerManager->DebugOverlayWantsNextFrame()) {
+ ScheduleComposition();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeDuration executionTime = TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime();
+ TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
+ int32_t frameRate = CalculateCompositionFrameRate();
+ if (frameRate > 0) {
+ frameBudget = TimeDuration::FromSeconds(1.0 / frameRate);
+ }
+ if (executionTime > frameBudget) {
+ printf_stderr("Compositor: Composite execution took %4.1f ms\n",
+ executionTime.ToMilliseconds());
+ }
+#endif
+
+ // 0 -> Full-tilt composite
+ if (gfxPrefs::LayersCompositionFrameRate() == 0
+ || mLayerManager->GetCompositor()->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS) {
+ // Special full-tilt composite mode for performance testing
+ ScheduleComposition();
+ }
+ mCompositor->SetCompositionTime(TimeStamp());
+
+ mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, start);
+ profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END);
+}
+
+bool
+CompositorBridgeParent::RecvRemotePluginsReady()
+{
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ mWaitForPluginsUntil = TimeStamp();
+ if (mHaveBlockedForPlugins) {
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ } else {
+ ScheduleComposition();
+ }
+ return true;
+#else
+ NS_NOTREACHED("CompositorBridgeParent::RecvRemotePluginsReady calls "
+ "unexpected on this platform.");
+ return false;
+#endif
+}
+
+void
+CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
+{
+ PROFILER_LABEL("CompositorBridgeParent", "ForceComposeToTarget",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ AutoRestore<bool> override(mOverrideComposeReadiness);
+ mOverrideComposeReadiness = true;
+ mCompositorScheduler->ForceComposeToTarget(aTarget, aRect);
+}
+
+PAPZCTreeManagerParent*
+CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+
+ // This message doubles as initialization
+ MOZ_ASSERT(!mApzcTreeManager);
+ mApzcTreeManager = new APZCTreeManager();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_ASSERT(state.mParent);
+ MOZ_ASSERT(!state.mApzcTreeManagerParent);
+ state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager());
+
+ return state.mApzcTreeManagerParent;
+}
+
+bool
+CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PAPZParent*
+CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool
+CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
+{
+ RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
+{
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(aLayersId == 0);
+ *aHasAPZ = AsyncPanZoomEnabled();
+ return true;
+}
+
+RefPtr<APZCTreeManager>
+CompositorBridgeParent::GetAPZCTreeManager()
+{
+ return mApzcTreeManager;
+}
+
+bool
+CompositorBridgeParent::CanComposite()
+{
+ return mLayerManager &&
+ mLayerManager->GetRoot() &&
+ !mPaused;
+}
+
+void
+CompositorBridgeParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
+ bool aIsFirstPaint)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (!aIsFirstPaint &&
+ !mCompositionManager->IsFirstPaint() &&
+ mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) {
+ if (mForceCompositionTask != nullptr) {
+ mForceCompositionTask->Cancel();
+ }
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod(this, &CompositorBridgeParent::ForceComposition);
+ mForceCompositionTask = task;
+ ScheduleTask(task.forget(), gfxPrefs::OrientationSyncMillis());
+ }
+}
+
+void
+CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aUnused,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate)
+{
+ ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
+
+ // Instruct the LayerManager to update its render bounds now. Since all the orientation
+ // change, dimension change would be done at the stage, update the size here is free of
+ // race condition.
+ mLayerManager->UpdateRenderBounds(aTargetConfig.naturalBounds());
+ mLayerManager->SetRegionToClear(aTargetConfig.clearRegion());
+ mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation());
+
+ mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId);
+ Layer* root = aLayerTree->GetRoot();
+ mLayerManager->SetRoot(root);
+
+ if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+
+ mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, root, aIsFirstPaint,
+ mRootLayerTreeID, aPaintSequenceNumber);
+ }
+
+ // The transaction ID might get reset to 1 if the page gets reloaded, see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
+ // Otherwise, it should be continually increasing.
+ MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransaction);
+ mPendingTransaction = aTransactionId;
+
+ if (root) {
+ SetShadowProperties(root);
+ }
+ if (aScheduleComposite) {
+ ScheduleComposition();
+ if (mPaused) {
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+ mLayerManager->NotifyShadowTreeTransaction();
+}
+
+void
+CompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
+{
+ ScheduleComposition();
+}
+
+bool
+CompositorBridgeParent::SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime)
+{
+ if (aTime.IsNull()) {
+ return false;
+ }
+
+ mIsTesting = true;
+ mTestTime = aTime;
+
+ bool testComposite = mCompositionManager &&
+ mCompositorScheduler->NeedsComposite();
+
+ // Update but only if we were already scheduled to animate
+ if (testComposite) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime, mVsyncRate);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is wating for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+
+ return true;
+}
+
+void
+CompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
+{
+ mIsTesting = false;
+}
+
+void
+CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+{
+ // NOTE: This should only be used for testing. For example, when mIsTesting is
+ // true or when called from test-only methods like
+ // LayerTransactionParent::RecvGetAnimationTransform.
+
+ // Synchronously update the layer tree
+ if (aLayerTree->GetRoot()) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
+ bool requestNextFrame =
+ mCompositionManager->TransformShadowTree(time, mVsyncRate,
+ AsyncCompositionManager::TransformsToSkip::APZ);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is waiting for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(now, now);
+ }
+ }
+}
+
+bool
+CompositorBridgeParent::RecvGetFrameUniformity(FrameUniformityData* aOutData)
+{
+ mCompositionManager->GetFrameUniformity(aOutData);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvRequestOverfill()
+{
+ uint32_t overfillRatio = mCompositor->GetFillRatio();
+ Unused << SendOverfill(overfillRatio);
+ return true;
+}
+
+void
+CompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree)
+{
+ MOZ_ASSERT(mApzcTreeManager);
+ uint64_t layersId = aLayerTree->GetId();
+ if (layersId == 0) {
+ // The request is coming from the parent-process layer tree, so we should
+ // use the compositor's root layer tree id.
+ layersId = mRootLayerTreeID;
+ }
+ RefPtr<CompositorBridgeParent> self = this;
+ APZThreadUtils::RunOnControllerThread(NS_NewRunnableFunction([=] () {
+ self->mApzcTreeManager->FlushApzRepaints(layersId);
+ }));
+}
+
+void
+CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ *aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
+}
+
+void
+CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ if (!mApzcTreeManager) {
+ return;
+ }
+ // Need to specifically bind this since it's overloaded.
+ void (APZCTreeManager::*setTargetApzcFunc)
+ (uint64_t, const nsTArray<ScrollableLayerGuid>&) =
+ &APZCTreeManager::SetTargetAPZC;
+ RefPtr<Runnable> task = NewRunnableMethod
+ <uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>>
+ (mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
+ APZThreadUtils::RunOnControllerThread(task.forget());
+
+}
+
+void
+CompositorBridgeParent::InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints)
+{
+ NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager");
+ NS_ASSERTION(!mCompositor, "Already initialised mCompositor");
+
+ mCompositor = NewCompositor(aBackendHints);
+ if (!mCompositor) {
+ return;
+ }
+
+ mLayerManager = new LayerManagerComposite(mCompositor);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager;
+}
+
+RefPtr<Compositor>
+CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHints)
+{
+ for (size_t i = 0; i < aBackendHints.Length(); ++i) {
+ RefPtr<Compositor> compositor;
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
+ compositor = new CompositorOGL(this,
+ mWidget,
+ mEGLSurfaceSize.width,
+ mEGLSurfaceSize.height,
+ mUseExternalSurfaceSize);
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
+#ifdef MOZ_WIDGET_GTK
+ if (gfxVars::UseXRender()) {
+ compositor = new X11BasicCompositor(this, mWidget);
+ } else
+#endif
+ {
+ compositor = new BasicCompositor(this, mWidget);
+ }
+#ifdef XP_WIN
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
+ compositor = new CompositorD3D11(this, mWidget);
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) {
+ compositor = new CompositorD3D9(this, mWidget);
+#endif
+ }
+ nsCString failureReason;
+ if (compositor && compositor->Initialize(&failureReason)) {
+ if (failureReason.IsEmpty()){
+ failureReason = "SUCCESS";
+ }
+
+ // should only report success here
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL){
+ Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, failureReason);
+ }
+#ifdef XP_WIN
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9){
+ Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason);
+ }
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){
+ Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason);
+ }
+#endif
+
+ compositor->SetCompositorID(mCompositorID);
+ return compositor;
+ }
+
+ // report any failure reasons here
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL){
+ gfxCriticalNote << "[OPENGL] Failed to init compositor with reason: "
+ << failureReason.get();
+ Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, failureReason);
+ }
+#ifdef XP_WIN
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9){
+ gfxCriticalNote << "[D3D9] Failed to init compositor with reason: "
+ << failureReason.get();
+ Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason);
+ }
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){
+ gfxCriticalNote << "[D3D11] Failed to init compositor with reason: "
+ << failureReason.get();
+ Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason);
+ }
+#endif
+ }
+
+ return nullptr;
+}
+
+PLayerTransactionParent*
+CompositorBridgeParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess)
+{
+ MOZ_ASSERT(aId == 0);
+
+ InitializeLayerManager(aBackendHints);
+
+ if (!mLayerManager) {
+ NS_WARNING("Failed to initialise Compositor");
+ *aSuccess = false;
+ LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0);
+ p->AddIPDLReference();
+ return p;
+ }
+
+ mCompositionManager = new AsyncCompositionManager(mLayerManager);
+ *aSuccess = true;
+
+ *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier();
+ LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool
+CompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor)
+{
+ static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent(uint64_t id)
+{
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ return it != sCompositorMap->end() ? it->second : nullptr;
+}
+
+void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor, uint64_t* outID)
+{
+ static uint64_t sNextID = 1;
+
+ ++sNextID;
+ (*sCompositorMap)[sNextID] = compositor;
+ *outID = sNextID;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id)
+{
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ if (it == sCompositorMap->end()) {
+ return nullptr;
+ }
+ CompositorBridgeParent *retval = it->second;
+ sCompositorMap->erase(it);
+ return retval;
+}
+
+void
+CompositorBridgeParent::NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end())
+ return;
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget)
+ return;
+
+ RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
+ if (!obs)
+ return;
+
+ obs->NotifyVsync(aTimeStamp);
+}
+
+bool
+CompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ return true;
+}
+
+bool
+CompositorBridgeParent::RecvNotifyChildRecreated(const uint64_t& aChild)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) {
+ // Invalid to register the same layer tree twice.
+ return false;
+ }
+
+ NotifyChildCreated(aChild);
+ return true;
+}
+
+void
+CompositorBridgeParent::NotifyChildCreated(uint64_t aChild)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ sIndirectLayerTrees[aChild].mParent = this;
+ sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
+}
+
+bool
+CompositorBridgeParent::RecvAdoptChild(const uint64_t& child)
+{
+ APZCTreeManagerParent* parent;
+ {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ if (sIndirectLayerTrees[child].mLayerTree) {
+ sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager);
+ }
+ parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
+ }
+
+ if (mApzcTreeManager && parent) {
+ parent->ChildAdopted(mApzcTreeManager);
+ }
+ return true;
+}
+
+static void
+EraseLayerState(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ auto iter = sIndirectLayerTrees.find(aId);
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent* parent = iter->second.mParent;
+ if (parent) {
+ parent->ClearApproximatelyVisibleRegions(aId, Nothing());
+ }
+
+ sIndirectLayerTrees.erase(iter);
+ }
+}
+
+/*static*/ void
+CompositorBridgeParent::DeallocateLayerTreeId(uint64_t aId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // Here main thread notifies compositor to remove an element from
+ // sIndirectLayerTrees. This removed element might be queried soon.
+ // Checking the elements of sIndirectLayerTrees exist or not before using.
+ if (!CompositorLoop()) {
+ gfxCriticalError() << "Attempting to post to a invalid Compositor Loop";
+ return;
+ }
+ CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId));
+}
+
+static void
+UpdateControllerForLayersId(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ // Adopt ref given to us by SetControllerForLayerTree()
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mController =
+ already_AddRefed<GeckoContentController>(aController);
+}
+
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
+ uint64_t aLayersId,
+ Layer* aRoot,
+ GeckoContentController* aController)
+ : mLayersId(aLayersId)
+{
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mRoot = aRoot;
+ sIndirectLayerTrees[aLayersId].mController = aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mLayersId);
+}
+
+/*static*/ void
+CompositorBridgeParent::SetControllerForLayerTree(uint64_t aLayersId,
+ GeckoContentController* aController)
+{
+ // This ref is adopted by UpdateControllerForLayersId().
+ aController->AddRef();
+ CompositorLoop()->PostTask(NewRunnableFunction(&UpdateControllerForLayersId,
+ aLayersId,
+ aController));
+}
+
+/*static*/ already_AddRefed<APZCTreeManager>
+CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId)
+{
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ LayerTreeState* lts = &cit->second;
+
+ RefPtr<APZCTreeManager> apzctm = lts->mParent
+ ? lts->mParent->mApzcTreeManager.get()
+ : nullptr;
+ return apzctm.forget();
+}
+
+static void
+InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp);
+ PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload);
+#endif
+}
+
+/*static */ void
+CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
+{
+ // Called in the vsync thread
+ if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
+ CompositorLoop()->PostTask(
+ NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
+ }
+}
+
+widget::PCompositorWidgetParent*
+CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ if (mWidget) {
+ // Should not create two widgets on the same compositor.
+ return nullptr;
+ }
+
+ widget::CompositorWidgetParent* widget =
+ new widget::CompositorWidgetParent(aInitData);
+ widget->AddRef();
+
+ // Sending the constructor acts as initialization as well.
+ mWidget = widget;
+ return widget;
+#else
+ return nullptr;
+#endif
+}
+
+bool
+CompositorBridgeParent::DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ static_cast<widget::CompositorWidgetParent*>(aActor)->Release();
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+CompositorBridgeParent::IsPendingComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return false;
+ }
+ return mCompositor->IsPendingComposite();
+}
+
+void
+CompositorBridgeParent::FinishPendingComposite()
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return;
+ }
+ return mCompositor->FinishPendingComposite();
+}
+
+CompositorController*
+CompositorBridgeParent::LayerTreeState::GetCompositorController() const
+{
+ return mParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const
+{
+ return mCrossProcessParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::InProcessSharingController() const
+{
+ return mParent;
+}
+
+void
+CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd)
+{
+ Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd);
+ mPendingTransaction = 0;
+
+ if (mLayerManager) {
+ nsTArray<ImageCompositeNotification> notifications;
+ mLayerManager->ExtractImageCompositeNotifications(&notifications);
+ if (!notifications.IsEmpty()) {
+ Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+ }
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
+ if (lts->mCrossProcessParent && lts->mParent == this) {
+ CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
+ cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd);
+ }
+ });
+}
+
+void
+CompositorBridgeParent::InvalidateRemoteLayers()
+{
+ MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
+
+ Unused << PCompositorBridgeParent::SendInvalidateLayers(0);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
+ if (lts->mCrossProcessParent) {
+ CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
+ Unused << cpcp->SendInvalidateLayers(aLayersId);
+ }
+ });
+}
+
+bool
+CompositorBridgeParent::ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier)
+{
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ {
+ MonitorAutoLock lock(mResetCompositorMonitor);
+
+ CompositorLoop()->PostTask(NewRunnableMethod
+ <StoreCopyPassByConstLRef<nsTArray<LayersBackend>>,
+ Maybe<TextureFactoryIdentifier>*>(this,
+ &CompositorBridgeParent::ResetCompositorTask,
+ aBackendHints,
+ &newIdentifier));
+
+ mResetCompositorMonitor.Wait();
+ }
+
+ if (!newIdentifier) {
+ return false;
+ }
+
+ *aOutIdentifier = newIdentifier.value();
+ return true;
+}
+
+// Invoked on the compositor thread. The main thread is waiting on the given
+// monitor.
+void
+CompositorBridgeParent::ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+ Maybe<TextureFactoryIdentifier>* aOutNewIdentifier)
+{
+ // Perform the reset inside a lock, so the main thread can wake up as soon as
+ // possible. We notify child processes (if necessary) outside the lock.
+ Maybe<TextureFactoryIdentifier> newIdentifier;
+ {
+ MonitorAutoLock lock(mResetCompositorMonitor);
+
+ newIdentifier = ResetCompositorImpl(aBackendHints);
+ *aOutNewIdentifier = newIdentifier;
+
+ mResetCompositorMonitor.NotifyAll();
+ }
+
+ // NOTE: |aBackendHints|, and |aOutNewIdentifier| are now all invalid since
+ // they are allocated on ResetCompositor's stack on the main thread, which
+ // is no longer waiting on the lock.
+
+ if (!newIdentifier) {
+ // No compositor change; nothing to do.
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void {
+ if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) {
+ Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value());
+
+ if (LayerTransactionParent* ltp = lts->mLayerTree) {
+ ltp->AddPendingCompositorUpdate();
+ }
+ lts->mPendingCompositorUpdates++;
+ }
+ });
+}
+
+Maybe<TextureFactoryIdentifier>
+CompositorBridgeParent::ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints)
+{
+ if (!mLayerManager) {
+ return Nothing();
+ }
+
+ RefPtr<Compositor> compositor = NewCompositor(aBackendHints);
+ if (!compositor) {
+ MOZ_RELEASE_ASSERT(compositor, "Failed to reset compositor.");
+ }
+
+ // Don't bother changing from basic->basic.
+ if (mCompositor &&
+ mCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC &&
+ compositor->GetBackendType() == LayersBackend::LAYERS_BASIC)
+ {
+ return Nothing();
+ }
+
+ if (mCompositor) {
+ mCompositor->SetInvalid();
+ }
+ mCompositor = compositor;
+ mLayerManager->ChangeCompositor(compositor);
+
+ return Some(compositor->GetTextureFactoryIdentifier());
+}
+
+static void
+OpenCompositor(RefPtr<CrossProcessCompositorBridgeParent> aCompositor,
+ Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ aCompositor->Bind(Move(aEndpoint));
+}
+
+/* static */ bool
+CompositorBridgeParent::CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint)
+{
+ gfxPlatform::InitLayersIPC();
+
+ RefPtr<CrossProcessCompositorBridgeParent> cpcp =
+ new CrossProcessCompositorBridgeParent();
+
+ CompositorLoop()->PostTask(NewRunnableFunction(OpenCompositor, cpcp, Move(aEndpoint)));
+ return true;
+}
+
+static void
+UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aId].mRoot = aRoot;
+ sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
+}
+
+/* static */ CompositorBridgeParent::LayerTreeState*
+CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ return &cit->second;
+}
+
+static CompositorBridgeParent::LayerTreeState*
+GetStateForRoot(uint64_t aContentLayersId, const MonitorAutoLock& aProofOfLock)
+{
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ // |state| is the state for the content process, but we want the APZCTMParent
+ // for the parent process owning that content process. So we have to jump to
+ // the LayerTreeState for the root layer tree id for that layer tree, and use
+ // the mApzcTreeManagerParent from that. This should also work with nested
+ // content processes, because RootLayerTreeId() will bypass any intermediate
+ // processes' ids and go straight to the root.
+ if (state) {
+ uint64_t rootLayersId = state->mParent->RootLayerTreeId();
+ itr = sIndirectLayerTrees.find(rootLayersId);
+ state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr;
+ }
+
+ return state;
+}
+
+/* static */ APZCTreeManagerParent*
+CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mApzcTreeManagerParent : nullptr;
+}
+
+/* static */ GeckoContentController*
+CompositorBridgeParent::GetGeckoContentControllerForRoot(uint64_t aContentLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mController.get() : nullptr;
+}
+
+PTextureParent*
+CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial)
+{
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool
+CompositorBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+//#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__);
+// printf_stderr(__VA_ARGS__);
+// printf_stderr("\n");
+#define PLUGINS_LOG(...)
+
+bool
+CompositorBridgeParent::UpdatePluginWindowState(uint64_t aId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& lts = sIndirectLayerTrees[aId];
+ if (!lts.mParent) {
+ PLUGINS_LOG("[%" PRIu64 "] layer tree compositor parent pointer is null", aId);
+ return false;
+ }
+
+ // Check if this layer tree has received any shadow layer updates
+ if (!lts.mUpdatedPluginDataAvailable) {
+ PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId);
+ return false;
+ }
+
+ // pluginMetricsChanged tracks whether we need to send plugin update
+ // data to the main thread. If we do we'll have to block composition,
+ // which we want to avoid if at all possible.
+ bool pluginMetricsChanged = false;
+
+ // Same layer tree checks
+ if (mLastPluginUpdateLayerTreeId == aId) {
+ // no plugin data and nothing has changed, bail.
+ if (!mCachedPluginData.Length() && !lts.mPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] no data, no changes", aId);
+ return false;
+ }
+
+ if (mCachedPluginData.Length() == lts.mPluginData.Length()) {
+ // check for plugin data changes
+ for (uint32_t idx = 0; idx < lts.mPluginData.Length(); idx++) {
+ if (!(mCachedPluginData[idx] == lts.mPluginData[idx])) {
+ pluginMetricsChanged = true;
+ break;
+ }
+ }
+ } else {
+ // array lengths don't match, need to update
+ pluginMetricsChanged = true;
+ }
+ } else {
+ // exchanging layer trees, we need to update
+ pluginMetricsChanged = true;
+ }
+
+ // Check if plugin windows are currently hidden due to scrolling
+ if (mDeferPluginWindows) {
+ PLUGINS_LOG("[%" PRIu64 "] suppressing", aId);
+ return false;
+ }
+
+ // If the plugin windows were hidden but now are not, we need to force
+ // update the metrics to make sure they are visible again.
+ if (mPluginWindowsHidden) {
+ PLUGINS_LOG("[%" PRIu64 "] re-showing", aId);
+ mPluginWindowsHidden = false;
+ pluginMetricsChanged = true;
+ }
+
+ if (!lts.mPluginData.Length()) {
+ // Don't hide plugins if the previous remote layer tree didn't contain any.
+ if (!mCachedPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId);
+ return false;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ // We will pass through here in cases where the previous shadow layer
+ // tree contained visible plugins and the new tree does not. All we need
+ // to do here is hide the plugins for the old tree, so don't waste time
+ // calculating clipping.
+ mPluginsLayerOffset = nsIntPoint(0,0);
+ mPluginsLayerVisibleRegion.SetEmpty();
+ Unused << lts.mParent->SendHideAllPlugins(parentWidget);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] hide all", aId);
+ } else {
+ // Retrieve the offset and visible region of the layer that hosts
+ // the plugins, CompositorBridgeChild needs these in calculating proper
+ // plugin clipping.
+ LayerTransactionParent* layerTree = lts.mLayerTree;
+ Layer* contentRoot = layerTree->GetRoot();
+ if (contentRoot) {
+ nsIntPoint offset;
+ nsIntRegion visibleRegion;
+ if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion,
+ &offset)) {
+ // Check to see if these values have changed, if so we need to
+ // update plugin window position within the window.
+ if (!pluginMetricsChanged &&
+ mPluginsLayerVisibleRegion == visibleRegion &&
+ mPluginsLayerOffset == offset) {
+ PLUGINS_LOG("[%" PRIu64 "] no change", aId);
+ return false;
+ }
+ mPluginsLayerOffset = offset;
+ mPluginsLayerVisibleRegion = visibleRegion;
+ Unused << lts.mParent->SendUpdatePluginConfigurations(
+ LayoutDeviceIntPoint::FromUnknownPoint(offset),
+ LayoutDeviceIntRegion::FromUnknownRegion(visibleRegion),
+ lts.mPluginData);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] updated", aId);
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId);
+ return false;
+ }
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no content root", aId);
+ return false;
+ }
+ }
+
+ mLastPluginUpdateLayerTreeId = aId;
+ mCachedPluginData = lts.mPluginData;
+ return true;
+}
+
+void
+CompositorBridgeParent::ScheduleShowAllPluginWindows()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ShowAllPluginWindows));
+}
+
+void
+CompositorBridgeParent::ShowAllPluginWindows()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ mDeferPluginWindows = false;
+ ScheduleComposition();
+}
+
+void
+CompositorBridgeParent::ScheduleHideAllPluginWindows()
+{
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::HideAllPluginWindows));
+}
+
+void
+CompositorBridgeParent::HideAllPluginWindows()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ // No plugins in the cache implies no plugins to manage
+ // in this content.
+ if (!mCachedPluginData.Length() || mDeferPluginWindows) {
+ return;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ mDeferPluginWindows = true;
+ mPluginWindowsHidden = true;
+
+#if defined(XP_WIN)
+ // We will get an async reply that this has happened and then send hide.
+ mWaitForPluginsUntil = TimeStamp::Now() + mVsyncRate;
+ Unused << SendCaptureAllPlugins(parentWidget);
+#else
+ Unused << SendHideAllPlugins(parentWidget);
+ ScheduleComposition();
+#endif
+}
+#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+
+bool
+CompositorBridgeParent::RecvAllPluginsCaptured()
+{
+#if defined(XP_WIN)
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ return true;
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected.");
+ return false;
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h
new file mode 100644
index 000000000..58052003f
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -0,0 +1,688 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeParent_h
+#define mozilla_layers_CompositorBridgeParent_h
+
+// Enable this pref to turn on compositor performance warning.
+// This will print warnings if the compositor isn't meeting
+// its responsiveness objectives:
+// 1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
+// 2) Unless a frame was composited within the throttle threshold in
+// which the deadline will be 15ms + throttle threshold
+//#define COMPOSITOR_PERFORMANCE_WARNING
+
+#include <stdint.h> // for uint64_t
+#include "Layers.h" // for Layer
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/CompositorController.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator
+#include "mozilla/layers/LayersMessages.h" // for TargetConfig
+#include "mozilla/layers/MetricsSharingController.h"
+#include "mozilla/layers/PCompositorBridgeParent.h"
+#include "mozilla/layers/APZTestData.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "mozilla/VsyncDispatcher.h"
+
+class MessageLoop;
+class nsIWidget;
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace gfx {
+class DrawTarget;
+class GPUProcessManager;
+class GPUParent;
+} // namespace gfx
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class APZCTreeManager;
+class APZCTreeManagerParent;
+class AsyncCompositionManager;
+class Compositor;
+class CompositorBridgeParent;
+class LayerManagerComposite;
+class LayerTransactionParent;
+class PAPZParent;
+class CrossProcessCompositorBridgeParent;
+class CompositorThreadHolder;
+class InProcessCompositorSession;
+
+struct ScopedLayerTreeRegistration
+{
+ ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
+ uint64_t aLayersId,
+ Layer* aRoot,
+ GeckoContentController* aController);
+ ~ScopedLayerTreeRegistration();
+
+private:
+ uint64_t mLayersId;
+};
+
+/**
+ * Manages the vsync (de)registration and tracking on behalf of the
+ * compositor when it need to paint.
+ * Turns vsync notifications into scheduled composites.
+ **/
+class CompositorVsyncScheduler
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
+
+public:
+ explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
+ widget::CompositorWidget* aWidget);
+
+ bool NotifyVsync(TimeStamp aVsyncTimestamp);
+ void SetNeedsComposite();
+ void OnForceComposeToTarget();
+
+ void ScheduleTask(already_AddRefed<CancelableRunnable>, int);
+ void ResumeComposition();
+ void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+ void PostCompositeTask(TimeStamp aCompositeTimestamp);
+ void Destroy();
+ void ScheduleComposition();
+ void CancelCurrentCompositeTask();
+ bool NeedsComposite();
+ void Composite(TimeStamp aVsyncTimestamp);
+ void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect);
+
+ const TimeStamp& GetLastComposeTime()
+ {
+ return mLastCompose;
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ const TimeStamp& GetExpectedComposeStartTime()
+ {
+ return mExpectedComposeStartTime;
+ }
+#endif
+
+private:
+ virtual ~CompositorVsyncScheduler();
+
+ void NotifyCompositeTaskExecuted();
+ void ObserveVsync();
+ void UnobserveVsync();
+ void DispatchTouchEvents(TimeStamp aVsyncTimestamp);
+ void DispatchVREvents(TimeStamp aVsyncTimestamp);
+ void CancelCurrentSetNeedsCompositeTask();
+
+ class Observer final : public VsyncObserver
+ {
+ public:
+ explicit Observer(CompositorVsyncScheduler* aOwner);
+ virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
+ void Destroy();
+ private:
+ virtual ~Observer();
+
+ Mutex mMutex;
+ // Hold raw pointer to avoid mutual reference.
+ CompositorVsyncScheduler* mOwner;
+ };
+
+ CompositorBridgeParent* mCompositorBridgeParent;
+ TimeStamp mLastCompose;
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeStamp mExpectedComposeStartTime;
+#endif
+
+ bool mAsapScheduling;
+ bool mIsObservingVsync;
+ uint32_t mNeedsComposite;
+ int32_t mVsyncNotificationsSkipped;
+ widget::CompositorWidget* mWidget;
+ RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
+
+ mozilla::Monitor mCurrentCompositeTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentCompositeTask;
+
+ mozilla::Monitor mSetNeedsCompositeMonitor;
+ RefPtr<CancelableRunnable> mSetNeedsCompositeTask;
+};
+
+class CompositorBridgeParentBase : public PCompositorBridgeParent,
+ public HostIPCAllocator,
+ public ShmemAllocator,
+ public MetricsSharingController
+{
+public:
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate) = 0;
+
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) { return nullptr; }
+
+ virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { }
+
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) { }
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) { return true; }
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { }
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) { }
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+ virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual bool RecvSyncWithCompositor() override { return true; }
+
+ // HostIPCAllocator
+ virtual base::ProcessId GetChildProcessId() override;
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ShmemAllocator
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // MetricsSharingController
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return HostIPCAllocator::AddRef(); }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { return HostIPCAllocator::Release(); }
+ base::ProcessId RemotePid() override;
+ bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ uint64_t aLayersId,
+ uint32_t aApzcId) override;
+ bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+ uint32_t aApzcId) override;
+};
+
+class CompositorBridgeParent final : public CompositorBridgeParentBase
+ , public CompositorController
+{
+ friend class CompositorVsyncScheduler;
+ friend class CompositorThreadHolder;
+ friend class InProcessCompositorSession;
+ friend class gfx::GPUProcessManager;
+ friend class gfx::GPUParent;
+
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return CompositorBridgeParentBase::AddRef(); }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { return CompositorBridgeParentBase::Release(); }
+
+ explicit CompositorBridgeParent(CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ // Must only be called by CompositorBridgeChild. After invoking this, the
+ // IPC channel is active and RecvWillStop/ActorDestroy must be called to
+ // free the compositor.
+ void InitSameProcess(widget::CompositorWidget* aWidget,
+ const uint64_t& aLayerTreeId,
+ bool aUseAPZ);
+
+ // Must only be called by GPUParent. After invoking this, the IPC channel
+ // is active and RecvWillStop/ActorDestroy must be called to free the
+ // compositor.
+ bool Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint);
+
+ virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override;
+ virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override;
+ virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override;
+ virtual bool RecvRequestOverfill() override;
+ virtual bool RecvWillClose() override;
+ virtual bool RecvPause() override;
+ virtual bool RecvResume() override;
+ virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
+ virtual bool RecvNotifyChildRecreated(const uint64_t& child) override;
+ virtual bool RecvAdoptChild(const uint64_t& child) override;
+ virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override;
+ virtual bool RecvFlushRendering() override;
+ virtual bool RecvForcePresent() override;
+
+ virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override {
+ MOZ_ASSERT_UNREACHABLE("This message is only sent cross-process");
+ return true;
+ }
+
+ virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override;
+ virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
+ virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override;
+
+ // Unused for chrome <-> compositor communication (which this class does).
+ // @see CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint
+ virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; };
+
+ virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId) override;
+ void ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const Maybe<uint32_t>& aPresShellId);
+ virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion) override;
+
+ virtual bool RecvAllPluginsCaptured() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t aPaintSyncId,
+ bool aHitTestUpdate) override;
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) override;
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) override;
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override;
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+ override;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) override;
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) override { return mCompositionManager; }
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ virtual bool IsSameProcess() const override;
+
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
+ bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
+
+ /**
+ * Request that the compositor be recreated due to a shared device reset.
+ * This must be called on the main thread, and blocks until a task posted
+ * to the compositor thread has completed.
+ *
+ * Note that this posts a task directly, rather than using synchronous
+ * IPDL, and waits on a monitor notification from the compositor thread.
+ * We do this as a best-effort attempt to jump any IPDL messages that
+ * have not yet been posted (and are sitting around in the IO pipe), to
+ * minimize the amount of time the main thread is blocked.
+ */
+ bool ResetCompositor(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier);
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint();
+
+ static void SetShadowProperties(Layer* aLayer);
+
+ void NotifyChildCreated(uint64_t aChild);
+
+ void AsyncRender();
+
+ // Can be called from any thread
+ void ScheduleRenderOnCompositorThread() override;
+ void SchedulePauseOnCompositorThread();
+ void InvalidateOnCompositorThread();
+ /**
+ * Returns true if a surface was obtained and the resume succeeded; false
+ * otherwise.
+ */
+ bool ScheduleResumeOnCompositorThread();
+ bool ScheduleResumeOnCompositorThread(int width, int height);
+
+ virtual void ScheduleComposition();
+ void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, bool aHitTestUpdate);
+
+ void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) override;
+
+ /**
+ * Check rotation info and schedule a rendering task if needed.
+ * Only can be called from compositor thread.
+ */
+ void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, bool aIsFirstPaint);
+
+ /**
+ * Returns the unique layer tree identifier that corresponds to the root
+ * tree of this compositor.
+ */
+ uint64_t RootLayerTreeId();
+
+ /**
+ * Notify local and remote layer trees connected to this compositor that
+ * the compositor's local device is being reset. All layers must be
+ * invalidated to clear any cached TextureSources.
+ *
+ * This must be called on the compositor thread.
+ */
+ void InvalidateRemoteLayers();
+
+ /**
+ * Returns a pointer to the CompositorBridgeParent corresponding to the given ID.
+ */
+ static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id);
+
+ /**
+ * Notify the compositor for the given layer tree that vsync has occurred.
+ */
+ static void NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId);
+
+ /**
+ * Set aController as the pan/zoom callback for the subtree referred
+ * to by aLayersId.
+ *
+ * Must run on content main thread.
+ */
+ static void SetControllerForLayerTree(uint64_t aLayersId,
+ GeckoContentController* aController);
+
+ /**
+ * A new child process has been configured to push transactions
+ * directly to us. Transport is to its thread context.
+ */
+ static bool
+ CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint);
+
+ struct LayerTreeState {
+ LayerTreeState();
+ ~LayerTreeState();
+ RefPtr<Layer> mRoot;
+ RefPtr<GeckoContentController> mController;
+ APZCTreeManagerParent* mApzcTreeManagerParent;
+ CompositorBridgeParent* mParent;
+ LayerManagerComposite* mLayerManager;
+ // Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share
+ // their FrameMetrics with the corresponding child process that holds
+ // the PCompositorBridgeChild
+ CrossProcessCompositorBridgeParent* mCrossProcessParent;
+ TargetConfig mTargetConfig;
+ APZTestData mApzTestData;
+ LayerTransactionParent* mLayerTree;
+ nsTArray<PluginWindowData> mPluginData;
+ bool mUpdatedPluginDataAvailable;
+
+ // Number of times the compositor has been reset without having been
+ // acknowledged by the child.
+ uint32_t mPendingCompositorUpdates;
+
+ CompositorController* GetCompositorController() const;
+ MetricsSharingController* CrossProcessSharingController() const;
+ MetricsSharingController* InProcessSharingController() const;
+ };
+
+ /**
+ * Lookup the indirect shadow tree for |aId| and return it if it
+ * exists. Otherwise null is returned. This must only be called on
+ * the compositor thread.
+ */
+ static LayerTreeState* GetIndirectShadowTree(uint64_t aId);
+
+ /**
+ * Given the layers id for a content process, get the APZCTreeManagerParent
+ * for the corresponding *root* layers id. That is, the APZCTreeManagerParent,
+ * if one is found, will always be connected to the parent process rather
+ * than a content process. Note that unless the compositor process is
+ * separated this is expected to return null, because if the compositor is
+ * living in the gecko parent process then there is no APZCTreeManagerParent
+ * for the parent process.
+ */
+ static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot(
+ uint64_t aContentLayersId);
+ /**
+ * Same as the GetApzcTreeManagerParentForRoot function, but returns
+ * the GeckoContentController for the parent process.
+ */
+ static GeckoContentController* GetGeckoContentControllerForRoot(
+ uint64_t aContentLayersId);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ /**
+ * Calculates and requests the main thread update plugin positioning, clip,
+ * and visibility via ipc.
+ */
+ bool UpdatePluginWindowState(uint64_t aId);
+
+ /**
+ * Plugin visibility helpers for the apz (main thread) and compositor
+ * thread.
+ */
+ void ScheduleShowAllPluginWindows() override;
+ void ScheduleHideAllPluginWindows() override;
+ void ShowAllPluginWindows();
+ void HideAllPluginWindows();
+#else
+ void ScheduleShowAllPluginWindows() override {}
+ void ScheduleHideAllPluginWindows() override {}
+#endif
+
+ /**
+ * Main thread response for a plugin visibility request made by the
+ * compositor thread.
+ */
+ virtual bool RecvRemotePluginsReady() override;
+
+ /**
+ * Used by the profiler to denote when a vsync occured
+ */
+ static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
+
+ widget::CompositorWidget* GetWidget() { return mWidget; }
+
+ void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override;
+
+ RefPtr<APZCTreeManager> GetAPZCTreeManager();
+
+ bool AsyncPanZoomEnabled() const {
+ return !!mApzcTreeManager;
+ }
+
+private:
+
+ void Initialize();
+
+ /**
+ * Called during destruction in order to release resources as early as possible.
+ */
+ void StopAndClearResources();
+
+ /**
+ * This returns a reference to the APZCTreeManager to which
+ * pan/zoom-related events can be sent.
+ */
+ static already_AddRefed<APZCTreeManager> GetAPZCTreeManager(uint64_t aLayersId);
+
+ /**
+ * Release compositor-thread resources referred to by |aID|.
+ *
+ * Must run on the content main thread.
+ */
+ static void DeallocateLayerTreeId(uint64_t aId);
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ virtual PLayerTransactionParent*
+ AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool* aSuccess) override;
+ virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
+ virtual void ScheduleTask(already_AddRefed<CancelableRunnable>, int);
+ void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+
+ void SetEGLSurfaceSize(int width, int height);
+
+ void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
+ void PauseComposition();
+ void ResumeComposition();
+ void ResumeCompositionAndResize(int width, int height);
+ void ForceComposition();
+ void CancelCurrentCompositeTask();
+ void Invalidate();
+ bool IsPendingComposite();
+ void FinishPendingComposite();
+
+ RefPtr<Compositor> NewCompositor(const nsTArray<LayersBackend>& aBackendHints);
+ void ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints,
+ Maybe<TextureFactoryIdentifier>* aOutNewIdentifier);
+ Maybe<TextureFactoryIdentifier> ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints);
+
+ /**
+ * Add a compositor to the global compositor map.
+ */
+ static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
+ /**
+ * Remove a compositor from the global compositor map.
+ */
+ static CompositorBridgeParent* RemoveCompositor(uint64_t id);
+
+ /**
+ * Creates the global compositor map.
+ */
+ static void Setup();
+
+ /**
+ * Destroys the compositor thread and global compositor map.
+ */
+ static void Shutdown();
+
+ /**
+ * Finish the shutdown operation on the compositor thread.
+ */
+ static void FinishShutdown();
+
+ /**
+ * Return true if current state allows compositing, that is
+ * finishing a layers transaction.
+ */
+ bool CanComposite();
+
+ void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
+ template <typename Lambda>
+ inline void ForEachIndirectLayerTree(const Lambda& aCallback);
+
+ RefPtr<LayerManagerComposite> mLayerManager;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<AsyncCompositionManager> mCompositionManager;
+ widget::CompositorWidget* mWidget;
+ TimeStamp mTestTime;
+ CSSToLayoutDeviceScale mScale;
+ TimeDuration mVsyncRate;
+ bool mIsTesting;
+
+ uint64_t mPendingTransaction;
+
+ bool mPaused;
+
+ bool mUseExternalSurfaceSize;
+ gfx::IntSize mEGLSurfaceSize;
+
+ mozilla::Monitor mPauseCompositionMonitor;
+ mozilla::Monitor mResumeCompositionMonitor;
+ mozilla::Monitor mResetCompositorMonitor;
+
+ uint64_t mCompositorID;
+ uint64_t mRootLayerTreeID;
+
+ bool mOverrideComposeReadiness;
+ RefPtr<CancelableRunnable> mForceCompositionTask;
+
+ RefPtr<APZCTreeManager> mApzcTreeManager;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+ RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
+ // This makes sure the compositorParent is not destroyed before receiving
+ // confirmation that the channel is closed.
+ // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
+ RefPtr<CompositorBridgeParent> mSelfRef;
+
+ TimeDuration mPaintTime;
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // cached plugin data used to reduce the number of updates we request.
+ uint64_t mLastPluginUpdateLayerTreeId;
+ nsIntPoint mPluginsLayerOffset;
+ nsIntRegion mPluginsLayerVisibleRegion;
+ nsTArray<PluginWindowData> mCachedPluginData;
+ // Time until which we will block composition to wait for plugin updates.
+ TimeStamp mWaitForPluginsUntil;
+ // Indicates that we have actually blocked a composition waiting for plugins.
+ bool mHaveBlockedForPlugins = false;
+ // indicates if plugin window visibility and metric updates are currently
+ // being defered due to a scroll operation.
+ bool mDeferPluginWindows;
+ // indicates if the plugin windows were hidden, and need to be made
+ // visible again even if their geometry has not changed.
+ bool mPluginWindowsHidden;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeParent);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBridgeParent_h
diff --git a/gfx/layers/ipc/CompositorThread.cpp b/gfx/layers/ipc/CompositorThread.cpp
new file mode 100644
index 000000000..789a5d5d3
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=99 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorThread.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "CompositorBridgeParent.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/media/MediaSystemResourceService.h"
+
+namespace mozilla {
+
+namespace gfx {
+// See VRManagerChild.cpp
+void ReleaseVRManagerParentSingleton();
+} // namespace gfx
+
+namespace layers {
+
+static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
+static bool sFinishedCompositorShutDown = false;
+
+// See ImageBridgeChild.cpp
+void ReleaseImageBridgeParentSingleton();
+
+CompositorThreadHolder* GetCompositorThreadHolder()
+{
+ return sCompositorThreadHolder;
+}
+
+base::Thread*
+CompositorThread()
+{
+ return sCompositorThreadHolder
+ ? sCompositorThreadHolder->GetCompositorThread()
+ : nullptr;
+}
+
+/* static */ MessageLoop*
+CompositorThreadHolder::Loop()
+{
+ return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
+}
+
+CompositorThreadHolder*
+CompositorThreadHolder::GetSingleton()
+{
+ return sCompositorThreadHolder;
+}
+
+CompositorThreadHolder::CompositorThreadHolder()
+ : mCompositorThread(CreateCompositorThread())
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_CTOR(CompositorThreadHolder);
+}
+
+CompositorThreadHolder::~CompositorThreadHolder()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_COUNT_DTOR(CompositorThreadHolder);
+
+ DestroyCompositorThread(mCompositorThread);
+}
+
+/* static */ void
+CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
+
+ CompositorBridgeParent::Shutdown();
+ delete aCompositorThread;
+ sFinishedCompositorShutDown = true;
+}
+
+/* static */ base::Thread*
+CompositorThreadHolder::CreateCompositorThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+ base::Thread* compositorThread = new base::Thread("Compositor");
+
+ base::Thread::Options options;
+ /* Timeout values are powers-of-two to enable us get better data.
+ 128ms is chosen for transient hangs because 8Hz should be the minimally
+ acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
+ options.transient_hang_timeout = 128; // milliseconds
+ /* 2048ms is chosen for permanent hangs because it's longer than most
+ * Compositor hangs seen in the wild, but is short enough to not miss getting
+ * native hang stacks. */
+ options.permanent_hang_timeout = 2048; // milliseconds
+#if defined(_WIN32)
+ /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As
+ * such the thread is a gui thread, and must process a windows message queue or
+ * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */
+ options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+
+ if (!compositorThread->StartWithOptions(options)) {
+ delete compositorThread;
+ return nullptr;
+ }
+
+ CompositorBridgeParent::Setup();
+ ImageBridgeParent::Setup();
+
+ return compositorThread;
+}
+
+void
+CompositorThreadHolder::Start()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+ sCompositorThreadHolder = new CompositorThreadHolder();
+}
+
+void
+CompositorThreadHolder::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
+
+ ReleaseImageBridgeParentSingleton();
+ gfx::ReleaseVRManagerParentSingleton();
+ MediaSystemResourceService::Shutdown();
+
+ sCompositorThreadHolder = nullptr;
+
+ // No locking is needed around sFinishedCompositorShutDown because it is only
+ // ever accessed on the main thread.
+ while (!sFinishedCompositorShutDown) {
+ NS_ProcessNextEvent(nullptr, true);
+ }
+
+ CompositorBridgeParent::FinishShutdown();
+}
+
+/* static */ bool
+CompositorThreadHolder::IsInCompositorThread()
+{
+ return CompositorThread() &&
+ CompositorThread()->thread_id() == PlatformThread::CurrentId();
+}
+
+} // namespace mozilla
+} // namespace layers
diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h
new file mode 100644
index 000000000..cc47f5fa2
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=99 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_layers_CompositorThread_h
+#define mozilla_layers_CompositorThread_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "base/platform_thread.h" // for PlatformThreadId
+#include "base/thread.h" // for Thread
+#include "base/message_loop.h"
+#include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorThreadHolder final
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder)
+
+public:
+ CompositorThreadHolder();
+
+ base::Thread* GetCompositorThread() const {
+ return mCompositorThread;
+ }
+
+ static CompositorThreadHolder* GetSingleton();
+
+ static bool IsActive() {
+ return !!GetSingleton();
+ }
+
+ /**
+ * Creates the compositor thread and the global compositor map.
+ */
+ static void Start();
+
+ /*
+ * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and
+ * releases compositor-thread owned resources.
+ */
+ static void Shutdown();
+
+ static MessageLoop* Loop();
+
+ // Returns true if the calling thread is the compositor thread.
+ static bool IsInCompositorThread();
+
+private:
+ ~CompositorThreadHolder();
+
+ base::Thread* const mCompositorThread;
+
+ static base::Thread* CreateCompositorThread();
+ static void DestroyCompositorThread(base::Thread* aCompositorThread);
+
+ friend class CompositorBridgeParent;
+};
+
+base::Thread* CompositorThread();
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorThread_h
diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
new file mode 100644
index 000000000..1bb6d046b
--- /dev/null
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "VRManager.h" // for VRManager
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_WIDGET_GTK
+#include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#ifdef XP_WIN
+#include "mozilla/layers/CompositorD3D11.h"
+#include "mozilla/layers/CompositorD3D9.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h"
+#endif
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+
+namespace layers {
+
+// defined in CompositorBridgeParent.cpp
+typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+extern LayerTreeMap sIndirectLayerTrees;
+extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+bool
+CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint()
+{
+ mNotifyAfterRemotePaint = true;
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // We must keep this object alive untill the code handling message
+ // reception is finished on this thread.
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy));
+}
+
+PLayerTransactionParent*
+CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>&,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess)
+{
+ MOZ_ASSERT(aId != 0);
+
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPLayerTransactionParent; dropping message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ if (state && state->mLayerManager) {
+ state->mCrossProcessParent = this;
+ LayerManagerComposite* lm = state->mLayerManager;
+ *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
+ *aSuccess = true;
+ LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
+ p->AddIPDLReference();
+ sIndirectLayerTrees[aId].mLayerTree = p;
+ p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates);
+ return p;
+ }
+
+ NS_WARNING("Created child without a matching parent?");
+ *aSuccess = false;
+ LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers)
+{
+ LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers);
+ EraseLayerState(slp->GetId());
+ static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in RecvAsyncPanZoomEnabled; dropping message...");
+ return false;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ *aHasAPZ = state.mParent ? state.mParent->AsyncPanZoomEnabled() : false;
+ return true;
+}
+
+PAPZCTreeManagerParent*
+CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+ MOZ_ASSERT(state.mParent);
+ MOZ_ASSERT(!state.mApzcTreeManagerParent);
+ state.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, state.mParent->GetAPZCTreeManager());
+
+ return state.mApzcTreeManagerParent;
+}
+bool
+CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
+{
+ APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto iter = sIndirectLayerTrees.find(parent->LayersId());
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent::LayerTreeState& state = iter->second;
+ MOZ_ASSERT(state.mApzcTreeManagerParent == parent);
+ state.mApzcTreeManagerParent = nullptr;
+ }
+
+ delete parent;
+
+ return true;
+}
+
+PAPZParent*
+CrossProcessCompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
+{
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPAPZParent; dropping message...");
+ return nullptr;
+ }
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+ MOZ_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
+{
+ RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
+ it != sIndirectLayerTrees.end(); it++) {
+ CompositorBridgeParent::LayerTreeState* lts = &it->second;
+ if (lts->mParent && lts->mCrossProcessParent == this) {
+ lts->mParent->NotifyChildCreated(child);
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CrossProcessCompositorBridgeParent::ShadowLayersUpdated(
+ LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t /*aPaintSyncId: unused*/,
+ bool aHitTestUpdate)
+{
+ uint64_t id = aLayerTree->GetId();
+
+ MOZ_ASSERT(id != 0);
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
+
+ Layer* shadowRoot = aLayerTree->GetRoot();
+ if (shadowRoot) {
+ CompositorBridgeParent::SetShadowProperties(shadowRoot);
+ }
+ UpdateIndirectTree(id, shadowRoot, aTargetConfig);
+
+ // Cache the plugin data for this remote layer tree
+ state->mPluginData = aPlugins;
+ state->mUpdatedPluginDataAvailable = true;
+
+ state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction, aHitTestUpdate);
+
+ // Send the 'remote paint ready' message to the content thread if it has already asked.
+ if(mNotifyAfterRemotePaint) {
+ Unused << SendRemotePaintIsReady();
+ mNotifyAfterRemotePaint = false;
+ }
+
+ if (aLayerTree->ShouldParentObserveEpoch()) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), true);
+ }
+
+ aLayerTree->SetPendingTransactionId(aTransactionId);
+}
+
+void
+CrossProcessCompositorBridgeParent::DidComposite(
+ uint64_t aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd)
+{
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) {
+ Unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd);
+ layerTree->SetPendingTransactionId(0);
+ }
+}
+
+void
+CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[id].mParent;
+ }
+ if (parent) {
+ parent->ForceComposite(aLayerTree);
+ }
+}
+
+void
+CrossProcessCompositorBridgeParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (state && state->mParent) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), false);
+ }
+}
+
+bool
+CrossProcessCompositorBridgeParent::SetTestSampleTime(
+ LayerTransactionParent* aLayerTree, const TimeStamp& aTime)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return false;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->SetTestSampleTime(aLayerTree, aTime);
+}
+
+void
+CrossProcessCompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->LeaveTestMode(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::ApplyAsyncProperties(
+ LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ApplyAsyncProperties(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->FlushApzRepaints(aLayerTree);
+}
+
+void
+CrossProcessCompositorBridgeParent::GetAPZTestData(
+ const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ *aOutData = sIndirectLayerTrees[id].mApzTestData;
+}
+
+void
+CrossProcessCompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->SetConfirmedTargetAPZC(aLayerTree, aInputBlockId, aTargets);
+}
+
+AsyncCompositionManager*
+CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent* aLayerTree)
+{
+ uint64_t id = aLayerTree->GetId();
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->GetCompositionManager(aLayerTree);
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId)
+{
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+ if (LayerTransactionParent* ltp = state.mLayerTree) {
+ ltp->AcknowledgeCompositorUpdate();
+ }
+ MOZ_ASSERT(state.mPendingCompositorUpdates > 0);
+ state.mPendingCompositorUpdates--;
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::DeferredDestroy()
+{
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+CrossProcessCompositorBridgeParent::~CrossProcessCompositorBridgeParent()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop());
+ MOZ_ASSERT(IToplevelProtocol::GetTransport());
+}
+
+PTextureParent*
+CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial)
+{
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ TextureFlags flags = aFlags;
+
+ if (!state || state->mPendingCompositorUpdates) {
+ // The compositor was recreated, and we're receiving layers updates for a
+ // a layer manager that will soon be discarded or invalidated. We can't
+ // return null because this will mess up deserialization later and we'll
+ // kill the content process. Instead, we signal that the underlying
+ // TextureHost should not attempt to access the compositor.
+ flags |= TextureFlags::INVALID_COMPOSITOR;
+ } else if (state->mLayerManager && state->mLayerManager->GetCompositor() &&
+ aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) {
+ gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong";
+ }
+
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool
+CrossProcessCompositorBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId)
+{
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[aLayersId].mParent;
+ }
+ if (parent) {
+ parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+ }
+ return true;
+}
+
+bool
+CrossProcessCompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion)
+{
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[aGuid.mLayersId].mParent;
+ }
+ if (parent) {
+ return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+ }
+ return true;
+}
+
+void
+CrossProcessCompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime)
+{
+ uint64_t id = aLayerTree->GetId();
+ MOZ_ASSERT(id != 0);
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->UpdatePaintTime(aLayerTree, aPaintTime);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
new file mode 100644
index 000000000..399969950
--- /dev/null
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CrossProcessCompositorBridgeParent_h
+#define mozilla_layers_CrossProcessCompositorBridgeParent_h
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class handles layer updates pushed directly from child processes to
+ * the compositor thread. It's associated with a CompositorBridgeParent on the
+ * compositor thread. While it uses the PCompositorBridge protocol to manage
+ * these updates, it doesn't actually drive compositing itself. For that it
+ * hands off work to the CompositorBridgeParent it's associated with.
+ */
+class CrossProcessCompositorBridgeParent final : public CompositorBridgeParentBase
+{
+ friend class CompositorBridgeParent;
+
+public:
+ explicit CrossProcessCompositorBridgeParent()
+ : mNotifyAfterRemotePaint(false)
+ , mDestroyCalled(false)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ void Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+ mSelfRef = this;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // FIXME/bug 774388: work out what shutdown protocol we need.
+ virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override { return false; }
+ virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override { return false; }
+ virtual bool RecvRequestOverfill() override { return true; }
+ virtual bool RecvWillClose() override { return true; }
+ virtual bool RecvPause() override { return true; }
+ virtual bool RecvResume() override { return true; }
+ virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
+ virtual bool RecvNotifyChildRecreated(const uint64_t& child) override { return false; }
+ virtual bool RecvAdoptChild(const uint64_t& child) override { return false; }
+ virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override
+ { return true; }
+ virtual bool RecvFlushRendering() override { return true; }
+ virtual bool RecvForcePresent() override { return true; }
+ virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
+ virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
+ virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override { return true; }
+
+ virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+ const uint32_t& aPresShellId) override;
+
+ virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+ const CSSIntRegion& aRegion) override;
+
+ virtual bool RecvAllPluginsCaptured() override { return true; }
+
+ virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override
+ {
+ // Don't support calculating frame uniformity on the child process and
+ // this is just a stub for now.
+ MOZ_ASSERT(false);
+ return true;
+ }
+
+ /**
+ * Tells this CompositorBridgeParent to send a message when the compositor has received the transaction.
+ */
+ virtual bool RecvRequestNotifyAfterRemotePaint() override;
+
+ virtual PLayerTransactionParent*
+ AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
+ const uint64_t& aId,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ bool *aSuccess) override;
+
+ virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
+
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const uint64_t& aTransactionId,
+ const TargetConfig& aTargetConfig,
+ const InfallibleTArray<PluginWindowData>& aPlugins,
+ bool aIsFirstPaint,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ int32_t /*aPaintSyncId: unused*/,
+ bool aHitTestUpdate) override;
+ virtual void ForceComposite(LayerTransactionParent* aLayerTree) override;
+ virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
+ virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
+ const TimeStamp& aTime) override;
+ virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override;
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
+ override;
+ virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override;
+ virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
+ APZTestData* aOutData) override;
+ virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
+ const uint64_t& aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
+ virtual bool RecvRemotePluginsReady() override { return false; }
+ virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override;
+
+ void DidComposite(uint64_t aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd);
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aId,
+ const uint64_t& aSerial) override;
+
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ virtual bool IsSameProcess() const override;
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override {
+ // Not allowed.
+ return nullptr;
+ }
+ bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override {
+ // Not allowed.
+ return false;
+ }
+
+ virtual bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override;
+
+ virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override;
+ virtual bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ virtual PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
+ virtual bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override;
+
+protected:
+ void OnChannelConnected(int32_t pid) override {
+ mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+ }
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CrossProcessCompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ // There can be many CPCPs, and IPDL-generated code doesn't hold a
+ // reference to top-level actors. So we hold a reference to
+ // ourself. This is released (deferred) in ActorDestroy().
+ RefPtr<CrossProcessCompositorBridgeParent> mSelfRef;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+ // If true, we should send a RemotePaintIsReady message when the layer transaction
+ // is received
+ bool mNotifyAfterRemotePaint;
+ bool mDestroyCalled;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CrossProcessCompositorBridgeParent_h
diff --git a/gfx/layers/ipc/GonkNativeHandle.cpp b/gfx/layers/ipc/GonkNativeHandle.cpp
new file mode 100644
index 000000000..8f808e623
--- /dev/null
+++ b/gfx/layers/ipc/GonkNativeHandle.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <unistd.h>
+
+#include "GonkNativeHandle.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+namespace layers {
+
+GonkNativeHandle::GonkNativeHandle()
+ : mNhObj(new NhObj())
+{
+}
+
+GonkNativeHandle::GonkNativeHandle(NhObj* aNhObj)
+ : mNhObj(aNhObj)
+{
+ MOZ_ASSERT(aNhObj);
+}
+
+
+void
+GonkNativeHandle::TransferToAnother(GonkNativeHandle& aHandle)
+{
+ aHandle.mNhObj = this->GetAndResetNhObj();
+}
+
+already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::GetAndResetNhObj()
+{
+ RefPtr<NhObj> nhObj = mNhObj;
+ mNhObj = new NhObj();
+ return nhObj.forget();
+}
+
+already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::GetDupNhObj()
+{
+ if (!IsValid()) {
+ return GonkNativeHandle::CreateDupNhObj(nullptr);
+ }
+ return GonkNativeHandle::CreateDupNhObj(mNhObj->mHandle);
+}
+
+/* static */ already_AddRefed<GonkNativeHandle::NhObj>
+GonkNativeHandle::CreateDupNhObj(native_handle_t* aHandle)
+{
+ RefPtr<NhObj> nhObj;
+ if (aHandle) {
+ native_handle* nativeHandle =
+ native_handle_create(aHandle->numFds, aHandle->numInts);
+ if (!nativeHandle) {
+ nhObj = new GonkNativeHandle::NhObj();
+ return nhObj.forget();
+ }
+
+ for (int i = 0; i < aHandle->numFds; ++i) {
+ nativeHandle->data[i] = dup(aHandle->data[i]);
+ }
+
+ memcpy(nativeHandle->data + nativeHandle->numFds,
+ aHandle->data + aHandle->numFds,
+ sizeof(int) * aHandle->numInts);
+
+ nhObj = new GonkNativeHandle::NhObj(nativeHandle);
+ } else {
+ nhObj = new GonkNativeHandle::NhObj();
+ }
+ return nhObj.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/GonkNativeHandle.h b/gfx/layers/ipc/GonkNativeHandle.h
new file mode 100644
index 000000000..6afaea540
--- /dev/null
+++ b/gfx/layers/ipc/GonkNativeHandle.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_GonkNativeHandle_h
+#define IPC_GonkNativeHandle_h
+
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace layers {
+
+struct GonkNativeHandle {
+ bool operator==(const GonkNativeHandle&) const { return false; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // IPC_GonkNativeHandle_h
diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.cpp b/gfx/layers/ipc/GonkNativeHandleUtils.cpp
new file mode 100644
index 000000000..fb2efecb3
--- /dev/null
+++ b/gfx/layers/ipc/GonkNativeHandleUtils.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GonkNativeHandleUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla::layers;
+
+namespace IPC {
+
+namespace {
+
+class native_handle_Delete
+{
+public:
+ void operator()(native_handle* aNativeHandle) const
+ {
+ native_handle_close(aNativeHandle); // closes file descriptors
+ native_handle_delete(aNativeHandle);
+ }
+};
+
+} // anonymous namespace
+
+void
+ParamTraits<GonkNativeHandle>::Write(Message* aMsg,
+ const paramType& aParam)
+{
+ GonkNativeHandle handle = aParam;
+ MOZ_ASSERT(handle.IsValid());
+
+ RefPtr<GonkNativeHandle::NhObj> nhObj = handle.GetAndResetNhObj();
+ native_handle_t* nativeHandle = nhObj->GetAndResetNativeHandle();
+
+ size_t nbytes = nativeHandle->numInts * sizeof(int);
+ aMsg->WriteSize(nbytes);
+ aMsg->WriteBytes((nativeHandle->data + nativeHandle->numFds), nbytes);
+
+ for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) {
+ aMsg->WriteFileDescriptor(base::FileDescriptor(nativeHandle->data[i], true));
+ }
+}
+
+bool
+ParamTraits<GonkNativeHandle>::Read(const Message* aMsg,
+ PickleIterator* aIter, paramType* aResult)
+{
+ size_t nbytes;
+ if (!aMsg->ReadSize(aIter, &nbytes)) {
+ return false;
+ }
+
+ if (nbytes % sizeof(int) != 0) {
+ return false;
+ }
+
+ size_t numInts = nbytes / sizeof(int);
+ size_t numFds = aMsg->num_fds();
+ mozilla::UniquePtr<native_handle, native_handle_Delete> nativeHandle(
+ native_handle_create(numFds, numInts));
+ if (!nativeHandle) {
+ return false;
+ }
+
+ auto data =
+ reinterpret_cast<char*>(nativeHandle->data + nativeHandle->numFds);
+ if (!aMsg->ReadBytesInto(aIter, data, nbytes)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < numFds; ++i) {
+ base::FileDescriptor fd;
+ if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
+ return false;
+ }
+ nativeHandle->data[i] = fd.fd;
+ nativeHandle->numFds = i + 1; // set number of valid file descriptors
+ }
+
+ GonkNativeHandle handle(new GonkNativeHandle::NhObj(nativeHandle.get()));
+ handle.TransferToAnother(*aResult);
+
+ mozilla::Unused << nativeHandle.release();
+
+ return true;
+}
+
+} // namespace IPC
diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.h b/gfx/layers/ipc/GonkNativeHandleUtils.h
new file mode 100644
index 000000000..d91792c95
--- /dev/null
+++ b/gfx/layers/ipc/GonkNativeHandleUtils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_GonkNativeHandleUtils_h
+#define IPC_GonkNativeHandleUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "GonkNativeHandle.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::GonkNativeHandle> {
+ typedef mozilla::layers::GonkNativeHandle paramType;
+ static void Write(Message*, const paramType&) {}
+ static bool Read(const Message*, PickleIterator*, paramType*) { return false; }
+};
+
+} // namespace IPC
+
+#endif // IPC_GonkNativeHandleUtils_h
diff --git a/gfx/layers/ipc/ISurfaceAllocator.cpp b/gfx/layers/ipc/ISurfaceAllocator.cpp
new file mode 100644
index 000000000..57da4d9cd
--- /dev/null
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ISurfaceAllocator.h"
+
+#include "gfxPrefs.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
+
+mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType()
+{
+ return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+}
+
+void
+HostIPCAllocator::SendPendingAsyncMessages()
+{
+ if (mPendingAsyncMessage.empty()) {
+ return;
+ }
+
+ // Some type of AsyncParentMessageData message could have
+ // one file descriptor (e.g. OpDeliverFence).
+ // A number of file descriptors per gecko ipc message have a limitation
+ // on OS_POSIX (MACOSX or LINUX).
+#if defined(OS_POSIX)
+ static const uint32_t kMaxMessageNumber = FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+ // default number that works everywhere else
+ static const uint32_t kMaxMessageNumber = 250;
+#endif
+
+ InfallibleTArray<AsyncParentMessageData> messages;
+ messages.SetCapacity(mPendingAsyncMessage.size());
+ for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) {
+ messages.AppendElement(mPendingAsyncMessage[i]);
+ // Limit maximum number of messages.
+ if (messages.Length() >= kMaxMessageNumber) {
+ SendAsyncMessage(messages);
+ // Initialize Messages.
+ messages.Clear();
+ }
+ }
+
+ if (messages.Length() > 0) {
+ SendAsyncMessage(messages);
+ }
+ mPendingAsyncMessage.clear();
+}
+
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+
+#ifdef DEBUG
+const uint32_t sSupportedBlockSize = 4;
+#endif
+
+FixedSizeSmallShmemSectionAllocator::FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider)
+: mShmProvider(aShmProvider)
+{
+ MOZ_ASSERT(mShmProvider);
+}
+
+FixedSizeSmallShmemSectionAllocator::~FixedSizeSmallShmemSectionAllocator()
+{
+ ShrinkShmemSectionHeap();
+}
+
+bool
+FixedSizeSmallShmemSectionAllocator::IPCOpen() const
+{
+ return mShmProvider->IPCOpen();
+}
+
+bool
+FixedSizeSmallShmemSectionAllocator::AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection)
+{
+ // For now we only support sizes of 4. If we want to support different sizes
+ // some more complicated bookkeeping should be added.
+ MOZ_ASSERT(aSize == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection);
+
+ if (!IPCOpen()) {
+ gfxCriticalError() << "Attempt to allocate a ShmemSection after shutdown.";
+ return false;
+ }
+
+ uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+ for (size_t i = 0; i < mUsedShmems.size(); i++) {
+ ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if ((header->mAllocatedBlocks + 1) * allocationSize + sizeof(ShmemSectionHeapHeader) < sShmemPageSize) {
+ aShmemSection->shmem() = mUsedShmems[i];
+ MOZ_ASSERT(mUsedShmems[i].IsWritable());
+ break;
+ }
+ }
+
+ if (!aShmemSection->shmem().IsWritable()) {
+ ipc::Shmem tmp;
+ if (!mShmProvider->AllocUnsafeShmem(sShmemPageSize, OptimalShmemType(), &tmp)) {
+ return false;
+ }
+
+ ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+ header->mTotalBlocks = 0;
+ header->mAllocatedBlocks = 0;
+
+ mUsedShmems.push_back(tmp);
+ aShmemSection->shmem() = tmp;
+ }
+
+ MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+ ShmemSectionHeapHeader* header = aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+ uint8_t* heap = aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+ ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+ if (header->mTotalBlocks > header->mAllocatedBlocks) {
+ // Search for the first available block.
+ for (size_t i = 0; i < header->mTotalBlocks; i++) {
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+ if (allocHeader->mStatus == STATUS_FREED) {
+ break;
+ }
+ heap += allocationSize;
+ }
+ MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+ MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+ } else {
+ heap += header->mTotalBlocks * allocationSize;
+
+ header->mTotalBlocks++;
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+ allocHeader->mSize = aSize;
+ }
+
+ MOZ_ASSERT(allocHeader);
+ header->mAllocatedBlocks++;
+ allocHeader->mStatus = STATUS_ALLOCATED;
+
+ aShmemSection->size() = aSize;
+ aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get<uint8_t>();
+ ShrinkShmemSectionHeap();
+ return true;
+}
+
+void
+FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+ MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+ if (!aShmemSection.shmem().IsWritable()) {
+ return;
+ }
+
+ ShmemSectionHeapAllocation* allocHeader =
+ reinterpret_cast<ShmemSectionHeapAllocation*>(aShmemSection.shmem().get<char>() +
+ aShmemSection.offset() -
+ sizeof(ShmemSectionHeapAllocation));
+
+ MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+ DebugOnly<bool> success = allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+ // If this fails something really weird is going on.
+ MOZ_ASSERT(success);
+
+ ShmemSectionHeapHeader* header = aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+ header->mAllocatedBlocks--;
+}
+
+void
+FixedSizeSmallShmemSectionAllocator::DeallocShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+ if (!IPCOpen()) {
+ gfxCriticalNote << "Attempt to dealloc a ShmemSections after shutdown.";
+ return;
+ }
+
+ FreeShmemSection(aShmemSection);
+ ShrinkShmemSectionHeap();
+}
+
+
+void
+FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap()
+{
+ if (!IPCOpen()) {
+ mUsedShmems.clear();
+ return;
+ }
+
+ // The loop will terminate as we either increase i, or decrease size
+ // every time through.
+ size_t i = 0;
+ while (i < mUsedShmems.size()) {
+ ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if (header->mAllocatedBlocks == 0) {
+ mShmProvider->DeallocShmem(mUsedShmems[i]);
+ // We don't particularly care about order, move the last one in the array
+ // to this position.
+ if (i < mUsedShmems.size() - 1) {
+ mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1];
+ }
+ mUsedShmems.pop_back();
+ } else {
+ i++;
+ }
+ }
+}
+
+int32_t
+ClientIPCAllocator::GetMaxTextureSize() const
+{
+ return gfxPrefs::MaxTextureSize();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ISurfaceAllocator.h b/gfx/layers/ipc/ISurfaceAllocator.h
new file mode 100644
index 000000000..cd38b7e8b
--- /dev/null
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_ISURFACEDEALLOCATOR
+#define GFX_LAYERS_ISURFACEDEALLOCATOR
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t
+#include "gfxTypes.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/RefPtr.h"
+#include "nsIMemoryReporter.h" // for nsIMemoryReporter
+#include "mozilla/Atomics.h" // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
+#include "LayersTypes.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+class IShmemAllocator;
+} // namespace ipc
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class CompositableForwarder;
+class TextureForwarder;
+
+class ShmemAllocator;
+class ShmemSectionAllocator;
+class LegacySurfaceDescriptorAllocator;
+class ClientIPCAllocator;
+class HostIPCAllocator;
+class LayersIPCChannel;
+
+enum BufferCapabilities {
+ DEFAULT_BUFFER_CAPS = 0,
+ /**
+ * The allocated buffer must be efficiently mappable as a DataSourceSurface.
+ */
+ MAP_AS_IMAGE_SURFACE = 1 << 0,
+ /**
+ * The allocated buffer will be used for GL rendering only
+ */
+ USING_GL_RENDERING_ONLY = 1 << 1
+};
+
+class SurfaceDescriptor;
+
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType();
+
+/**
+ * An interface used to create and destroy surfaces that are shared with the
+ * Compositor process (using shmem, or gralloc, or other platform specific memory)
+ *
+ * Most of the methods here correspond to methods that are implemented by IPDL
+ * actors without a common polymorphic interface.
+ * These methods should be only called in the ipdl implementor's thread, unless
+ * specified otherwise in the implementing class.
+ */
+class ISurfaceAllocator
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ISurfaceAllocator)
+
+ ISurfaceAllocator() {}
+
+ // down-casting
+
+ virtual ShmemAllocator* AsShmemAllocator() { return nullptr; }
+
+ virtual ShmemSectionAllocator* AsShmemSectionAllocator() { return nullptr; }
+
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
+
+ virtual TextureForwarder* GetTextureForwarder() { return nullptr; }
+
+ virtual ClientIPCAllocator* AsClientAllocator() { return nullptr; }
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() { return nullptr; }
+
+ virtual LegacySurfaceDescriptorAllocator*
+ AsLegacySurfaceDescriptorAllocator() { return nullptr; }
+
+ // ipc info
+
+ virtual bool IPCOpen() const { return true; }
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+protected:
+ void Finalize() {}
+
+ virtual ~ISurfaceAllocator() {}
+};
+
+/// Methods that are specific to the client/child side.
+class ClientIPCAllocator : public ISurfaceAllocator
+{
+public:
+ ClientIPCAllocator() {}
+
+ virtual ClientIPCAllocator* AsClientAllocator() override { return this; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop * GetMessageLoop() const = 0;
+
+ virtual int32_t GetMaxTextureSize() const;
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
+};
+
+/// Methods that are specific to the host/parent side.
+class HostIPCAllocator : public ISurfaceAllocator
+{
+public:
+ HostIPCAllocator() {}
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() override { return this; }
+
+ /**
+ * Get child side's process Id.
+ */
+ virtual base::ProcessId GetChildProcessId() = 0;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) = 0;
+
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) = 0;
+
+ virtual void SendPendingAsyncMessages();
+
+ virtual void SetAboutToSendAsyncMessages()
+ {
+ mAboutToSendAsyncMessages = true;
+ }
+
+ bool IsAboutToSendAsyncMessages()
+ {
+ return mAboutToSendAsyncMessages;
+ }
+
+protected:
+ std::vector<AsyncParentMessageData> mPendingAsyncMessage;
+ bool mAboutToSendAsyncMessages = false;
+};
+
+/// An allocator can provide shared memory.
+///
+/// The allocated shmems can be deallocated on either process, as long as they
+/// belong to the same channel.
+class ShmemAllocator
+{
+public:
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
+};
+
+/// An allocator that can group allocations in bigger chunks of shared memory.
+///
+/// The allocated shmem sections can only be deallocated by the same allocator
+/// instance (and only in the child process).
+class ShmemSectionAllocator
+{
+public:
+ virtual bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) = 0;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) = 0;
+
+ virtual void MemoryPressure() {}
+};
+
+/// Some old stuff that's still around and used for screenshots.
+///
+/// New code should not need this (see TextureClient).
+class LegacySurfaceDescriptorAllocator
+{
+public:
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) = 0;
+};
+
+bool
+IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface);
+
+already_AddRefed<gfx::DrawTarget>
+GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend);
+
+already_AddRefed<gfx::DataSourceSurface>
+GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor);
+
+uint8_t*
+GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor);
+
+void
+DestroySurfaceDescriptor(mozilla::ipc::IShmemAllocator* aAllocator, SurfaceDescriptor* aSurface);
+
+class GfxMemoryImageReporter final : public nsIMemoryReporter
+{
+ ~GfxMemoryImageReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ GfxMemoryImageReporter()
+ {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount|
+ // being static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+
+ static void DidAlloc(void* aPointer)
+ {
+ sAmount += MallocSizeOfOnAlloc(aPointer);
+ }
+
+ static void WillFree(void* aPointer)
+ {
+ sAmount -= MallocSizeOfOnFree(aPointer);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount,
+ "Heap memory shared between threads by texture clients and hosts.");
+
+ return NS_OK;
+ }
+
+private:
+ // Typically we use |size_t| in memory reporters, but in the past this
+ // variable has sometimes gone negative due to missing DidAlloc() calls.
+ // Therefore, we use a signed type so that any such negative values show up
+ // as negative in about:memory, rather than as enormous positive numbers.
+ static mozilla::Atomic<ptrdiff_t> sAmount;
+};
+
+/// A simple shmem section allocator that can only allocate small
+/// fixed size elements (only intended to be used to store tile
+/// copy-on-write locks for now).
+class FixedSizeSmallShmemSectionAllocator final : public ShmemSectionAllocator
+{
+public:
+ enum AllocationStatus
+ {
+ STATUS_ALLOCATED,
+ STATUS_FREED
+ };
+
+ struct ShmemSectionHeapHeader
+ {
+ Atomic<uint32_t> mTotalBlocks;
+ Atomic<uint32_t> mAllocatedBlocks;
+ };
+
+ struct ShmemSectionHeapAllocation
+ {
+ Atomic<uint32_t> mStatus;
+ uint32_t mSize;
+ };
+
+ explicit FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider);
+
+ ~FixedSizeSmallShmemSectionAllocator();
+
+ virtual bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) override;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) override;
+
+ virtual void MemoryPressure() override { ShrinkShmemSectionHeap(); }
+
+ // can be called on the compositor process.
+ static void FreeShmemSection(ShmemSection& aShmemSection);
+
+ void ShrinkShmemSectionHeap();
+
+ bool IPCOpen() const;
+
+protected:
+ std::vector<mozilla::ipc::Shmem> mUsedShmems;
+ LayersIPCChannel* mShmProvider;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp
new file mode 100644
index 000000000..0466a1031
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -0,0 +1,1239 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeChild.h"
+#include <vector> // for vector
+#include "ImageBridgeParent.h" // for ImageBridgeParent
+#include "ImageContainer.h" // for ImageContainer
+#include "Layers.h" // for Layer, etc
+#include "ShadowLayers.h" // for ShadowLayerForwarder
+#include "base/message_loop.h" // for MessageLoop
+#include "base/platform_thread.h" // for PlatformThread
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for NewRunnableFunction, etc
+#include "base/thread.h" // for Thread
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
+#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/ImageContainerChild.h"
+#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
+#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mtransport/runnable_utils.h"
+#include "nsContentUtils.h"
+#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsTArrayForwardDeclare.h" // for AutoTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/layers/TextureClient.h"
+#include "SynchronousTask.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using base::Thread;
+using base::ProcessId;
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+typedef std::vector<CompositableOperation> OpVector;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+namespace {
+class ImageBridgeThread : public Thread {
+public:
+
+ ImageBridgeThread() : Thread("ImageBridgeChild") {
+ }
+
+protected:
+
+ MOZ_IS_CLASS_INIT
+ void Init() {
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ mPseudoStackHack = mozilla_get_pseudo_stack();
+#endif
+ }
+
+ void CleanUp() {
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ mPseudoStackHack = nullptr;
+#endif
+ }
+
+private:
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ // This is needed to avoid a spurious leak report. There's no other
+ // use for it. See bug 1239504 and bug 1215265.
+ MOZ_INIT_OUTSIDE_CTOR PseudoStack* mPseudoStackHack;
+#endif
+};
+}
+
+struct CompositableTransaction
+{
+ CompositableTransaction()
+ : mSwapRequired(false)
+ , mFinished(true)
+ {}
+ ~CompositableTransaction()
+ {
+ End();
+ }
+ bool Finished() const
+ {
+ return mFinished;
+ }
+ void Begin()
+ {
+ MOZ_ASSERT(mFinished);
+ mFinished = false;
+ }
+ void End()
+ {
+ mFinished = true;
+ mSwapRequired = false;
+ mOperations.clear();
+ mDestroyedActors.Clear();
+ }
+ bool IsEmpty() const
+ {
+ return mOperations.empty() && mDestroyedActors.IsEmpty();
+ }
+ void AddNoSwapEdit(const CompositableOperation& op)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mOperations.push_back(op);
+ }
+ void AddEdit(const CompositableOperation& op)
+ {
+ AddNoSwapEdit(op);
+ MarkSyncTransaction();
+ }
+ void MarkSyncTransaction()
+ {
+ mSwapRequired = true;
+ }
+
+ OpVector mOperations;
+ OpDestroyVector mDestroyedActors;
+ bool mSwapRequired;
+ bool mFinished;
+};
+
+struct AutoEndTransaction {
+ explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
+ ~AutoEndTransaction() { mTxn->End(); }
+ CompositableTransaction* mTxn;
+};
+
+void
+ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aCompositable->GetIPDLActor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+
+ AutoTArray<TimedTexture,4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+
+ if (!t.mTextureClient->IsSharedWithCompositor()) {
+ return;
+ }
+
+ ReadLockDescriptor readLock;
+ t.mTextureClient->SerializeReadLock(readLock);
+
+ textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
+ readLock,
+ t.mTimeStamp, t.mPictureRect,
+ t.mFrameID, t.mProducerID));
+
+ // Wait end of usage on host side if TextureFlags::RECYCLE is set or GrallocTextureData case
+ HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTexture(textures)));
+}
+
+void
+ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aTextureOnWhite);
+ MOZ_ASSERT(aTextureOnBlack);
+ MOZ_ASSERT(aCompositable->IsConnected());
+ MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
+
+ ReadLockDescriptor readLockW;
+ ReadLockDescriptor readLockB;
+ aTextureOnBlack->SerializeReadLock(readLockB);
+ aTextureOnWhite->SerializeReadLock(readLockW);
+
+ HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
+ HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
+
+ mTxn->AddNoSwapEdit(
+ CompositableOperation(
+ nullptr,
+ aCompositable->GetIPDLActor(),
+ OpUseComponentAlphaTextures(
+ nullptr, aTextureOnBlack->GetIPDLActor(),
+ nullptr, aTextureOnWhite->GetIPDLActor(),
+ readLockB, readLockW
+ )
+ )
+ );
+}
+
+void
+ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
+{
+ // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge.
+ if (!aClient ||
+ !(aClient->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient);
+}
+
+void
+ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId)
+{
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ if (aFwdTransactionId < client->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+void
+ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
+ if (!client) {
+ return;
+ }
+ mTexturesWaitingRecycled.Remove(aTextureId);
+}
+
+// Singleton
+static StaticMutex sImageBridgeSingletonLock;
+static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
+static Thread *sImageBridgeChildThread = nullptr;
+
+// dispatched function
+void
+ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask)
+{
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ MediaSystemResourceManager::Shutdown();
+
+ // Force all managed protocols to shut themselves down cleanly
+ InfallibleTArray<PCompositableChild*> compositables;
+ ManagedPCompositableChild(compositables);
+ for (int i = compositables.Length() - 1; i >= 0; --i) {
+ auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
+ if (compositable) {
+ compositable->Destroy();
+ }
+ }
+ InfallibleTArray<PTextureChild*> textures;
+ ManagedPTextureChild(textures);
+ for (int i = textures.Length() - 1; i >= 0; --i) {
+ RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+ if (client) {
+ client->Destroy();
+ }
+ }
+
+ if (mCanSend) {
+ SendWillClose();
+ }
+ MarkShutDown();
+
+ // From now on, no message can be sent through the image bridge from the
+ // client side except the final Stop message.
+}
+
+// dispatched function
+void
+ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
+{
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ if (!mCalledClose) {
+ Close();
+ mCalledClose = true;
+ }
+}
+
+void
+ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+ mCalledClose = true;
+}
+
+void
+ImageBridgeChild::DeallocPImageBridgeChild()
+{
+ this->Release();
+}
+
+void
+ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ AutoCompleteTask complete(aTask);
+ *result = CreateImageClientNow(aType, aImageContainer, aContainerChild);
+}
+
+// dispatched function
+void
+ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult)
+{
+ AutoCompleteTask complete(aTask);
+ *outResult = CreateCanvasClientNow(aType, aFlags);
+}
+
+ImageBridgeChild::ImageBridgeChild()
+ : mCanSend(false)
+ , mCalledClose(false)
+ , mFwdTransactionId(0)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTxn = new CompositableTransaction();
+}
+
+ImageBridgeChild::~ImageBridgeChild()
+{
+ delete mTxn;
+}
+
+void
+ImageBridgeChild::MarkShutDown()
+{
+ mTexturesWaitingRecycled.Clear();
+
+ mCanSend = false;
+}
+
+void
+ImageBridgeChild::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(CanSend());
+
+ uint64_t id = 0;
+
+ PImageContainerChild* imageContainerChild = nullptr;
+ if (aImageContainer)
+ imageContainerChild = aImageContainer->GetPImageContainerChild();
+
+ PCompositableChild* child =
+ SendPCompositableConstructor(aCompositable->GetTextureInfo(),
+ imageContainerChild, &id);
+ if (!child) {
+ return;
+ }
+ aCompositable->InitIPDLActor(child, id);
+}
+
+PCompositableChild*
+ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo,
+ PImageContainerChild* aChild, uint64_t* aID)
+{
+ MOZ_ASSERT(CanSend());
+ return AsyncCompositableChild::CreateActor();
+}
+
+bool
+ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
+{
+ AsyncCompositableChild::DestroyActor(aActor);
+ return true;
+}
+
+
+Thread* ImageBridgeChild::GetThread() const
+{
+ return sImageBridgeChildThread;
+}
+
+/* static */ RefPtr<ImageBridgeChild>
+ImageBridgeChild::GetSingleton()
+{
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ return sImageBridgeChildSingleton;
+}
+
+void
+ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild)
+{
+ if (!aChild) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ReleaseImageContainer,
+ aChild);
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+
+ aChild->SendAsyncDelete();
+}
+
+void
+ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ RELEASE_MANUALLY(aClient);
+}
+
+/* static */ void
+ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
+{
+ if (!aClient) {
+ return;
+ }
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (!imageBridge) {
+ // TextureClient::Release should normally happen in the ImageBridgeChild
+ // thread because it usually generate some IPDL messages.
+ // However, if we take this branch it means that the ImageBridgeChild
+ // has already shut down, along with the TextureChild, which means no
+ // message will be sent and it is safe to run this code from any thread.
+ MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
+ RELEASE_MANUALLY(aClient);
+ return;
+ }
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ imageBridge,
+ &ImageBridgeChild::ReleaseTextureClientNow,
+ aClient);
+ imageBridge->GetMessageLoop()->PostTask(runnable.forget());
+}
+
+void
+ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer)
+{
+ if (!aClient || !aContainer) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateImageClient,
+ aClient,
+ aContainer);
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ // If the client has become disconnected before this event was dispatched,
+ // early return now.
+ if (!aClient->IsConnected()) {
+ return;
+ }
+
+ BeginTransaction();
+ aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
+{
+ AutoCompleteTask complete(aTask);
+
+ UpdateAsyncCanvasRendererNow(aWrapper);
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
+{
+ aWrapper->GetCanvasClient()->UpdateAsync(aWrapper);
+
+ if (InImageBridgeChildThread()) {
+ UpdateAsyncCanvasRendererNow(aWrapper);
+ return;
+ }
+
+ SynchronousTask task("UpdateAsyncCanvasRenderer Lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateAsyncCanvasRendererSync,
+ &task,
+ aWrapper);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+}
+
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
+{
+ MOZ_ASSERT(aWrapper);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ BeginTransaction();
+ aWrapper->GetCanvasClient()->Updated();
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ MOZ_ASSERT(aClient);
+ BeginTransaction();
+ if (aContainer) {
+ aContainer->ClearImagesFromImageBridge();
+ }
+ aClient->FlushAllImages();
+ EndTransaction();
+}
+
+void
+ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer)
+{
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ if (InImageBridgeChildThread()) {
+ NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
+ return;
+ }
+
+ SynchronousTask task("FlushAllImages Lock");
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::FlushAllImagesSync,
+ &task,
+ aClient,
+ aContainer);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+}
+
+void
+ImageBridgeChild::BeginTransaction()
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin();
+}
+
+void
+ImageBridgeChild::EndTransaction()
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ AutoEndTransaction _(mTxn);
+
+ if (mTxn->IsEmpty()) {
+ return;
+ }
+
+ AutoTArray<CompositableOperation, 10> cset;
+ cset.SetCapacity(mTxn->mOperations.size());
+ if (!mTxn->mOperations.empty()) {
+ cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
+ }
+
+ if (!IsSameProcess()) {
+ ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+ }
+
+ AutoTArray<EditReply, 10> replies;
+
+ if (mTxn->mSwapRequired) {
+ if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId(), &replies)) {
+ NS_WARNING("could not send async texture transaction");
+ return;
+ }
+ } else {
+ // If we don't require a swap we can call SendUpdateNoSwap which
+ // assumes that aReplies is empty (DEBUG assertion)
+ if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
+ NS_WARNING("could not send async texture transaction (no swap)");
+ return;
+ }
+ }
+ for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
+ NS_RUNTIMEABORT("not reached");
+ }
+}
+
+void
+ImageBridgeChild::SendImageBridgeThreadId()
+{
+}
+
+bool
+ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gfxPlatform::GetPlatform();
+
+ if (!sImageBridgeChildThread) {
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->Start()) {
+ return false;
+ }
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+ RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child,
+ &ImageBridgeChild::Bind,
+ Move(aEndpoint));
+ child->GetMessageLoop()->PostTask(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get a
+ // false result and a MsgDropped processing error. This is okay.
+ ShutdownSingleton();
+
+ return InitForContent(Move(aEndpoint));
+}
+
+void
+ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
+}
+
+void
+ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
+{
+ MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
+ ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
+ Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
+}
+
+/* static */ void
+ImageBridgeChild::ShutDown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ShutdownSingleton();
+
+ delete sImageBridgeChildThread;
+ sImageBridgeChildThread = nullptr;
+}
+
+/* static */ void
+ImageBridgeChild::ShutdownSingleton()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->WillShutdown();
+
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = nullptr;
+ }
+}
+
+void
+ImageBridgeChild::WillShutdown()
+{
+ {
+ SynchronousTask task("ImageBridge ShutdownStep1 lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep1,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ }
+
+ {
+ SynchronousTask task("ImageBridge ShutdownStep2 lock");
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep2,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ }
+}
+
+void
+ImageBridgeChild::InitSameProcess()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->IsRunning()) {
+ sImageBridgeChildThread->Start();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+ RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ child,
+ &ImageBridgeChild::BindSameProcess,
+ parent);
+ child->GetMessageLoop()->PostTask(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+/* static */ void
+ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ sImageBridgeChildThread = new ImageBridgeThread();
+ if (!sImageBridgeChildThread->IsRunning()) {
+ sImageBridgeChildThread->Start();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+ MessageLoop* loop = child->GetMessageLoop();
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child, &ImageBridgeChild::Bind, Move(aEndpoint)));
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+bool InImageBridgeChildThread()
+{
+ return sImageBridgeChildThread &&
+ sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
+}
+
+MessageLoop * ImageBridgeChild::GetMessageLoop() const
+{
+ return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr;
+}
+
+/* static */ void
+ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
+{
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->IdentifyTextureHost(aIdentifier);
+ }
+}
+
+RefPtr<ImageClient>
+ImageBridgeChild::CreateImageClient(CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ if (InImageBridgeChildThread()) {
+ return CreateImageClientNow(aType, aImageContainer, aContainerChild);
+ }
+
+ SynchronousTask task("CreateImageClient Lock");
+
+ RefPtr<ImageClient> result = nullptr;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateImageClientSync,
+ &task,
+ &result,
+ aType,
+ aImageContainer,
+ aContainerChild);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return result;
+}
+
+RefPtr<ImageClient>
+ImageBridgeChild::CreateImageClientNow(CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (!CanSend()) {
+ return nullptr;
+ }
+
+ if (aImageContainer) {
+ aContainerChild->RegisterWithIPDL();
+ if (!SendPImageContainerConstructor(aContainerChild)) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
+ MOZ_ASSERT(client, "failed to create ImageClient");
+ if (client) {
+ client->Connect(aImageContainer);
+ }
+ return client;
+}
+
+already_AddRefed<CanvasClient>
+ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag)
+{
+ if (InImageBridgeChildThread()) {
+ return CreateCanvasClientNow(aType, aFlag);
+ }
+
+ SynchronousTask task("CreateCanvasClient Lock");
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<CanvasClient> result = nullptr;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateCanvasClientSync,
+ &task,
+ aType,
+ aFlag,
+ &result);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return result.forget();
+}
+
+already_AddRefed<CanvasClient>
+ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag)
+{
+ RefPtr<CanvasClient> client
+ = CanvasClient::CreateCanvasClient(aType, this, aFlag);
+ MOZ_ASSERT(client, "failed to create CanvasClient");
+ if (client) {
+ client->Connect();
+ }
+ return client.forget();
+}
+
+bool
+ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+ImageBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+// NewRunnableFunction accepts a limited number of parameters so we need a
+// struct here
+struct AllocShmemParams {
+ size_t mSize;
+ ipc::SharedMemory::SharedMemoryType mType;
+ ipc::Shmem* mShmem;
+ bool mUnsafe;
+ bool mSuccess;
+};
+
+void
+ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aParams->mUnsafe) {
+ ok = AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem);
+ } else {
+ ok = AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem);
+ }
+ aParams->mSuccess = ok;
+}
+
+bool
+ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem,
+ bool aUnsafe)
+{
+ SynchronousTask task("AllocatorProxy alloc");
+
+ AllocShmemParams params = {
+ aSize, aType, aShmem, aUnsafe, false
+ };
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyAllocShmemNow,
+ &task,
+ &params);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+
+ return params.mSuccess;
+}
+
+void
+ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem,
+ bool* aResult)
+{
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool
+ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (InImageBridgeChildThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::DeallocShmem(aShmem);
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyDeallocShmemNow,
+ &task,
+ &aShmem,
+ &result);
+ GetMessageLoop()->PostTask(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild*
+ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t& aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerChild*
+ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
+{
+ MOZ_ASSERT(CanSend());
+ return new mozilla::media::MediaSystemResourceManagerChild();
+}
+
+bool
+ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
+ return true;
+}
+
+PImageContainerChild*
+ImageBridgeChild::AllocPImageContainerChild()
+{
+ // we always use the "power-user" ctor
+ NS_RUNTIMEABORT("not reached");
+ return nullptr;
+}
+
+bool
+ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor)
+{
+ static_cast<ImageContainerChild*>(actor)->UnregisterFromIPDL();
+ return true;
+}
+
+bool
+ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
+{
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
+{
+ for (auto& n : aNotifications) {
+ ImageContainerChild* child =
+ static_cast<ImageContainerChild*>(n.imageContainerChild());
+ if (child) {
+ child->NotifyComposite(n);
+ }
+ }
+ return true;
+}
+
+PTextureChild*
+ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+static bool
+IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+ if (aTxn->Finished()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+
+ if (synchronously) {
+ aTxn->MarkSyncTransaction();
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+ return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+ return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
+
+void
+ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture)
+{
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsSharedWithCompositor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+ if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
+ return;
+ }
+
+ CompositableOperation op(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor()));
+
+ if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTxn->AddEdit(op);
+ } else {
+ mTxn->AddNoSwapEdit(op);
+ }
+}
+
+bool ImageBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+ImageBridgeChild::Destroy(CompositableChild* aCompositable)
+{
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::Destroy,
+ RefPtr<CompositableChild>(aCompositable));
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+ CompositableForwarder::Destroy(aCompositable);
+}
+
+bool
+ImageBridgeChild::CanSend() const
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ return mCanSend;
+}
+
+void
+ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+{
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h
new file mode 100644
index 000000000..f068149f7
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H
+#define MOZILLA_GFX_IMAGEBRIDGECHILD_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/PImageBridgeChild.h"
+#include "mozilla/Mutex.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsIObserver.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class AsyncCanvasRenderer;
+class ImageClient;
+class ImageContainer;
+class ImageContainerChild;
+class ImageBridgeParent;
+class CompositableClient;
+struct CompositableTransaction;
+class Image;
+class TextureClient;
+class SynchronousTask;
+struct AllocShmemParams;
+
+/**
+ * Returns true if the current thread is the ImageBrdigeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+bool InImageBridgeChildThread();
+
+/**
+ * The ImageBridge protocol is meant to allow ImageContainers to forward images
+ * directly to the compositor thread/process without using the main thread.
+ *
+ * ImageBridgeChild is a CompositableForwarder just like ShadowLayerForwarder.
+ * This means it also does transactions with the compositor thread/process,
+ * except that the transactions are restricted to operations on the Compositables
+ * and cannot contain messages affecting layers directly.
+ *
+ * ImageBridgeChild is also a ISurfaceAllocator. It can be used to allocate or
+ * deallocate data that is shared with the compositor. The main differerence
+ * with other ISurfaceAllocators is that some of its overriden methods can be
+ * invoked from any thread.
+ *
+ * There are three important phases in the ImageBridge protocol. These three steps
+ * can do different things depending if (A) the ImageContainer uses ImageBridge
+ * or (B) it does not use ImageBridge:
+ *
+ * - When an ImageContainer calls its method SetCurrentImage:
+ * - (A) The image is sent directly to the compositor process through the
+ * ImageBridge IPDL protocol.
+ * On the compositor side the image is stored in a global table that associates
+ * the image with an ID corresponding to the ImageContainer, and a composition is
+ * triggered.
+ * - (B) Since it does not have an ImageBridge, the image is not sent yet.
+ * instead the will be sent to the compositor during the next layer transaction
+ * (on the main thread).
+ *
+ * - During a Layer transaction:
+ * - (A) The ImageContainer uses ImageBridge. The image is already available to the
+ * compositor process because it has been sent with SetCurrentImage. Yet, the
+ * CompositableHost on the compositor side will needs the ID referring to the
+ * ImageContainer to access the Image. So during the Swap operation that happens
+ * in the transaction, we swap the container ID rather than the image data.
+ * - (B) Since the ImageContainer does not use ImageBridge, the image data is swaped.
+ *
+ * - During composition:
+ * - (A) The CompositableHost has an AsyncID, it looks up the ID in the
+ * global table to see if there is an image. If there is no image, nothing is rendered.
+ * - (B) The CompositableHost has image data rather than an ID (meaning it is not
+ * using ImageBridge), then it just composites the image data normally.
+ *
+ * This means that there might be a possibility for the ImageBridge to send the first
+ * frame before the first layer transaction that will pass the container ID to the
+ * CompositableHost happens. In this (unlikely) case the layer is not composited
+ * until the layer transaction happens. This means this scenario is not harmful.
+ *
+ * Since sending an image through imageBridge triggers compositing, the main thread is
+ * not used at all (except for the very first transaction that provides the
+ * CompositableHost with an AsyncID).
+ */
+class ImageBridgeChild final : public PImageBridgeChild
+ , public CompositableForwarder
+ , public TextureForwarder
+{
+ friend class ImageContainer;
+
+ typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageBridgeChild, override);
+
+ TextureForwarder* GetTextureForwarder() override { return this; }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ /**
+ * Creates the image bridge with a dedicated thread for ImageBridgeChild.
+ *
+ * We may want to use a specifi thread in the future. In this case, use
+ * CreateWithThread instead.
+ */
+ static void InitSameProcess();
+
+ static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint);
+ static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
+ static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint);
+
+ /**
+ * Destroys the image bridge by calling DestroyBridge, and destroys the
+ * ImageBridge's thread.
+ *
+ * If you don't want to destroy the thread, call DestroyBridge directly
+ * instead.
+ */
+ static void ShutDown();
+
+ /**
+ * returns the singleton instance.
+ *
+ * can be called from any thread.
+ */
+ static RefPtr<ImageBridgeChild> GetSingleton();
+
+
+ static void IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ void BeginTransaction();
+ void EndTransaction();
+
+ /**
+ * Returns the ImageBridgeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+ base::Thread * GetThread() const;
+
+ /**
+ * Returns the ImageBridgeChild's message loop.
+ *
+ * Can be called from any thread.
+ */
+ virtual MessageLoop * GetMessageLoop() const override;
+
+ virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo,
+ PImageContainerChild* aChild, uint64_t* aID) override;
+ bool DeallocPCompositableChild(PCompositableChild* aActor) override;
+
+ virtual PTextureChild*
+ AllocPTextureChild(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aSerial) override;
+
+ virtual bool
+ DeallocPTextureChild(PTextureChild* actor) override;
+
+ PMediaSystemResourceManagerChild*
+ AllocPMediaSystemResourceManagerChild() override;
+ bool
+ DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) override;
+
+ virtual PImageContainerChild*
+ AllocPImageContainerChild() override;
+ virtual bool
+ DeallocPImageContainerChild(PImageContainerChild* actor) override;
+
+ virtual bool
+ RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
+
+ virtual bool
+ RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) override;
+
+ // Create an ImageClient from any thread.
+ RefPtr<ImageClient> CreateImageClient(
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ // Create an ImageClient from the ImageBridge thread.
+ RefPtr<ImageClient> CreateImageClientNow(
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClient::CanvasClientType aType,
+ TextureFlags aFlag);
+ void ReleaseImageContainer(RefPtr<ImageContainerChild> aChild);
+ void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
+ void UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer);
+ static void DispatchReleaseTextureClient(TextureClient* aClient);
+
+ /**
+ * Flush all Images sent to CompositableHost.
+ */
+ void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+ virtual bool IPCOpen() const override { return mCanSend; }
+
+private:
+
+ /**
+ * This must be called by the static function DeleteImageBridgeSync defined
+ * in ImageBridgeChild.cpp ONLY.
+ */
+ ~ImageBridgeChild();
+
+ // Helpers for dispatching.
+ already_AddRefed<CanvasClient> CreateCanvasClientNow(
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags);
+ void CreateCanvasClientSync(
+ SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult);
+
+ void CreateImageClientSync(
+ SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+
+ void ReleaseTextureClientNow(TextureClient* aClient);
+
+ void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
+ void UpdateAsyncCanvasRendererSync(
+ SynchronousTask* aTask,
+ AsyncCanvasRenderer* aWrapper);
+
+ void FlushAllImagesSync(
+ SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer);
+
+ void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, Shmem* aShmem, bool* aResult);
+
+public:
+ // CompositableForwarder
+
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ virtual bool UsesImageBridge() const override { return true; }
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ void Destroy(CompositableChild* aCompositable) override;
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set.
+ * Host side's usage is checked via CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
+
+ virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+ virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override
+ {
+ NS_RUNTIMEABORT("should not be called");
+ }
+
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override {
+ NS_RUNTIMEABORT("should not be called");
+ }
+
+ // ISurfaceAllocator
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ virtual bool IsSameProcess() const override;
+
+ virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
+ virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
+
+ bool InForwarderThread() override {
+ return InImageBridgeChildThread();
+ }
+
+ virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+
+protected:
+ ImageBridgeChild();
+ bool DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ Shmem* aShmem,
+ bool aUnsafe);
+
+ void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+ void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+ void SendImageBridgeThreadId();
+
+ void WillShutdown();
+ void ShutdownStep1(SynchronousTask* aTask);
+ void ShutdownStep2(SynchronousTask* aTask);
+ void MarkShutDown();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPImageBridgeChild() override;
+
+ bool CanSend() const;
+
+ static void ShutdownSingleton();
+
+private:
+ CompositableTransaction* mTxn;
+
+ bool mCanSend;
+ bool mCalledClose;
+
+ /**
+ * Transaction id of CompositableForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp
new file mode 100644
index 000000000..7b116f520
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -0,0 +1,449 @@
+/* vim: set ts=2 sw=2 et tw=80: */
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeParent.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableHost.h" // for CompositableParent, Create
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, DeleteTask, etc
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority()
+#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
+#include "mozilla/layers/CompositableTransactionParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply
+#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent
+#include "mozilla/layers/PCompositableParent.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT, etc
+#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/layers/TextureHost.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeParent::sImageBridges;
+
+StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
+
+// defined in CompositorBridgeParent.cpp
+CompositorThreadHolder* GetCompositorThreadHolder();
+
+/* static */ void
+ImageBridgeParent::Setup()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sImageBridgesLock) {
+ sImageBridgesLock = new Monitor("ImageBridges");
+ mozilla::ClearOnShutdown(&sImageBridgesLock);
+ }
+}
+
+ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
+ ProcessId aChildProcessId)
+ : mMessageLoop(aLoop)
+ , mSetChildThreadPriority(false)
+ , mClosed(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // creates the map only if it has not been created already, so it is safe
+ // with several bridges
+ CompositableMap::Create();
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges[aChildProcessId] = this;
+ }
+ SetOtherProcessId(aChildProcessId);
+}
+
+ImageBridgeParent::~ImageBridgeParent()
+{
+ nsTArray<PImageContainerParent*> parents;
+ ManagedPImageContainerParent(parents);
+ for (PImageContainerParent* p : parents) {
+ delete p;
+ }
+}
+
+static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
+
+void ReleaseImageBridgeParentSingleton() {
+ sImageBridgeParentSingleton = nullptr;
+}
+
+/* static */ ImageBridgeParent*
+ImageBridgeParent::CreateSameProcess()
+{
+ RefPtr<ImageBridgeParent> parent =
+ new ImageBridgeParent(CompositorThreadHolder::Loop(), base::GetCurrentProcId());
+ parent->mSelfRef = parent;
+
+ sImageBridgeParentSingleton = parent;
+ return parent;
+}
+
+/* static */ bool
+ImageBridgeParent::CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+ RefPtr<ImageBridgeParent> parent = new ImageBridgeParent(loop, aEndpoint.OtherPid());
+
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ parent, &ImageBridgeParent::Bind, Move(aEndpoint)));
+
+ sImageBridgeParentSingleton = parent;
+ return true;
+}
+
+void
+ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges.erase(OtherPid());
+ }
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy));
+
+ // It is very important that this method gets called at shutdown (be it a clean
+ // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef.
+ // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent
+ // is leaked which causes the CompositorThreadHolder to be leaked and
+ // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting
+ // for the compositor thread to terminate.
+}
+
+bool
+ImageBridgeParent::RecvImageBridgeThreadId(const PlatformThreadId& aThreadId)
+{
+ MOZ_ASSERT(!mSetChildThreadPriority);
+ if (mSetChildThreadPriority) {
+ return false;
+ }
+ mSetChildThreadPriority = true;
+ return true;
+}
+
+class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender
+{
+public:
+ explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge,
+ InfallibleTArray<OpDestroy>* aToDestroy = nullptr)
+ : mImageBridge(aImageBridge)
+ , mToDestroy(aToDestroy)
+ {
+ mImageBridge->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoImageBridgeParentAsyncMessageSender()
+ {
+ mImageBridge->SendPendingAsyncMessages();
+ if (mToDestroy) {
+ for (const auto& op : *mToDestroy) {
+ mImageBridge->DestroyActor(op);
+ }
+ }
+ }
+private:
+ ImageBridgeParent* mImageBridge;
+ InfallibleTArray<OpDestroy>* mToDestroy;
+};
+
+bool
+ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ EditReplyArray* aReply)
+{
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ EditReplyVector replyv;
+ for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) {
+ if (!ReceiveCompositableUpdate(aEdits[i], replyv)) {
+ return false;
+ }
+ }
+
+ aReply->SetCapacity(replyv.size());
+ if (replyv.size() > 0) {
+ aReply->AppendElements(&replyv.front(), replyv.size());
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+ return true;
+}
+
+bool
+ImageBridgeParent::RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId)
+{
+ InfallibleTArray<EditReply> noReplies;
+ bool success = RecvUpdate(Move(aEdits), Move(aToDestroy), aFwdTransactionId, &noReplies);
+ MOZ_ASSERT(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits");
+ return success;
+}
+
+/* static */ bool
+ImageBridgeParent::CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+
+ RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aEndpoint.OtherPid());
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ bridge, &ImageBridgeParent::Bind, Move(aEndpoint)));
+
+ return true;
+}
+
+void
+ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint)
+{
+ if (!aEndpoint.Bind(this))
+ return;
+ mSelfRef = this;
+}
+
+bool ImageBridgeParent::RecvWillClose()
+{
+ // If there is any texture still alive we have to force it to deallocate the
+ // device data (GL textures, etc.) now because shortly after SenStop() returns
+ // on the child side the widget will be destroyed along with it's associated
+ // GL context.
+ InfallibleTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+ return true;
+}
+
+static uint64_t GenImageContainerID() {
+ static uint64_t sNextImageID = 1;
+
+ ++sNextImageID;
+ return sNextImageID;
+}
+
+PCompositableParent*
+ImageBridgeParent::AllocPCompositableParent(const TextureInfo& aInfo,
+ PImageContainerParent* aImageContainer,
+ uint64_t* aID)
+{
+ uint64_t id = GenImageContainerID();
+ *aID = id;
+ return CompositableHost::CreateIPDLActor(this, aInfo, id, aImageContainer);
+}
+
+bool ImageBridgeParent::DeallocPCompositableParent(PCompositableParent* aActor)
+{
+ return CompositableHost::DestroyIPDLActor(aActor);
+}
+
+PTextureParent*
+ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial)
+{
+ return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool
+ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerParent*
+ImageBridgeParent::AllocPMediaSystemResourceManagerParent()
+{
+ return new mozilla::media::MediaSystemResourceManagerParent();
+}
+
+bool
+ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor)
+{
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor);
+ return true;
+}
+
+PImageContainerParent*
+ImageBridgeParent::AllocPImageContainerParent()
+{
+ return new ImageContainerParent();
+}
+
+bool
+ImageBridgeParent::DeallocPImageContainerParent(PImageContainerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ mozilla::Unused << SendParentAsyncMessages(aMessage);
+}
+
+class ProcessIdComparator
+{
+public:
+ bool Equals(const ImageCompositeNotification& aA,
+ const ImageCompositeNotification& aB) const
+ {
+ return aA.imageContainerParent()->OtherPid() == aB.imageContainerParent()->OtherPid();
+ }
+ bool LessThan(const ImageCompositeNotification& aA,
+ const ImageCompositeNotification& aB) const
+ {
+ return aA.imageContainerParent()->OtherPid() < aB.imageContainerParent()->OtherPid();
+ }
+};
+
+/* static */ bool
+ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications)
+{
+ // Group the notifications by destination process ID and then send the
+ // notifications in one message per group.
+ aNotifications.Sort(ProcessIdComparator());
+ uint32_t i = 0;
+ bool ok = true;
+ while (i < aNotifications.Length()) {
+ AutoTArray<ImageCompositeNotification,1> notifications;
+ notifications.AppendElement(aNotifications[i]);
+ uint32_t end = i + 1;
+ MOZ_ASSERT(aNotifications[i].imageContainerParent());
+ ProcessId pid = aNotifications[i].imageContainerParent()->OtherPid();
+ while (end < aNotifications.Length() &&
+ aNotifications[end].imageContainerParent()->OtherPid() == pid) {
+ notifications.AppendElement(aNotifications[end]);
+ ++end;
+ }
+ GetInstance(pid)->SendPendingAsyncMessages();
+ if (!GetInstance(pid)->SendDidComposite(notifications)) {
+ ok = false;
+ }
+ i = end;
+ }
+ return ok;
+}
+
+void
+ImageBridgeParent::DeferredDestroy()
+{
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr; // "this" ImageBridge may get deleted here.
+}
+
+RefPtr<ImageBridgeParent>
+ImageBridgeParent::GetInstance(ProcessId aId)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(*sImageBridgesLock);
+ NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process");
+ return sImageBridges[aId];
+}
+
+void
+ImageBridgeParent::OnChannelConnected(int32_t aPid)
+{
+ mCompositorThreadHolder = GetCompositorThreadHolder();
+}
+
+
+bool
+ImageBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+ImageBridgeParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (mClosed) {
+ return;
+ }
+ PImageBridgeParent::DeallocShmem(aShmem);
+}
+
+bool ImageBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(
+ OpNotifyNotUsed(textureId, aTransactionId));
+
+ if (!IsAboutToSendAsyncMessages()) {
+ SendPendingAsyncMessages();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h
new file mode 100644
index 000000000..2dc705691
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_ImageBridgeParent_h_
+#define gfx_layers_ipc_ImageBridgeParent_h_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "CompositableTransactionParent.h"
+#include "ImageContainerParent.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "nsISupportsImpl.h"
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+/**
+ * ImageBridgeParent is the manager Protocol of ImageContainerParent.
+ * It's purpose is mainly to setup the IPDL connection. Most of the
+ * interesting stuff is in ImageContainerParent.
+ */
+class ImageBridgeParent final : public PImageBridgeParent,
+ public CompositableParentManager,
+ public ShmemAllocator
+{
+public:
+ typedef InfallibleTArray<CompositableOperation> EditArray;
+ typedef InfallibleTArray<OpDestroy> OpDestroyArray;
+ typedef InfallibleTArray<EditReply> EditReplyArray;
+
+protected:
+ ImageBridgeParent(MessageLoop* aLoop, ProcessId aChildProcessId);
+
+public:
+ ~ImageBridgeParent();
+
+ /**
+ * Creates the globals of ImageBridgeParent.
+ */
+ static void Setup();
+
+ static ImageBridgeParent* CreateSameProcess();
+ static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint);
+ static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // CompositableParentManager
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+
+ virtual base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+
+ // PImageBridge
+ virtual bool RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
+ virtual bool RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ EditReplyArray* aReply) override;
+ virtual bool RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId) override;
+
+ PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo,
+ PImageContainerParent* aImageContainer,
+ uint64_t*) override;
+ bool DeallocPCompositableParent(PCompositableParent* aActor) override;
+
+ virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ virtual bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override;
+ bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override;
+ virtual PImageContainerParent* AllocPImageContainerParent() override;
+ virtual bool DeallocPImageContainerParent(PImageContainerParent* actor) override;
+
+ // Shutdown step 1
+ virtual bool RecvWillClose() override;
+
+ MessageLoop* GetMessageLoop() const { return mMessageLoop; }
+
+ // ShmemAllocator
+
+ virtual bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual void DeallocShmem(ipc::Shmem& aShmem) override;
+
+ virtual bool IsSameProcess() const override;
+
+ static RefPtr<ImageBridgeParent> GetInstance(ProcessId aId);
+
+ static bool NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications);
+
+ virtual bool UsesImageBridge() const override { return true; }
+
+ virtual bool IPCOpen() const override { return !mClosed; }
+
+protected:
+ void OnChannelConnected(int32_t pid) override;
+
+ void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+private:
+ void DeferredDestroy();
+ MessageLoop* mMessageLoop;
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<ImageBridgeParent> mSelfRef;
+
+ bool mSetChildThreadPriority;
+ bool mClosed;
+
+ /**
+ * Map of all living ImageBridgeParent instances
+ */
+ static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_ImageBridgeParent_h_
diff --git a/gfx/layers/ipc/ImageContainerChild.cpp b/gfx/layers/ipc/ImageContainerChild.cpp
new file mode 100644
index 000000000..c54eb2c41
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerChild.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainerChild.h"
+#include "ImageContainer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageContainerChild::ImageContainerChild(ImageContainer* aImageContainer)
+ : mLock("ImageContainerChild")
+ , mImageContainer(aImageContainer)
+ , mIPCOpen(false)
+{
+}
+
+void
+ImageContainerChild::ForgetImageContainer()
+{
+ MutexAutoLock lock(mLock);
+ mImageContainer = nullptr;
+}
+
+void
+ImageContainerChild::NotifyComposite(const ImageCompositeNotification& aNotification)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ MutexAutoLock lock(mLock);
+ if (mImageContainer) {
+ mImageContainer->NotifyCompositeInternal(aNotification);
+ }
+}
+
+void
+ImageContainerChild::RegisterWithIPDL()
+{
+ MOZ_ASSERT(!mIPCOpen);
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ AddRef();
+ mIPCOpen = true;
+}
+
+void
+ImageContainerChild::UnregisterFromIPDL()
+{
+ MOZ_ASSERT(mIPCOpen);
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ mIPCOpen = false;
+ Release();
+}
+
+void
+ImageContainerChild::SendAsyncDelete()
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ if (mIPCOpen) {
+ PImageContainerChild::SendAsyncDelete();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageContainerChild.h b/gfx/layers/ipc/ImageContainerChild.h
new file mode 100644
index 000000000..839540411
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerChild.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_layers_ImageContainerChild_h
+#define mozilla_gfx_layers_ImageContainerChild_h
+
+#include "mozilla/Mutex.h"
+#include "mozilla/layers/PImageContainerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageContainer;
+class ImageCompositeNotification;
+
+/**
+ * The child side of PImageContainer. It's best to avoid ImageContainer filling
+ * this role since IPDL objects should be associated with a single thread and
+ * ImageContainer definitely isn't. This object belongs to (and is always
+ * destroyed on) the ImageBridge thread, except when we need to destroy it
+ * during shutdown.
+ * An ImageContainer owns one of these; we have a weak reference to our
+ * ImageContainer.
+ */
+class ImageContainerChild final : public PImageContainerChild
+{
+public:
+ explicit ImageContainerChild(ImageContainer* aImageContainer);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainerChild)
+
+ void RegisterWithIPDL();
+ void UnregisterFromIPDL();
+ void SendAsyncDelete();
+
+ void NotifyComposite(const ImageCompositeNotification& aNotification);
+ void ForgetImageContainer();
+
+private:
+ ~ImageContainerChild()
+ {}
+
+private:
+ Mutex mLock;
+ ImageContainer* mImageContainer;
+
+ // If mIPCOpen is false, it means the IPDL code tried to deallocate the actor
+ // before the ImageContainer released it. When this happens we don't actually
+ // delete the actor right away because the ImageContainer has a reference to
+ // it. In this case the actor will be deleted when the ImageContainer lets go
+ // of it.
+ // mIPCOpen must not be accessed off the ImageBridgeChild thread.
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_ImageContainerChild_h
diff --git a/gfx/layers/ipc/ImageContainerParent.cpp b/gfx/layers/ipc/ImageContainerParent.cpp
new file mode 100644
index 000000000..0dc0d6d32
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerParent.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainerParent.h"
+
+#include "nsThreadUtils.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageContainerParent::~ImageContainerParent()
+{
+ while (!mImageHosts.IsEmpty()) {
+ mImageHosts[mImageHosts.Length() - 1]->SetImageContainer(nullptr);
+ }
+}
+
+bool ImageContainerParent::RecvAsyncDelete()
+{
+ Unused << PImageContainerParent::Send__delete__(this);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageContainerParent.h b/gfx/layers/ipc/ImageContainerParent.h
new file mode 100644
index 000000000..849bcb44f
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerParent.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ImageContainerParent_h
+#define mozilla_layers_ImageContainerParent_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PImageContainerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+
+class ImageContainerParent : public PImageContainerParent
+{
+public:
+ ImageContainerParent() {}
+ ~ImageContainerParent();
+
+ virtual bool RecvAsyncDelete() override;
+
+ AutoTArray<ImageHost*,1> mImageHosts;
+
+private:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ImageContainerParent_h
diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h
new file mode 100755
index 000000000..c4cb8092d
--- /dev/null
+++ b/gfx/layers/ipc/KnowsCompositor.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_KNOWSCOMPOSITOR
+#define MOZILLA_LAYERS_KNOWSCOMPOSITOR
+
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/CompositorTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+class SyncObject;
+class TextureForwarder;
+class LayersIPCActor;
+
+/**
+ * An abstract interface for classes that are tied to a specific Compositor across
+ * IPDL and uses TextureFactoryIdentifier to describe this Compositor.
+ */
+class KnowsCompositor {
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ KnowsCompositor();
+ ~KnowsCompositor();
+
+ void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ SyncObject* GetSyncObject() { return mSyncObject; }
+
+ int32_t GetMaxTextureSize() const
+ {
+ return mTextureFactoryIdentifier.mMaxTextureSize;
+ }
+
+ /**
+ * Returns the type of backend that is used off the main thread.
+ * We only don't allow changing the backend type at runtime so this value can
+ * be queried once and will not change until Gecko is restarted.
+ */
+ LayersBackend GetCompositorBackendType() const
+ {
+ return mTextureFactoryIdentifier.mParentBackend;
+ }
+
+ bool SupportsTextureBlitting() const
+ {
+ return mTextureFactoryIdentifier.mSupportsTextureBlitting;
+ }
+
+ bool SupportsPartialUploads() const
+ {
+ return mTextureFactoryIdentifier.mSupportsPartialUploads;
+ }
+
+ bool SupportsComponentAlpha() const
+ {
+ return mTextureFactoryIdentifier.mSupportsComponentAlpha;
+ }
+
+ const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
+ {
+ return mTextureFactoryIdentifier;
+ }
+
+ int32_t GetSerial() { return mSerial; }
+
+ /**
+ * Helpers for finding other related interface. These are infallible.
+ */
+ virtual TextureForwarder* GetTextureForwarder() = 0;
+ virtual LayersIPCActor* GetLayersIPCActor() = 0;
+
+protected:
+ TextureFactoryIdentifier mTextureFactoryIdentifier;
+ RefPtr<SyncObject> mSyncObject;
+
+ const int32_t mSerial;
+ static mozilla::Atomic<int32_t> sSerialCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/LayerAnimationUtils.cpp b/gfx/layers/ipc/LayerAnimationUtils.cpp
new file mode 100644
index 000000000..60c2791ac
--- /dev/null
+++ b/gfx/layers/ipc/LayerAnimationUtils.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerAnimationUtils.h"
+#include "mozilla/ComputedTimingFunction.h" // For ComputedTimingFunction
+#include "mozilla/layers/LayersMessages.h" // For TimingFunction etc.
+
+namespace mozilla {
+namespace layers {
+
+/* static */ Maybe<ComputedTimingFunction>
+AnimationUtils::TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction)
+{
+ switch (aTimingFunction.type()) {
+ case TimingFunction::Tnull_t:
+ return Nothing();
+ case TimingFunction::TCubicBezierFunction: {
+ ComputedTimingFunction result;
+ CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction();
+ result.Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2()));
+ return Some(result);
+ }
+ case TimingFunction::TStepFunction: {
+ StepFunction sf = aTimingFunction.get_StepFunction();
+ nsTimingFunction::Type type = sf.type() == 1 ?
+ nsTimingFunction::Type::StepStart :
+ nsTimingFunction::Type::StepEnd;
+ ComputedTimingFunction result;
+ result.Init(nsTimingFunction(type, sf.steps()));
+ return Some(result);
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "Function must be null, bezier or step");
+ break;
+ }
+ return Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerAnimationUtils.h b/gfx/layers/ipc/LayerAnimationUtils.h
new file mode 100644
index 000000000..fc807dbea
--- /dev/null
+++ b/gfx/layers/ipc/LayerAnimationUtils.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerAnimationUtils_h
+#define mozilla_layers_LayerAnimationUtils_h
+
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class ComputedTimingFunction;
+
+namespace layers {
+
+class TimingFunction;
+
+class AnimationUtils
+{
+public:
+ static Maybe<ComputedTimingFunction> TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerAnimationUtils_h
diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp
new file mode 100644
index 000000000..8b60d3b51
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionChild.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+#include "mozilla/layers/PImageContainerChild.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_RUNTIMEABORT, etc
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+
+void
+LayerTransactionChild::Destroy()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ // mDestroyed is used to prevent calling Send__delete__() twice.
+ // When this function is called from CompositorBridgeChild::Destroy(),
+ // under Send__delete__() call, this function is called from
+ // ShadowLayerForwarder's destructor.
+ // When it happens, IPCOpen() is still true.
+ // See bug 1004191.
+ mDestroyed = true;
+
+ SendShutdown();
+}
+
+
+PLayerChild*
+LayerTransactionChild::AllocPLayerChild()
+{
+ // we always use the "power-user" ctor
+ NS_RUNTIMEABORT("not reached");
+ return nullptr;
+}
+
+bool
+LayerTransactionChild::DeallocPLayerChild(PLayerChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+PCompositableChild*
+LayerTransactionChild::AllocPCompositableChild(const TextureInfo& aInfo)
+{
+ MOZ_ASSERT(!mDestroyed);
+ return CompositableChild::CreateActor();
+}
+
+bool
+LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor)
+{
+ CompositableChild::DestroyActor(actor);
+ return true;
+}
+
+void
+LayerTransactionChild::ActorDestroy(ActorDestroyReason why)
+{
+ mDestroyed = true;
+#ifdef MOZ_B2G
+ // Due to poor lifetime management of gralloc (and possibly shmems) we will
+ // crash at some point in the future when we get destroyed due to abnormal
+ // shutdown. Its better just to crash here. On desktop though, we have a chance
+ // of recovering.
+ if (why == AbnormalShutdown) {
+ NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at LayerTransactionChild");
+ }
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTransactionChild.h b/gfx/layers/ipc/LayerTransactionChild.h
new file mode 100644
index 000000000..3d56399f4
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+namespace layout {
+class RenderFrameChild;
+} // namespace layout
+
+namespace layers {
+
+class ShadowLayerForwarder;
+
+class LayerTransactionChild : public PLayerTransactionChild
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild)
+ /**
+ * Clean this up, finishing with SendShutDown() which will cause __delete__
+ * to be sent from the parent side.
+ *
+ * It is expected (checked with an assert) that all shadow layers
+ * created by this have already been destroyed and
+ * Send__delete__()d by the time this method is called.
+ */
+ void Destroy();
+
+ bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
+ bool IsDestroyed() const { return mDestroyed; }
+
+ void SetForwarder(ShadowLayerForwarder* aForwarder)
+ {
+ mForwarder = aForwarder;
+ }
+
+ uint64_t GetId() const { return mId; }
+
+protected:
+ explicit LayerTransactionChild(const uint64_t& aId)
+ : mForwarder(nullptr)
+ , mIPCOpen(false)
+ , mDestroyed(false)
+ , mId(aId)
+ {}
+ ~LayerTransactionChild() { }
+
+ virtual PLayerChild* AllocPLayerChild() override;
+ virtual bool DeallocPLayerChild(PLayerChild* actor) override;
+
+ virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) override;
+ virtual bool DeallocPCompositableChild(PCompositableChild* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeChild;
+ friend class layout::RenderFrameChild;
+
+ ShadowLayerForwarder* mForwarder;
+ bool mIPCOpen;
+ bool mDestroyed;
+ uint64_t mId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp
new file mode 100644
index 000000000..c30ccee5b
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -0,0 +1,1098 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionParent.h"
+#include <vector> // for vector
+#include "apz/src/AsyncPanZoomController.h"
+#include "CompositableHost.h" // for CompositableParent, Get, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ShadowLayerParent.h" // for ShadowLayerParent
+#include "CompositableTransactionParent.h" // for EditReplyVector
+#include "CompositorBridgeParent.h"
+#include "gfxPrefs.h"
+#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
+#include "mozilla/layers/CanvasLayerComposite.h"
+#include "mozilla/layers/ColorLayerComposite.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ContainerLayerComposite.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/ImageLayerComposite.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/PCompositableParent.h"
+#include "mozilla/layers/PLayerParent.h" // for PLayerParent
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/Unused.h"
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsLayoutUtils.h" // for nsLayoutUtils
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "TreeTraversal.h" // for ForEachNode
+#include "GeckoProfiler.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/AsyncCompositionManager.h"
+
+typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
+
+using mozilla::layout::RenderFrameParent;
+
+namespace mozilla {
+namespace layers {
+
+//--------------------------------------------------
+// Convenience accessors
+static ShadowLayerParent*
+cast(const PLayerParent* in)
+{
+ return const_cast<ShadowLayerParent*>(
+ static_cast<const ShadowLayerParent*>(in));
+}
+
+template<class OpCreateT>
+static ShadowLayerParent*
+AsLayerComposite(const OpCreateT& op)
+{
+ return cast(op.layerParent());
+}
+
+static ShadowLayerParent*
+AsLayerComposite(const OpSetRoot& op)
+{
+ return cast(op.rootParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpInsertAfter& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpInsertAfter& op)
+{
+ return cast(op.childLayerParent());
+}
+static ShadowLayerParent*
+ShadowAfter(const OpInsertAfter& op)
+{
+ return cast(op.afterParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpPrependChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpPrependChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRemoveChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRemoveChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRepositionChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRepositionChild& op)
+{
+ return cast(op.childLayerParent());
+}
+static ShadowLayerParent*
+ShadowAfter(const OpRepositionChild& op)
+{
+ return cast(op.afterParent());
+}
+
+static ShadowLayerParent*
+ShadowContainer(const OpRaiseToTopChild& op)
+{
+ return cast(op.containerParent());
+}
+static ShadowLayerParent*
+ShadowChild(const OpRaiseToTopChild& op)
+{
+ return cast(op.childLayerParent());
+}
+
+//--------------------------------------------------
+// LayerTransactionParent
+LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
+ CompositorBridgeParentBase* aBridge,
+ uint64_t aId)
+ : mLayerManager(aManager)
+ , mCompositorBridge(aBridge)
+ , mId(aId)
+ , mChildEpoch(0)
+ , mParentEpoch(0)
+ , mPendingTransaction(0)
+ , mPendingCompositorUpdates(0)
+ , mDestroyed(false)
+ , mIPCOpen(false)
+{
+}
+
+LayerTransactionParent::~LayerTransactionParent()
+{
+}
+
+void
+LayerTransactionParent::SetLayerManager(LayerManagerComposite* aLayerManager)
+{
+ mLayerManager = aLayerManager;
+ const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent();
+ for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) {
+ ShadowLayerParent* slp =
+ static_cast<ShadowLayerParent*>(iter.Get()->GetKey());
+ if (slp->AsLayer() && slp->AsLayer()->AsLayerComposite()) {
+ slp->AsLayer()->AsLayerComposite()->SetLayerManager(aLayerManager);
+ }
+ }
+}
+
+bool
+LayerTransactionParent::RecvShutdown()
+{
+ Destroy();
+ return Send__delete__(this);
+}
+
+void
+LayerTransactionParent::Destroy()
+{
+ const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent();
+ for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) {
+ ShadowLayerParent* slp =
+ static_cast<ShadowLayerParent*>(iter.Get()->GetKey());
+ slp->Destroy();
+ }
+ mDestroyed = true;
+}
+
+bool
+LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray<Edit>&& cset,
+ InfallibleTArray<OpDestroy>&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId)
+{
+ return RecvUpdate(Move(cset), Move(aToDestroy), aFwdTransactionId,
+ aTransactionId, targetConfig, Move(aPlugins), isFirstPaint,
+ scheduleComposite, paintSequenceNumber, isRepeatTransaction,
+ aTransactionStart, aPaintSyncId, nullptr);
+}
+
+class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender
+{
+public:
+ explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction,
+ InfallibleTArray<OpDestroy>* aDestroyActors = nullptr)
+ : mLayerTransaction(aLayerTransaction)
+ , mActorsToDestroy(aDestroyActors)
+ {
+ mLayerTransaction->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoLayerTransactionParentAsyncMessageSender()
+ {
+ mLayerTransaction->SendPendingAsyncMessages();
+ if (mActorsToDestroy) {
+ // Destroy the actors after sending the async messages because the latter may contain
+ // references to some actors.
+ for (const auto& op : *mActorsToDestroy) {
+ mLayerTransaction->DestroyActor(op);
+ }
+ }
+ }
+private:
+ LayerTransactionParent* mLayerTransaction;
+ InfallibleTArray<OpDestroy>* mActorsToDestroy;
+};
+
+bool
+LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
+ const TimeDuration& aPaintTime)
+{
+ mCompositorBridge->UpdatePaintTime(this, aPaintTime);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
+ InfallibleTArray<OpDestroy>&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId,
+ InfallibleTArray<EditReply>* reply)
+{
+ profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_START);
+ PROFILER_LABEL("LayerTransactionParent", "RecvUpdate",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeStamp updateStart = TimeStamp::Now();
+#endif
+
+ MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
+
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ for (const auto& op : aToDestroy) {
+ DestroyActor(op);
+ }
+ return true;
+ }
+
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
+ EditReplyVector replyv;
+
+ {
+ AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
+ layer_manager()->BeginTransaction();
+ }
+
+ // not all edits require an update to the hit testing tree
+ bool updateHitTestingTree = false;
+
+ for (EditArray::index_type i = 0; i < cset.Length(); ++i) {
+ const Edit& edit = cset[i];
+
+ switch (edit.type()) {
+ // Create* ops
+ case Edit::TOpCreatePaintedLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer"));
+
+ RefPtr<PaintedLayerComposite> layer =
+ layer_manager()->CreatePaintedLayerComposite();
+ AsLayerComposite(edit.get_OpCreatePaintedLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateContainerLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer"));
+
+ RefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayerComposite();
+ AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateImageLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer"));
+
+ RefPtr<ImageLayerComposite> layer =
+ layer_manager()->CreateImageLayerComposite();
+ AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateColorLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer"));
+
+ RefPtr<ColorLayerComposite> layer = layer_manager()->CreateColorLayerComposite();
+ AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateCanvasLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
+
+ RefPtr<CanvasLayerComposite> layer =
+ layer_manager()->CreateCanvasLayerComposite();
+ AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpCreateRefLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer"));
+
+ RefPtr<RefLayerComposite> layer =
+ layer_manager()->CreateRefLayerComposite();
+ AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer);
+
+ updateHitTestingTree = true;
+ break;
+ }
+
+ // Attributes
+ case Edit::TOpSetLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
+
+ const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
+ ShadowLayerParent* layerParent = AsLayerComposite(osla);
+ Layer* layer = layerParent->AsLayer();
+ if (!layer) {
+ return false;
+ }
+ const LayerAttributes& attrs = osla.attrs();
+
+ const CommonLayerAttributes& common = attrs.common();
+ layer->SetLayerBounds(common.layerBounds());
+ layer->SetVisibleRegion(common.visibleRegion());
+ layer->SetEventRegions(common.eventRegions());
+ layer->SetContentFlags(common.contentFlags());
+ layer->SetOpacity(common.opacity());
+ layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing());
+ layer->SetScrolledClip(common.scrolledClip());
+ layer->SetBaseTransform(common.transform().value());
+ layer->SetTransformIsPerspective(common.transformIsPerspective());
+ layer->SetPostScale(common.postXScale(), common.postYScale());
+ layer->SetIsFixedPosition(common.isFixedPosition());
+ if (common.isFixedPosition()) {
+ layer->SetFixedPositionData(common.fixedPositionScrollContainerId(),
+ common.fixedPositionAnchor(),
+ common.fixedPositionSides());
+ }
+ if (common.isStickyPosition()) {
+ layer->SetStickyPositionData(common.stickyScrollContainerId(),
+ common.stickyScrollRangeOuter(),
+ common.stickyScrollRangeInner());
+ }
+ layer->SetScrollbarData(common.scrollbarTargetContainerId(),
+ static_cast<Layer::ScrollDirection>(common.scrollbarDirection()),
+ common.scrollbarThumbRatio());
+ if (common.isScrollbarContainer()) {
+ layer->SetIsScrollbarContainer();
+ }
+ layer->SetMixBlendMode((gfx::CompositionOp)common.mixBlendMode());
+ layer->SetForceIsolatedGroup(common.forceIsolatedGroup());
+ if (PLayerParent* maskLayer = common.maskLayerParent()) {
+ layer->SetMaskLayer(cast(maskLayer)->AsLayer());
+ } else {
+ layer->SetMaskLayer(nullptr);
+ }
+ layer->SetAnimations(common.animations());
+ layer->SetScrollMetadata(common.scrollMetadata());
+ layer->SetDisplayListLog(common.displayListLog().get());
+
+ // The updated invalid region is added to the existing one, since we can
+ // update multiple times before the next composite.
+ layer->AddInvalidRegion(common.invalidRegion());
+
+ nsTArray<RefPtr<Layer>> maskLayers;
+ for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
+ Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
+ maskLayers.AppendElement(maskLayer);
+ }
+ layer->SetAncestorMaskLayers(maskLayers);
+
+ typedef SpecificLayerAttributes Specific;
+ const SpecificLayerAttributes& specific = attrs.specific();
+ switch (specific.type()) {
+ case Specific::Tnull_t:
+ break;
+
+ case Specific::TPaintedLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] painted layer"));
+
+ PaintedLayerComposite* paintedLayer = layerParent->AsPaintedLayerComposite();
+ if (!paintedLayer) {
+ return false;
+ }
+ const PaintedLayerAttributes& attrs =
+ specific.get_PaintedLayerAttributes();
+
+ paintedLayer->SetValidRegion(attrs.validRegion());
+
+ break;
+ }
+ case Specific::TContainerLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] container layer"));
+
+ ContainerLayerComposite* containerLayer = layerParent->AsContainerLayerComposite();
+ if (!containerLayer) {
+ return false;
+ }
+ const ContainerLayerAttributes& attrs =
+ specific.get_ContainerLayerAttributes();
+ containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
+ containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
+ containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
+ attrs.presShellResolution());
+ containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
+
+ break;
+ }
+ case Specific::TColorLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] color layer"));
+
+ ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
+ if (!colorLayer) {
+ return false;
+ }
+ colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value());
+ colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TCanvasLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] canvas layer"));
+
+ CanvasLayerComposite* canvasLayer = layerParent->AsCanvasLayerComposite();
+ if (!canvasLayer) {
+ return false;
+ }
+ canvasLayer->SetSamplingFilter(specific.get_CanvasLayerAttributes().samplingFilter());
+ canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TRefLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] ref layer"));
+
+ RefLayerComposite* refLayer = layerParent->AsRefLayerComposite();
+ if (!refLayer) {
+ return false;
+ }
+ refLayer->SetReferentId(specific.get_RefLayerAttributes().id());
+ refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride());
+ break;
+ }
+ case Specific::TImageLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] image layer"));
+
+ ImageLayerComposite* imageLayer = layerParent->AsImageLayerComposite();
+ if (!imageLayer) {
+ return false;
+ }
+ const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes();
+ imageLayer->SetSamplingFilter(attrs.samplingFilter());
+ imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode());
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpSetDiagnosticTypes: {
+ mLayerManager->GetCompositor()->SetDiagnosticTypes(
+ edit.get_OpSetDiagnosticTypes().diagnostics());
+ break;
+ }
+ case Edit::TOpWindowOverlayChanged: {
+ mLayerManager->SetWindowOverlayChanged();
+ break;
+ }
+ // Tree ops
+ case Edit::TOpSetRoot: {
+ MOZ_LAYERS_LOG(("[ParentSide] SetRoot"));
+
+ Layer* newRoot = AsLayerComposite(edit.get_OpSetRoot())->AsLayer();
+ if (!newRoot) {
+ return false;
+ }
+ if (newRoot->GetParent()) {
+ // newRoot is not a root!
+ return false;
+ }
+ mRoot = newRoot;
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpInsertAfter: {
+ MOZ_LAYERS_LOG(("[ParentSide] InsertAfter"));
+
+ const OpInsertAfter& oia = edit.get_OpInsertAfter();
+ Layer* child = ShadowChild(oia)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(oia)->AsContainerLayerComposite();
+ if (!container ||
+ !container->InsertAfter(child, ShadowAfter(oia)->AsLayer()))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpPrependChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] PrependChild"));
+
+ const OpPrependChild& oac = edit.get_OpPrependChild();
+ Layer* child = ShadowChild(oac)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(oac)->AsContainerLayerComposite();
+ if (!container ||
+ !container->InsertAfter(child, nullptr))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRemoveChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RemoveChild"));
+
+ const OpRemoveChild& orc = edit.get_OpRemoveChild();
+ Layer* childLayer = ShadowChild(orc)->AsLayer();
+ if (!childLayer) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RemoveChild(childLayer))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRepositionChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RepositionChild"));
+
+ const OpRepositionChild& orc = edit.get_OpRepositionChild();
+ Layer* child = ShadowChild(orc)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RepositionChild(child, ShadowAfter(orc)->AsLayer()))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TOpRaiseToTopChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild"));
+
+ const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild();
+ Layer* child = ShadowChild(rtc)->AsLayer();
+ if (!child) {
+ return false;
+ }
+ ContainerLayerComposite* container = ShadowContainer(rtc)->AsContainerLayerComposite();
+ if (!container ||
+ !container->RepositionChild(child, nullptr))
+ {
+ return false;
+ }
+
+ updateHitTestingTree = true;
+ break;
+ }
+ case Edit::TCompositableOperation: {
+ if (!ReceiveCompositableUpdate(edit.get_CompositableOperation(),
+ replyv)) {
+ return false;
+ }
+ break;
+ }
+ case Edit::TOpAttachCompositable: {
+ const OpAttachCompositable& op = edit.get_OpAttachCompositable();
+ CompositableHost* host = CompositableHost::FromIPDLActor(op.compositableParent());
+ if (mPendingCompositorUpdates) {
+ // Do not attach compositables from old layer trees. Return true since
+ // content cannot handle errors.
+ return true;
+ }
+ if (!Attach(cast(op.layerParent()), host, false)) {
+ return false;
+ }
+ host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+ break;
+ }
+ case Edit::TOpAttachAsyncCompositable: {
+ const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
+ PCompositableParent* compositableParent = CompositableMap::Get(op.containerID());
+ if (!compositableParent) {
+ NS_ERROR("CompositableParent not found in the map");
+ return false;
+ }
+ if (mPendingCompositorUpdates) {
+ // Do not attach compositables from old layer trees. Return true since
+ // content cannot handle errors.
+ return true;
+ }
+ CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent);
+ if (!Attach(cast(op.layerParent()), host, true)) {
+ return false;
+ }
+ host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+ }
+
+ mCompositorBridge->ShadowLayersUpdated(this, aTransactionId, targetConfig,
+ aPlugins, isFirstPaint, scheduleComposite,
+ paintSequenceNumber, isRepeatTransaction,
+ aPaintSyncId, updateHitTestingTree);
+
+ {
+ AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
+ layer_manager()->EndTransaction(TimeStamp(), LayerManager::END_NO_IMMEDIATE_REDRAW);
+ }
+
+ if (reply) {
+ reply->SetCapacity(replyv.size());
+ if (replyv.size() > 0) {
+ reply->AppendElements(&replyv.front(), replyv.size());
+ }
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ int compositeTime = (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds();
+ if (compositeTime > 15) {
+ printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", compositeTime);
+ }
+#endif
+
+ // Enable visual warning for long transaction when draw FPS option is enabled
+ bool drawFps = gfxPrefs::LayersDrawFPS();
+ if (drawFps) {
+ uint32_t visualWarningTrigger = gfxPrefs::LayerTransactionWarning();
+ // The default theshold is 200ms to trigger, hit red when it take 4 times longer
+ TimeDuration latency = TimeStamp::Now() - aTransactionStart;
+ if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) {
+ float severity = (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)).ToMilliseconds() /
+ (4 * visualWarningTrigger);
+ if (severity > 1.f) {
+ severity = 1.f;
+ }
+ mLayerManager->VisualFrameWarning(severity);
+ PR_LogPrint("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms",
+ OtherPid(),
+ latency.ToMilliseconds());
+ }
+ }
+
+ profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_END);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
+{
+ mChildEpoch = aLayerObserverEpoch;
+ return true;
+}
+
+bool
+LayerTransactionParent::ShouldParentObserveEpoch()
+{
+ if (mParentEpoch == mChildEpoch) {
+ return false;
+ }
+
+ mParentEpoch = mChildEpoch;
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime)
+{
+ return mCompositorBridge->SetTestSampleTime(this, aTime);
+}
+
+bool
+LayerTransactionParent::RecvLeaveTestMode()
+{
+ mCompositorBridge->LeaveTestMode(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAnimationOpacity(PLayerParent* aParent,
+ float* aOpacity,
+ bool* aHasAnimationOpacity)
+{
+ *aHasAnimationOpacity = false;
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ Layer* layer = cast(aParent)->AsLayer();
+ if (!layer) {
+ return false;
+ }
+
+ mCompositorBridge->ApplyAsyncProperties(this);
+
+ if (!layer->AsLayerComposite()->GetShadowOpacitySetByAnimation()) {
+ return true;
+ }
+
+ *aOpacity = layer->GetLocalOpacity();
+ *aHasAnimationOpacity = true;
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
+ MaybeTransform* aTransform)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ Layer* layer = cast(aParent)->AsLayer();
+ if (!layer) {
+ return false;
+ }
+
+ // Make sure we apply the latest animation style or else we can end up with
+ // a race between when we temporarily clear the animation transform (in
+ // CompositorBridgeParent::SetShadowProperties) and when animation recalculates
+ // the value.
+ mCompositorBridge->ApplyAsyncProperties(this);
+
+ // This method is specific to transforms applied by animation.
+ // This is because this method uses the information stored with an animation
+ // such as the origin of the reference frame corresponding to the layer, to
+ // recover the untranslated transform from the shadow transform. For
+ // transforms that are not set by animation we don't have this information
+ // available.
+ if (!layer->AsLayerComposite()->GetShadowTransformSetByAnimation()) {
+ *aTransform = mozilla::void_t();
+ return true;
+ }
+
+ // The following code recovers the untranslated transform
+ // from the shadow transform by undoing the translations in
+ // AsyncCompositionManager::SampleValue.
+
+ Matrix4x4 transform = layer->AsLayerComposite()->GetShadowBaseTransform();
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ // Undo the scale transform applied by AsyncCompositionManager::SampleValue
+ transform.PostScale(1.0f/c->GetInheritedXScale(),
+ 1.0f/c->GetInheritedYScale(),
+ 1.0f);
+ }
+ float scale = 1;
+ Point3D scaledOrigin;
+ Point3D transformOrigin;
+ for (uint32_t i=0; i < layer->GetAnimations().Length(); i++) {
+ if (layer->GetAnimations()[i].data().type() == AnimationData::TTransformData) {
+ const TransformData& data = layer->GetAnimations()[i].data().get_TransformData();
+ scale = data.appUnitsPerDevPixel();
+ scaledOrigin =
+ Point3D(NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)),
+ NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)),
+ 0.0f);
+ transformOrigin = data.transformOrigin();
+ break;
+ }
+ }
+
+ // If our parent isn't a perspective layer, then the offset into reference
+ // frame coordinates will have been applied to us. Add an inverse translation
+ // to cancel it out.
+ if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
+ transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+ }
+
+ // Undo the rebasing applied by
+ // nsDisplayTransform::GetResultingTransformMatrixInternal
+ transform.ChangeBasis(-transformOrigin);
+
+ // Convert to CSS pixels (this undoes the operations performed by
+ // nsStyleTransformMatrix::ProcessTranslatePart which is called from
+ // nsDisplayTransform::GetResultingTransformMatrix)
+ double devPerCss =
+ double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
+ transform._41 *= devPerCss;
+ transform._42 *= devPerCss;
+ transform._43 *= devPerCss;
+
+ *aTransform = transform;
+ return true;
+}
+
+static AsyncPanZoomController*
+GetAPZCForViewID(Layer* aLayer, FrameMetrics::ViewID aScrollID)
+{
+ AsyncPanZoomController* resultApzc = nullptr;
+ ForEachNode<ForwardIterator>(
+ aLayer,
+ [aScrollID, &resultApzc] (Layer* layer)
+ {
+ for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+ if (layer->GetFrameMetrics(i).GetScrollId() == aScrollID) {
+ resultApzc = layer->GetAsyncPanZoomController(i);
+ return TraversalFlag::Abort;
+ }
+ }
+ return TraversalFlag::Continue;
+ });
+ return resultApzc;
+}
+
+bool
+LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollID,
+ const float& aX, const float& aY)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
+ if (!controller) {
+ return false;
+ }
+ controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY));
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID,
+ const float& aValue)
+{
+ if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+ return false;
+ }
+
+ AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
+ if (!controller) {
+ return false;
+ }
+ controller->SetTestAsyncZoom(LayerToParentLayerScale(aValue));
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvFlushApzRepaints()
+{
+ mCompositorBridge->FlushApzRepaints(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
+{
+ mCompositorBridge->GetAPZTestData(this, aOutData);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvRequestProperty(const nsString& aProperty, float* aValue)
+{
+ if (aProperty.Equals(NS_LITERAL_STRING("overdraw"))) {
+ *aValue = layer_manager()->GetCompositor()->GetFillRatio();
+ } else if (aProperty.Equals(NS_LITERAL_STRING("missed_hwc"))) {
+ *aValue = layer_manager()->LastFrameMissedHWC() ? 1 : 0;
+ } else {
+ *aValue = -1;
+ }
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets)
+{
+ mCompositorBridge->SetConfirmedTargetAPZC(this, aBlockId, aTargets);
+ return true;
+}
+
+bool
+LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent,
+ CompositableHost* aCompositable,
+ bool aIsAsync)
+{
+ if (!aCompositable) {
+ return false;
+ }
+
+ Layer* baselayer = aLayerParent->AsLayer();
+ if (!baselayer) {
+ return false;
+ }
+ LayerComposite* layer = baselayer->AsLayerComposite();
+ if (!layer) {
+ return false;
+ }
+
+ Compositor* compositor
+ = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
+
+ if (!layer->SetCompositableHost(aCompositable)) {
+ // not all layer types accept a compositable, see bug 967824
+ return false;
+ }
+ aCompositable->Attach(aLayerParent->AsLayer(),
+ compositor,
+ aIsAsync
+ ? CompositableHost::ALLOW_REATTACH
+ | CompositableHost::KEEP_ATTACHED
+ : CompositableHost::NO_FLAGS);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvClearCachedResources()
+{
+ if (mRoot) {
+ // NB: |mRoot| here is the *child* context's root. In this parent
+ // context, it's just a subtree root. We need to scope the clear
+ // of resources to exactly that subtree, so we specify it here.
+ mLayerManager->ClearCachedResources(mRoot);
+ }
+ mCompositorBridge->NotifyClearCachedResources(this);
+ return true;
+}
+
+bool
+LayerTransactionParent::RecvForceComposite()
+{
+ mCompositorBridge->ForceComposite(this);
+ return true;
+}
+
+PLayerParent*
+LayerTransactionParent::AllocPLayerParent()
+{
+ return new ShadowLayerParent();
+}
+
+bool
+LayerTransactionParent::DeallocPLayerParent(PLayerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PCompositableParent*
+LayerTransactionParent::AllocPCompositableParent(const TextureInfo& aInfo)
+{
+ return CompositableHost::CreateIPDLActor(this, aInfo, 0);
+}
+
+bool
+LayerTransactionParent::DeallocPCompositableParent(PCompositableParent* aActor)
+{
+ return CompositableHost::DestroyIPDLActor(aActor);
+}
+
+void
+LayerTransactionParent::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+bool
+LayerTransactionParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+ return PLayerTransactionParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+LayerTransactionParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+
+ return PLayerTransactionParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (!mIPCOpen || mDestroyed) {
+ return;
+ }
+ PLayerTransactionParent::DeallocShmem(aShmem);
+}
+
+bool LayerTransactionParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+LayerTransactionParent::SendPendingAsyncMessages()
+{
+ mCompositorBridge->SendPendingAsyncMessages();
+}
+
+void
+LayerTransactionParent::SetAboutToSendAsyncMessages()
+{
+ mCompositorBridge->SetAboutToSendAsyncMessages();
+}
+
+void
+LayerTransactionParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h
new file mode 100644
index 000000000..d92aa0358
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableTransactionParent.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+
+namespace mozilla {
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layout {
+class RenderFrameParent;
+} // namespace layout
+
+namespace layers {
+
+class Layer;
+class LayerManagerComposite;
+class ShadowLayerParent;
+class CompositableParent;
+class CompositorBridgeParentBase;
+
+class LayerTransactionParent final : public PLayerTransactionParent,
+ public CompositableParentManager,
+ public ShmemAllocator
+{
+ typedef mozilla::layout::RenderFrameParent RenderFrameParent;
+ typedef InfallibleTArray<Edit> EditArray;
+ typedef InfallibleTArray<OpDestroy> OpDestroyArray;
+ typedef InfallibleTArray<EditReply> EditReplyArray;
+ typedef InfallibleTArray<PluginWindowData> PluginsArray;
+
+public:
+ LayerTransactionParent(LayerManagerComposite* aManager,
+ CompositorBridgeParentBase* aBridge,
+ uint64_t aId);
+
+protected:
+ ~LayerTransactionParent();
+
+public:
+ void Destroy();
+
+ LayerManagerComposite* layer_manager() const { return mLayerManager; }
+
+ void SetLayerManager(LayerManagerComposite* aLayerManager);
+
+ uint64_t GetId() const { return mId; }
+ Layer* GetRoot() const { return mRoot; }
+
+ uint64_t GetChildEpoch() const { return mChildEpoch; }
+ bool ShouldParentObserveEpoch();
+
+ virtual ShmemAllocator* AsShmemAllocator() override { return this; }
+
+ virtual bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ virtual void DeallocShmem(ipc::Shmem& aShmem) override;
+
+ virtual bool IsSameProcess() const override;
+
+ const uint64_t& GetPendingTransactionId() { return mPendingTransaction; }
+ void SetPendingTransactionId(uint64_t aId) { mPendingTransaction = aId; }
+
+ // CompositableParentManager
+ virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ virtual void SendPendingAsyncMessages() override;
+
+ virtual void SetAboutToSendAsyncMessages() override;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+
+ virtual base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+
+ void AddPendingCompositorUpdate() {
+ mPendingCompositorUpdates++;
+ }
+ void SetPendingCompositorUpdates(uint32_t aCount) {
+ // Only called after construction.
+ MOZ_ASSERT(mPendingCompositorUpdates == 0);
+ mPendingCompositorUpdates = aCount;
+ }
+ void AcknowledgeCompositorUpdate() {
+ MOZ_ASSERT(mPendingCompositorUpdates > 0);
+ mPendingCompositorUpdates--;
+ }
+
+protected:
+ virtual bool RecvShutdown() override;
+
+ virtual bool RecvPaintTime(const uint64_t& aTransactionId,
+ const TimeDuration& aPaintTime) override;
+
+ virtual bool RecvUpdate(EditArray&& cset,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId,
+ EditReplyArray* reply) override;
+
+ virtual bool RecvUpdateNoSwap(EditArray&& cset,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId,
+ const uint64_t& aTransactionId,
+ const TargetConfig& targetConfig,
+ PluginsArray&& aPlugins,
+ const bool& isFirstPaint,
+ const bool& scheduleComposite,
+ const uint32_t& paintSequenceNumber,
+ const bool& isRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ const int32_t& aPaintSyncId) override;
+
+ virtual bool RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
+
+ virtual bool RecvClearCachedResources() override;
+ virtual bool RecvForceComposite() override;
+ virtual bool RecvSetTestSampleTime(const TimeStamp& aTime) override;
+ virtual bool RecvLeaveTestMode() override;
+ virtual bool RecvGetAnimationOpacity(PLayerParent* aParent,
+ float* aOpacity,
+ bool* aHasAnimationOpacity) override;
+ virtual bool RecvGetAnimationTransform(PLayerParent* aParent,
+ MaybeTransform* aTransform)
+ override;
+ virtual bool RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId,
+ const float& aX, const float& aY) override;
+ virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId,
+ const float& aValue) override;
+ virtual bool RecvFlushApzRepaints() override;
+ virtual bool RecvGetAPZTestData(APZTestData* aOutData) override;
+ virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override;
+ virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ virtual PLayerParent* AllocPLayerParent() override;
+ virtual bool DeallocPLayerParent(PLayerParent* actor) override;
+
+ virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) override;
+ virtual bool DeallocPCompositableParent(PCompositableParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool Attach(ShadowLayerParent* aLayerParent,
+ CompositableHost* aCompositable,
+ bool aIsAsyncVideo);
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeParent;
+ friend class CrossProcessCompositorBridgeParent;
+ friend class layout::RenderFrameParent;
+
+private:
+ RefPtr<LayerManagerComposite> mLayerManager;
+ CompositorBridgeParentBase* mCompositorBridge;
+ // Hold the root because it might be grafted under various
+ // containers in the "real" layer tree
+ RefPtr<Layer> mRoot;
+ // When this is nonzero, it refers to a layer tree owned by the
+ // compositor thread. It is always true that
+ // mId != 0 => mRoot == null
+ // because the "real tree" is owned by the compositor.
+ uint64_t mId;
+
+ // These fields keep track of the latest epoch values in the child and the
+ // parent. mChildEpoch is the latest epoch value received from the child.
+ // mParentEpoch is the latest epoch value that we have told TabParent about
+ // (via ObserveLayerUpdate).
+ uint64_t mChildEpoch;
+ uint64_t mParentEpoch;
+
+ uint64_t mPendingTransaction;
+
+ // Number of compositor updates we're waiting for the child to
+ // acknowledge.
+ uint32_t mPendingCompositorUpdates;
+
+ // When the widget/frame/browser stuff in this process begins its
+ // destruction process, we need to Disconnect() all the currently
+ // live shadow layers, because some of them might be orphaned from
+ // the layer tree. This happens in Destroy() above. After we
+ // Destroy() ourself, there's a window in which that information
+ // hasn't yet propagated back to the child side and it might still
+ // send us layer transactions. We want to ignore those transactions
+ // because they refer to "zombie layers" on this side. So, we track
+ // that state with |mDestroyed|. This is similar to, but separate
+ // from, |mLayerManager->IsDestroyed()|; we might have had Destroy()
+ // called on us but the mLayerManager might not be destroyed, or
+ // vice versa. In both cases though, we want to ignore shadow-layer
+ // transactions posted by the child.
+
+ bool mDestroyed;
+
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.cpp b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
new file mode 100644
index 000000000..9dac68688
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeOwnerTracker.h"
+
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/dom/ContentParent.h" // for ContentParent
+#include "mozilla/gfx/GPUChild.h" // for GPUChild
+#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager
+
+#include <utility> // for std::make_pair
+
+namespace mozilla {
+namespace layers {
+
+static StaticAutoPtr<LayerTreeOwnerTracker> sSingleton;
+
+LayerTreeOwnerTracker::LayerTreeOwnerTracker() :
+ mLayerIdsLock("LayerTreeOwnerTrackerLock")
+{
+}
+
+void
+LayerTreeOwnerTracker::Initialize()
+{
+ MOZ_ASSERT(!sSingleton);
+ sSingleton = new LayerTreeOwnerTracker();
+}
+
+void
+LayerTreeOwnerTracker::Shutdown()
+{
+ sSingleton = nullptr;
+}
+
+LayerTreeOwnerTracker*
+LayerTreeOwnerTracker::Get()
+{
+ return sSingleton;
+}
+
+void
+LayerTreeOwnerTracker::Map(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ // Add the mapping to the list
+ mLayerIds[aLayersId] = aProcessId;
+}
+
+void
+LayerTreeOwnerTracker::Unmap(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId);
+ mLayerIds.erase(aLayersId);
+}
+
+bool
+LayerTreeOwnerTracker::IsMapped(uint64_t aLayersId, base::ProcessId aProcessId)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ auto iter = mLayerIds.find(aLayersId);
+ return iter != mLayerIds.end() && iter->second == aProcessId;
+}
+
+void
+LayerTreeOwnerTracker::Iterate(function<void(uint64_t aLayersId, base::ProcessId aProcessId)> aCallback)
+{
+ MutexAutoLock lock(mLayerIdsLock);
+
+ for (const auto& iter : mLayerIds) {
+ aCallback(iter.first, iter.second);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.h b/gfx/layers/ipc/LayerTreeOwnerTracker.h
new file mode 100644
index 000000000..71f734bbf
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerTreeOwnerTracker_h
+#define mozilla_layers_LayerTreeOwnerTracker_h
+
+#include "base/process.h" // for base::ProcessId
+#include "mozilla/Mutex.h" // for mozilla::Mutex
+#include "mozilla/Function.h"
+
+#include <map>
+
+namespace mozilla {
+
+namespace dom {
+ class ContentParent;
+}
+
+namespace layers {
+
+/**
+ * A utility class for tracking which content processes should be allowed
+ * to access which layer trees.
+ *
+ * ProcessId's are used to track which content process can access the layer
+ * tree, and in the case of nested browser's we use the top level content
+ * processes' ProcessId.
+ *
+ * This class is only available in the main process and gpu process. Mappings
+ * are synced from main process to the gpu process. The actual syncing happens
+ * in GPUProcessManager, and so this class should not be used directly.
+ */
+class LayerTreeOwnerTracker final
+{
+public:
+ static void Initialize();
+ static void Shutdown();
+ static LayerTreeOwnerTracker* Get();
+
+ /**
+ * Map aLayersId and aProcessId together so that that process
+ * can access that layer tree.
+ */
+ void Map(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Remove an existing mapping.
+ */
+ void Unmap(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Checks whether it is okay for aProcessId to access aLayersId.
+ */
+ bool IsMapped(uint64_t aLayersId, base::ProcessId aProcessId);
+
+ void Iterate(function<void(uint64_t aLayersId, base::ProcessId aProcessId)> aCallback);
+
+private:
+ LayerTreeOwnerTracker();
+
+ mozilla::Mutex mLayerIdsLock;
+ std::map<uint64_t, base::ProcessId> mLayerIds;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerTreeOwnerTracker_h
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
new file mode 100644
index 000000000..dbbb3649a
--- /dev/null
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -0,0 +1,499 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PCompositable;
+include protocol PCompositorBridge;
+include protocol PLayer;
+include protocol PImageContainer;
+include protocol PRenderFrame;
+include protocol PTexture;
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "ImageLayers.h";
+
+using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::Color from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using nscoord from "nsCoord.h";
+using struct nsRect from "nsRect.h";
+using struct nsPoint from "nsPoint.h";
+using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
+using nsCSSPropertyID from "nsCSSPropertyID.h";
+using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::LayerMargin from "Units.h";
+using mozilla::LayerPoint from "Units.h";
+using mozilla::LayerRect from "Units.h";
+using mozilla::LayerIntRegion from "Units.h";
+using mozilla::ParentLayerIntRect from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
+
+namespace mozilla {
+namespace layers {
+
+struct TargetConfig {
+ IntRect naturalBounds;
+ ScreenRotation rotation;
+ ScreenOrientationInternal orientation;
+ nsIntRegion clearRegion;
+};
+
+// Create a shadow layer for |layer|
+struct OpCreatePaintedLayer { PLayer layer; };
+struct OpCreateContainerLayer { PLayer layer; };
+struct OpCreateImageLayer { PLayer layer; };
+struct OpCreateColorLayer { PLayer layer; };
+struct OpCreateCanvasLayer { PLayer layer; };
+struct OpCreateRefLayer { PLayer layer; };
+
+struct OpAttachCompositable {
+ PLayer layer;
+ PCompositable compositable;
+};
+
+struct OpAttachAsyncCompositable {
+ PLayer layer;
+ uint64_t containerID;
+};
+
+struct ThebesBufferData {
+ IntRect rect;
+ IntPoint rotation;
+};
+
+struct CubicBezierFunction {
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct StepFunction {
+ int steps;
+ // 1 = nsTimingFunction::StepStart, 2 = nsTimingFunction::StepEnd
+ int type;
+};
+
+union TimingFunction {
+ null_t;
+ CubicBezierFunction;
+ StepFunction;
+};
+
+// Send the angle with units rather than sending all angles in radians
+// to avoid having floating point error introduced by unit switching.
+struct CSSAngle {
+ float value;
+ int unit; // an nsCSSUnit that is valid for angles
+};
+
+struct LayerColor { Color value; };
+struct Perspective { float value; };
+struct RotationX { CSSAngle angle; };
+struct RotationY { CSSAngle angle; };
+struct RotationZ { CSSAngle angle; };
+struct Rotation { CSSAngle angle; };
+struct Rotation3D {
+ float x;
+ float y;
+ float z;
+ CSSAngle angle;
+};
+struct Scale {
+ float x;
+ float y;
+ float z;
+};
+struct Skew { CSSAngle x; CSSAngle y; };
+struct SkewX { CSSAngle x; };
+struct SkewY { CSSAngle y; };
+struct TransformMatrix { Matrix4x4 value; };
+struct Translation {
+ float x;
+ float y;
+ float z;
+};
+
+union TransformFunction {
+ Perspective;
+ RotationX;
+ RotationY;
+ RotationZ;
+ Rotation;
+ Rotation3D;
+ Scale;
+ Skew;
+ SkewX;
+ SkewY;
+ Translation;
+ TransformMatrix;
+};
+
+union Animatable {
+ float;
+ TransformFunction[];
+};
+
+struct AnimationSegment {
+ Animatable startState;
+ Animatable endState;
+ float startPortion;
+ float endPortion;
+ TimingFunction sampleFn;
+};
+
+// Transforms need extra information to correctly convert the list of transform
+// functions to a Matrix4x4 that can be applied directly to the layer.
+struct TransformData {
+ // the origin of the frame being transformed in app units
+ nsPoint origin;
+ // the transform-origin property for the transform in device pixels
+ Point3D transformOrigin;
+ nsRect bounds;
+ int32_t appUnitsPerDevPixel;
+};
+
+union AnimationData {
+ null_t;
+ TransformData;
+};
+
+struct Animation {
+ TimeStamp startTime;
+ TimeDuration delay;
+ // The value of the animation's current time at the moment it was created.
+ // For animations that are waiting to start, their startTime will be null.
+ // Once the animation is ready to start, we calculate an appropriate value
+ // of startTime such that we begin playback from initialCurrentTime.
+ TimeDuration initialCurrentTime;
+ TimeDuration duration;
+ // For each frame, the interpolation point is computed based on the
+ // startTime, the direction, the duration, and the current time.
+ // The segments must uniquely cover the portion from 0.0 to 1.0
+ AnimationSegment[] segments;
+ // Number of times to repeat the animation, including positive infinity.
+ // Values <= 0 mean the animation will not play (although events are still
+ // dispatched on the main thread).
+ float iterations;
+ float iterationStart;
+ // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
+ uint8_t direction;
+ // This uses dom::FillMode.
+ uint8_t fillMode;
+ nsCSSPropertyID property;
+ AnimationData data;
+ float playbackRate;
+ // This is used in the transformed progress calculation.
+ TimingFunction easingFunction;
+ uint8_t iterationComposite;
+};
+
+// Change a layer's attributes
+struct CommonLayerAttributes {
+ IntRect layerBounds;
+ LayerIntRegion visibleRegion;
+ EventRegions eventRegions;
+ TransformMatrix transform;
+ bool transformIsPerspective;
+ float postXScale;
+ float postYScale;
+ uint32_t contentFlags;
+ float opacity;
+ bool useClipRect;
+ ParentLayerIntRect clipRect;
+ MaybeLayerClip scrolledClip;
+ bool isFixedPosition;
+ uint64_t fixedPositionScrollContainerId;
+ LayerPoint fixedPositionAnchor;
+ int32_t fixedPositionSides;
+ bool isStickyPosition;
+ uint64_t stickyScrollContainerId;
+ LayerRect stickyScrollRangeOuter;
+ LayerRect stickyScrollRangeInner;
+ uint64_t scrollbarTargetContainerId;
+ uint32_t scrollbarDirection;
+ float scrollbarThumbRatio;
+ bool isScrollbarContainer;
+ int8_t mixBlendMode;
+ bool forceIsolatedGroup;
+ nullable PLayer maskLayer;
+ PLayer[] ancestorMaskLayers;
+ // Animated colors will only honored for ColorLayers.
+ Animation[] animations;
+ nsIntRegion invalidRegion;
+ ScrollMetadata[] scrollMetadata;
+ nsCString displayListLog;
+};
+
+struct PaintedLayerAttributes {
+ nsIntRegion validRegion;
+};
+struct ContainerLayerAttributes {
+ float preXScale;
+ float preYScale;
+ float inheritedXScale;
+ float inheritedYScale;
+ float presShellResolution;
+ bool scaleToResolution;
+ EventRegionsOverride eventRegionsOverride;
+};
+struct ColorLayerAttributes { LayerColor color; IntRect bounds; };
+struct CanvasLayerAttributes { SamplingFilter samplingFilter; IntRect bounds; };
+struct RefLayerAttributes {
+ int64_t id;
+ // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override
+ // explicitly here.
+ EventRegionsOverride eventRegionsOverride;
+};
+struct ImageLayerAttributes { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; };
+
+union SpecificLayerAttributes {
+ null_t;
+ PaintedLayerAttributes;
+ ContainerLayerAttributes;
+ ColorLayerAttributes;
+ CanvasLayerAttributes;
+ RefLayerAttributes;
+ ImageLayerAttributes;
+};
+
+struct LayerAttributes {
+ CommonLayerAttributes common;
+ SpecificLayerAttributes specific;
+};
+
+// See nsIWidget Configurations
+struct PluginWindowData {
+ uintptr_t windowId;
+ LayoutDeviceIntRect[] clip;
+ LayoutDeviceIntRect bounds;
+ bool visible;
+};
+
+struct OpSetLayerAttributes {
+ PLayer layer;
+ LayerAttributes attrs;
+};
+
+// Monkey with the tree structure
+struct OpSetRoot { PLayer root; };
+struct OpInsertAfter { PLayer container; PLayer childLayer; PLayer after; };
+struct OpPrependChild { PLayer container; PLayer childLayer; };
+struct OpRemoveChild { PLayer container; PLayer childLayer; };
+struct OpRepositionChild { PLayer container; PLayer childLayer; PLayer after; };
+struct OpRaiseToTopChild { PLayer container; PLayer childLayer; };
+
+struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
+struct OpWindowOverlayChanged { };
+
+struct ShmemSection {
+ Shmem shmem;
+ uint32_t offset;
+ size_t size;
+};
+
+union ReadLockDescriptor {
+ ShmemSection;
+ uintptr_t;
+ null_t;
+};
+
+union MaybeTexture {
+ PTexture;
+ null_t;
+};
+
+struct TexturedTileDescriptor {
+ PTexture texture;
+ MaybeTexture textureOnWhite;
+ IntRect updateRect;
+ ReadLockDescriptor sharedLock;
+ ReadLockDescriptor sharedLockOnWhite;
+ bool wasPlaceholder;
+};
+
+struct PlaceholderTileDescriptor {
+};
+
+union TileDescriptor {
+ TexturedTileDescriptor;
+ PlaceholderTileDescriptor;
+};
+
+struct SurfaceDescriptorTiles {
+ nsIntRegion validRegion;
+ TileDescriptor[] tiles;
+ IntPoint tileOrigin;
+ IntSize tileSize;
+ int firstTileX;
+ int firstTileY;
+ int retainedWidth;
+ int retainedHeight;
+ float resolution;
+ float frameXResolution;
+ float frameYResolution;
+ bool isProgressive;
+};
+
+struct OpUseTiledLayerBuffer {
+ SurfaceDescriptorTiles tileLayerDescriptor;
+};
+
+struct OpUseOverlaySource {
+ OverlaySource overlay;
+ IntRect picture;
+};
+
+struct OpPaintTextureRegion {
+ ThebesBufferData bufferData;
+ nsIntRegion updatedRegion;
+};
+
+/**
+ * Tells the CompositableHost to remove the corresponding TextureHost
+ */
+struct OpRemoveTexture {
+ PTexture texture;
+};
+
+struct TimedTexture {
+ PTexture texture;
+ ReadLockDescriptor sharedLock;
+ TimeStamp timeStamp;
+ IntRect picture;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+/**
+ * Tells the compositor-side which textures to use (for example, as front buffer
+ * if there are several textures for double buffering).
+ * This provides a list of textures with timestamps, ordered by timestamp.
+ * The newest texture whose timestamp is <= the current time is rendered
+ * (where null is considered less than every other timestamp). If there is no
+ * such texture, the first texture is rendered.
+ * The first timestamp value can be null, but the others must not be.
+ * The list must not be empty.
+ */
+struct OpUseTexture {
+ TimedTexture[] textures;
+};
+
+struct OpUseComponentAlphaTextures {
+ PTexture textureOnBlack;
+ PTexture textureOnWhite;
+ ReadLockDescriptor sharedLockBlack;
+ ReadLockDescriptor sharedLockWhite;
+};
+
+union MaybeRegion {
+ nsIntRegion;
+ null_t;
+};
+
+struct OpNotifyNotUsed {
+ uint64_t TextureId;
+ uint64_t fwdTransactionId;
+};
+
+union CompositableOperationDetail {
+ OpPaintTextureRegion;
+
+ OpUseTiledLayerBuffer;
+
+ OpRemoveTexture;
+
+ OpUseTexture;
+ OpUseComponentAlphaTextures;
+ OpUseOverlaySource;
+};
+
+struct CompositableOperation {
+ PCompositable compositable;
+ CompositableOperationDetail detail;
+};
+
+// A unit of a changeset; a set of these comprise a changeset
+// If adding a new edit type that requires the hit testing tree to be updated,
+// set the updateHitTestingTree flag to true in RecvUpdate()
+union Edit {
+ OpCreatePaintedLayer;
+ OpCreateContainerLayer;
+ OpCreateImageLayer;
+ OpCreateColorLayer;
+ OpCreateCanvasLayer;
+ OpCreateRefLayer;
+
+ OpSetLayerAttributes;
+ OpSetDiagnosticTypes;
+ OpWindowOverlayChanged;
+
+ OpSetRoot;
+ OpInsertAfter;
+ OpPrependChild;
+ OpRemoveChild;
+ OpRepositionChild;
+ OpRaiseToTopChild;
+
+ OpAttachCompositable;
+ OpAttachAsyncCompositable;
+
+ CompositableOperation;
+};
+
+// Operations related to destroying resources, always handled after the other
+// operations for safety.
+union OpDestroy {
+ PTexture;
+ PCompositable;
+};
+
+// Replies to operations
+
+struct OpContentBufferSwap {
+ PCompositable compositable;
+ nsIntRegion frontUpdatedRegion;
+};
+
+/**
+ * An ImageCompositeNotification is sent the first time a particular
+ * image is composited by an ImageHost.
+ */
+struct ImageCompositeNotification {
+ PImageContainer imageContainer;
+ TimeStamp imageTimeStamp;
+ TimeStamp firstCompositeTimeStamp;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+// Unit of a "changeset reply". This is a weird abstraction, probably
+// only to be used for buffer swapping.
+union EditReply {
+ OpContentBufferSwap;
+};
+
+union AsyncParentMessageData {
+ OpNotifyNotUsed;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh
new file mode 100644
index 000000000..8eeda4ada
--- /dev/null
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using struct gfxPoint from "gfxPoint.h";
+using nsIntRegion from "nsRegion.h";
+using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
+using mozilla::StereoMode from "ImageTypes.h";
+using mozilla::YUVColorSpace from "ImageTypes.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using gfxImageFormat from "gfxTypes.h";
+using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+union OverlayHandle {
+ int32_t;
+ GonkNativeHandle;
+ null_t;
+};
+
+struct OverlaySource {
+ OverlayHandle handle;
+ IntSize size;
+};
+
+struct SurfaceDescriptorD3D9 {
+ // IDirect3DTexture9*
+ uintptr_t texture;
+};
+
+struct SurfaceDescriptorFileMapping {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+};
+
+struct SurfaceDescriptorDIB {
+ // gfxWindowsSurface*
+ uintptr_t surface;
+};
+
+struct SurfaceDescriptorD3D10 {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+};
+
+struct SurfaceDescriptorDXGIYCbCr {
+ WindowsHandle handleY;
+ WindowsHandle handleCb;
+ WindowsHandle handleCr;
+ IntSize size;
+ IntSize sizeY;
+ IntSize sizeCbCr;
+};
+
+struct SurfaceDescriptorMacIOSurface {
+ uint32_t surfaceId;
+ double scaleFactor;
+ bool isOpaque;
+};
+
+struct SurfaceTextureDescriptor {
+ uintptr_t surfTex;
+ IntSize size;
+};
+
+struct EGLImageDescriptor {
+ uintptr_t image; // `EGLImage` is a `void*`.
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+struct SurfaceDescriptorSharedGLTexture {
+ uint32_t texture;
+ uint32_t target;
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+struct SurfaceDescriptorGPUVideo {
+ uint64_t handle;
+};
+
+struct RGBDescriptor {
+ IntSize size;
+ SurfaceFormat format;
+ bool hasIntermediateBuffer;
+};
+
+struct YCbCrDescriptor {
+ IntSize ySize;
+ IntSize cbCrSize;
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ StereoMode stereoMode;
+ YUVColorSpace yUVColorSpace;
+ bool hasIntermediateBuffer;
+};
+
+union BufferDescriptor {
+ RGBDescriptor;
+ YCbCrDescriptor;
+};
+
+union MemoryOrShmem {
+ uintptr_t;
+ Shmem;
+};
+
+struct SurfaceDescriptorBuffer {
+ BufferDescriptor desc;
+ MemoryOrShmem data;
+};
+
+union SurfaceDescriptor {
+ SurfaceDescriptorBuffer;
+ SurfaceDescriptorD3D9;
+ SurfaceDescriptorDIB;
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorFileMapping;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorX11;
+ SurfaceTextureDescriptor;
+ EGLImageDescriptor;
+ SurfaceDescriptorMacIOSurface;
+ SurfaceDescriptorSharedGLTexture;
+ SurfaceDescriptorGPUVideo;
+ null_t;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl
new file mode 100644
index 000000000..e321b9ea0
--- /dev/null
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using class nsRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
+ * PCompositorBridge protocol which either connects to the compositor thread
+ * in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
+ * GeckoContentController lives (generally the main thread of the main or content process).
+ * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
+ *
+ * PAPZ is always used for ContentProcessController and only used for ChromeProcessController
+ * when there is a gpu process, otherwhise ChromeProcessController is used directly on the
+ * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
+ * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
+ * must be updated to handle it.
+ */
+sync protocol PAPZ
+{
+ manager PCompositorBridge;
+
+parent:
+
+ async __delete__();
+
+child:
+
+ async RequestContentRepaint(FrameMetrics frame);
+
+ async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent);
+
+ async UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent);
+
+ async SetScrollingRootContent(bool aIsRootContent);
+
+ async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
+
+ async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg);
+
+ async NotifyFlushComplete();
+
+ async Destroy();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PAPZCTreeManager.ipdl b/gfx/layers/ipc/PAPZCTreeManager.ipdl
new file mode 100644
index 000000000..21d899f91
--- /dev/null
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "ipc/nsGUIEventIPC.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using LayoutDeviceCoord from "Units.h";
+using LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using ScreenPoint from "Units.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
+
+using nsEventStatus from "mozilla/EventForwards.h";
+using EventMessage from "mozilla/EventForwards.h";
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using class mozilla::WidgetInputEvent from "mozilla/BasicEvents.h";
+using class mozilla::WidgetMouseEventBase from "mozilla/MouseEvents.h";
+using mozilla::WidgetMouseEvent::Reason from "mozilla/MouseEvents.h";
+using class mozilla::WidgetTouchEvent from "mozilla/TouchEvents.h";
+using class mozilla::WidgetWheelEvent from "mozilla/MouseEvents.h";
+using class mozilla::InputData from "InputData.h";
+using class mozilla::MultiTouchInput from "InputData.h";
+using class mozilla::MouseInput from "InputData.h";
+using class mozilla::PanGestureInput from "InputData.h";
+using class mozilla::PinchGestureInput from "InputData.h";
+using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
+using class mozilla::TapGestureInput from "InputData.h";
+using class mozilla::ScrollWheelInput from "InputData.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
+ * lives on the PCompositorBridge protocol which either connects to the compositor
+ * thread in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
+ * lives in the main thread of the main or the content process. APZCTreeManagerParent
+ * and APZCTreeManagerChild implement this protocol.
+ */
+sync protocol PAPZCTreeManager
+{
+manager PCompositorBridge;
+
+parent:
+
+ // These messages correspond to the methods
+ // on the IAPZCTreeManager interface
+
+ async ZoomToRect(ScrollableLayerGuid aGuid, CSSRect aRect, uint32_t Flags);
+
+ async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault);
+
+ async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets);
+
+ async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints aConstraints);
+
+ async CancelAnimation(ScrollableLayerGuid aGuid);
+
+ async AdjustScrollForSurfaceShift(ScreenPoint aShift);
+
+ async SetDPI(float aDpiValue);
+
+ async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues);
+
+ async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics);
+
+ async SetLongTapEnabled(bool aTapGestureEnabled);
+
+ async ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY);
+
+ // The following messages are used to
+ // implement the ReceiveInputEvent methods
+
+ sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ MultiTouchInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveMouseInputEvent(MouseInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ MouseInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceivePanGestureInputEvent(PanGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ PanGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ PinchGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveTapGestureInputEvent(TapGestureInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ TapGestureInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent)
+ returns (nsEventStatus aOutStatus,
+ ScrollWheelInput aOutEvent,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutInputBlockId);
+
+ async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage);
+
+ sync TransformEventRefPoint(LayoutDeviceIntPoint aRefPoint)
+ returns (LayoutDeviceIntPoint aOutRefPoint,
+ ScrollableLayerGuid aOutTargetGuid);
+
+ async __delete__();
+
+child:
+
+ async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+
+ async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid,
+ LayoutDeviceCoord aSpanChange, Modifiers aModifiers);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PCompositable.ipdl b/gfx/layers/ipc/PCompositable.ipdl
new file mode 100644
index 000000000..d7754cd95
--- /dev/null
+++ b/gfx/layers/ipc/PCompositable.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PLayerTransaction;
+include protocol PImageBridge;
+include protocol PCompositorBridge;
+
+namespace mozilla {
+namespace layers {
+
+async protocol PCompositable
+{
+ manager PImageBridge or PLayerTransaction;
+child:
+ async __delete__();
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl
new file mode 100644
index 000000000..03a353506
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include PlatformWidgetTypes;
+include protocol PAPZ;
+include protocol PAPZCTreeManager;
+include protocol PBrowser;
+include protocol PCompositable;
+include protocol PCompositorWidget;
+include protocol PImageContainer;
+include protocol PLayer;
+include protocol PLayerTransaction;
+include protocol PTexture;
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::CSSIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDeviceIntRegion from "Units.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * The PCompositorBridge protocol is used to manage communication between
+ * the main thread and the compositor thread context. It's primary
+ * purpose is to manage the PLayerTransaction sub protocol.
+ */
+sync protocol PCompositorBridge
+{
+ manages PAPZ;
+ manages PAPZCTreeManager;
+ // A Compositor manages a single Layer Manager (PLayerTransaction)
+ manages PLayerTransaction;
+ manages PTexture;
+ manages PCompositorWidget;
+
+child:
+ // The child should invalidate retained layers. This is used for local
+ // compositor device resets, such as in CompositorD3D9, and ensures that
+ // TextureSources are recreated.
+ async InvalidateLayers(uint64_t layersId);
+
+ // The compositor type or device has changed, and a new texture factory
+ // identifier is available. Layers must be invalidated and the new identifier
+ // must be propagated.
+ async CompositorUpdated(uint64_t layersId, TextureFactoryIdentifier newIdentifier);
+
+ // The compositor completed a layers transaction. id is the layers id
+ // of the child layer tree that was composited (or 0 when notifying
+ // the root layer tree).
+ // transactionId is the id of the transaction before this composite, or 0
+ // if there was no transaction since the last composite.
+ async DidComposite(uint64_t id, uint64_t transactionId,
+ TimeStamp compositeStart, TimeStamp compositeEnd);
+
+ // The parent sends the child the requested fill ratio numbers.
+ async Overfill(uint32_t aOverfill);
+
+ /**
+ * Parent informs the child that the graphics objects are ready for
+ * compositing. This usually means that the graphics objects (textures
+ * and the like) are available on the GPU. This is used for chrome UI.
+ * @see RequestNotifyAfterRemotePaint
+ * @see PBrowser
+ */
+ async RemotePaintIsReady();
+
+ /**
+ * Bounce plugin widget configurations over to the main thread for
+ * application on the widgets. Used on Windows and Linux in managing
+ * plugin widgets.
+ */
+ async UpdatePluginConfigurations(LayoutDeviceIntPoint aContentOffset,
+ LayoutDeviceIntRegion aVisibleRegion,
+ PluginWindowData[] aPlugins);
+
+ /**
+ * Captures an image for all visible child plugins of a given widget for use
+ * during scrolling.
+ * @param aParentWidget parent of widgets to be captured
+ */
+ async CaptureAllPlugins(uintptr_t aParentWidget);
+
+ /**
+ * Hides all registered plugin widgets associated with a particular chrome
+ * widget.
+ */
+ async HideAllPlugins(uintptr_t aParentWidget);
+
+ /**
+ * Drop any buffers that might be retained on the child compositor
+ * side.
+ */
+ async ClearCachedResources(uint64_t id);
+
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive);
+
+parent:
+ // Must be called before Initialize().
+ async PCompositorWidget(CompositorWidgetInitData aInitData);
+
+ // When out-of-process, this must be called to finish initialization.
+ sync Initialize(uint64_t rootLayerTreeId);
+ sync Reset(LayersBackend[] aBackendHints) returns (bool aResult, TextureFactoryIdentifier aOutIdentifier);
+
+ // Returns whether this Compositor has APZ enabled or not.
+ sync AsyncPanZoomEnabled(uint64_t layersId) returns (bool aHasAPZ);
+
+ // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true.
+ async PAPZ(uint64_t layersId);
+ async PAPZCTreeManager(uint64_t layersId);
+
+ /**
+ * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins.
+ */
+ async RemotePluginsReady();
+
+ // Confirmation that the child has invalidated all its layers, and will not
+ // request layers against an old compositor.
+ async AcknowledgeCompositorUpdate(uint64_t id);
+
+ // Child sends the parent a request for fill ratio numbers.
+ async RequestOverfill();
+
+ // Child requests frame uniformity measurements
+ sync GetFrameUniformity() returns (FrameUniformityData data);
+
+ // The child is about to be destroyed, so perform any necessary cleanup.
+ sync WillClose();
+
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume();
+
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync NotifyChildCreated(uint64_t id);
+
+ async AdoptChild(uint64_t id);
+
+ // Same as NotifyChildCreated, but used when child processes need to
+ // reassociate layers. This must be synchronous to ensure that the
+ // association happens before PLayerTransactions are sent over the
+ // cross-process bridge.
+ sync NotifyChildRecreated(uint64_t id);
+
+ // Make a snapshot of the content that would have been drawn to our
+ // render target at the time this message is received. If the size
+ // or format of |inSnapshot| doesn't match our render target,
+ // results are undefined.
+ //
+ // NB: this message will result in animations, transforms, effects,
+ // and so forth being interpolated. That's what we want to happen.
+ sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect);
+
+ // Make sure any pending composites are started immediately and
+ // block until they are completed.
+ sync FlushRendering();
+
+ // Force an additional frame presentation to be executed. This is used to
+ // work around a windows presentation bug (See Bug 1232042)
+ async ForcePresent();
+
+ sync StartFrameTimeRecording(int32_t bufferSize)
+ returns (uint32_t startIndex);
+
+ sync StopFrameTimeRecording(uint32_t startIndex)
+ returns (float[] intervals);
+
+ // layersBackendHints is an ordered list of preffered backends where
+ // layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE
+ // that hint is ignored.
+ sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id)
+ returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success);
+
+ // Notify the compositor that a region of the screen has been invalidated.
+ async NotifyRegionInvalidated(nsIntRegion region);
+
+ /**
+ * The child (content/chrome thread) requests that the parent inform it when
+ * the graphics objects are ready to display.
+ * @see PBrowser
+ * @see RemotePaintIsReady
+ */
+ async RequestNotifyAfterRemotePaint();
+
+ // The child clears the 'approximately visible' regions associated with the
+ // provided layers ID and pres shell ID (i.e., the regions for all view IDs
+ // associated with those IDs).
+ async ClearApproximatelyVisibleRegions(uint64_t layersId, uint32_t presShellId);
+
+ // The child sends a region containing rects associated with the provided
+ // scrollable layer GUID that the child considers 'approximately visible'.
+ // We visualize this information in the APZ minimap.
+ async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region);
+
+ /**
+ * Sent when the child has finished CaptureAllPlugins.
+ */
+ async AllPluginsCaptured();
+
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
+
+ sync SyncWithCompositor();
+
+child:
+ // Send back Compositor Frame Metrics from APZCs so tiled layers can
+ // update progressively.
+ async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint64_t aLayersId, uint32_t aAPZCId);
+ async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl
new file mode 100644
index 000000000..3bed222d0
--- /dev/null
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PCompositable;
+include protocol PImageContainer;
+include protocol PLayer;
+include protocol PTexture;
+include ProtocolTypes;
+include protocol PMediaSystemResourceManager;
+
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+using PlatformThreadId from "base/platform_thread.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PImageBridge protocol is used to allow isolated threads or processes to push
+ * frames directly to the compositor thread/process without relying on the main thread
+ * which might be too busy dealing with content script.
+ */
+sync protocol PImageBridge
+{
+ manages PCompositable;
+ manages PTexture;
+ manages PMediaSystemResourceManager;
+ manages PImageContainer;
+
+child:
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async DidComposite(ImageCompositeNotification[] aNotifications);
+
+parent:
+ async ImageBridgeThreadId(PlatformThreadId aTreahdId);
+
+ sync Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId)
+ returns (EditReply[] reply);
+
+ async UpdateNoSwap(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
+
+ // First step of the destruction sequence. This puts ImageBridge
+ // in a state in which it can't send asynchronous messages
+ // so as to not race with the channel getting closed.
+ // In the child side, the Closing the channel does not happen right after WillClose,
+ // it is scheduled in the ImageBridgeChild's message queue in order to ensure
+ // that all of the messages from the parent side have been received and processed
+ // before sending closing the channel.
+ sync WillClose();
+
+ sync PCompositable(TextureInfo aInfo,
+ nullable PImageContainer aImageContainer) returns (uint64_t id);
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial);
+ async PMediaSystemResourceManager();
+ async PImageContainer();
+
+};
+
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/PImageContainer.ipdl b/gfx/layers/ipc/PImageContainer.ipdl
new file mode 100644
index 000000000..7ae567a88
--- /dev/null
+++ b/gfx/layers/ipc/PImageContainer.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PImageBridge;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PImageContainer represents an ImageContainer.
+ */
+
+async protocol PImageContainer {
+ manager PImageBridge;
+parent:
+ /**
+ * The child effectively owns the parent. When the child should be
+ * destroyed, it sends an AsyncDelete to the parent but does not die
+ * because we could still have messages in flight from the compositor
+ * mentioning the child. The parent handles AsyncDelete by destroying
+ * itself and sending __delete__ to the child to clean it up.
+ */
+ async AsyncDelete();
+child:
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PLayer.ipdl b/gfx/layers/ipc/PLayer.ipdl
new file mode 100644
index 000000000..4a46b56d6
--- /dev/null
+++ b/gfx/layers/ipc/PLayer.ipdl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PLayerTransaction;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PLayer represents a layer shared across thread contexts.
+ */
+
+async protocol PLayer {
+ manager PLayerTransaction;
+
+ /**
+ * OWNERSHIP MODEL
+ *
+ * Roughly speaking, the child side "actually owns" a Layer. This
+ * is because the parent side is the "shadow"; when the child
+ * releases a Layer, the parent's shadow is no longer meaningful.
+ *
+ * To implement this model, the concrete PLayerParent keeps a
+ * strong ref to its Layer, so the Layer's lifetime is bound to
+ * the PLayerParent's. Then, when the Layer's refcount hits 0 on
+ * the child side, we send __delete__() from the child to parent.
+ * The parent then releases its Layer, which results in the Layer
+ * being deleted "soon" (usually immediately).
+ */
+parent:
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl
new file mode 100644
index 000000000..d669b1d65
--- /dev/null
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PCompositable;
+include protocol PCompositorBridge;
+include protocol PLayer;
+include protocol PRenderFrame;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
+using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+
+/**
+ * The layers protocol is spoken between thread contexts that manage
+ * layer (sub)trees. The protocol comprises atomically publishing
+ * layer subtrees to a "shadow" thread context (which grafts the
+ * subtree into its own tree), and atomically updating a published
+ * subtree. ("Atomic" in this sense is wrt painting.)
+ */
+
+namespace mozilla {
+namespace layers {
+
+union MaybeTransform {
+ Matrix4x4;
+ void_t;
+};
+
+sync protocol PLayerTransaction {
+ manager PCompositorBridge;
+ manages PLayer;
+ manages PCompositable;
+
+parent:
+ async PLayer();
+ async PCompositable(TextureInfo aTextureInfo);
+
+ // The isFirstPaint flag can be used to indicate that this is the first update
+ // for a particular document.
+ sync Update(Edit[] cset, OpDestroy[] toDestroy,
+ uint64_t fwdTransactionId,
+ uint64_t id, TargetConfig targetConfig,
+ PluginWindowData[] plugins, bool isFirstPaint,
+ bool scheduleComposite, uint32_t paintSequenceNumber,
+ bool isRepeatTransaction, TimeStamp transactionStart,
+ int32_t paintSyncId)
+ returns (EditReply[] reply);
+
+ async PaintTime(uint64_t id, TimeDuration paintTime);
+
+ // We don't need to send a sync transaction if
+ // no transaction operate require a swap.
+ async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy,
+ uint64_t fwdTransactionId,
+ uint64_t id, TargetConfig targetConfig,
+ PluginWindowData[] plugins, bool isFirstPaint,
+ bool scheduleComposite, uint32_t paintSequenceNumber,
+ bool isRepeatTransaction, TimeStamp transactionStart,
+ int32_t paintSyncId);
+
+ async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
+
+ // Testing APIs
+
+ // Enter test mode, set the sample time to sampleTime, and resample
+ // animations. sampleTime must not be null.
+ sync SetTestSampleTime(TimeStamp sampleTime);
+ // Leave test mode and resume normal compositing
+ sync LeaveTestMode();
+
+ // Returns the value of the opacity applied to the layer by animation.
+ // |hasAnimationOpacity| is true if the layer has an opacity value
+ // specified by animation. If it's false, |opacity| value is indefinite.
+ sync GetAnimationOpacity(PLayer layer) returns (float opacity,
+ bool hasAnimationOpacity);
+
+ // Returns the value of the transform applied to the layer by animation after
+ // factoring out translation components introduced to account for the offset
+ // of the corresponding frame and transform origin and after converting to CSS
+ // pixels. If the layer is not transformed by animation, the return value will
+ // be void_t.
+ sync GetAnimationTransform(PLayer layer) returns (MaybeTransform transform);
+
+ // The next time the layer tree is composited, add this async scroll offset in
+ // CSS pixels for the given ViewID.
+ // Useful for testing rendering of async scrolling.
+ sync SetAsyncScrollOffset(ViewID id, float x, float y);
+
+ // The next time the layer tree is composited, include this async zoom in
+ // for the given ViewID.
+ // Useful for testing rendering of async zooming.
+ sync SetAsyncZoom(ViewID id, float zoom);
+
+ // Flush any pending APZ repaints to the main thread.
+ async FlushApzRepaints();
+
+ // Drop any front buffers that might be retained on the compositor
+ // side.
+ async ClearCachedResources();
+
+ // Schedule a composite if one isn't already scheduled.
+ async ForceComposite();
+
+ // Get a copy of the compositor-side APZ test data instance for this
+ // layers id.
+ sync GetAPZTestData() returns (APZTestData data);
+
+ // Query a named property from the last frame
+ sync RequestProperty(nsString property) returns (float value);
+
+ // Tell the compositor to notify APZ that a layer has been confirmed for an
+ // input event.
+ async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+
+ async Shutdown();
+child:
+ async __delete__();
+};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl
new file mode 100644
index 000000000..bccff8627
--- /dev/null
+++ b/gfx/layers/ipc/PTexture.ipdl
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PLayerTransaction;
+include protocol PCompositorBridge;
+include protocol PImageBridge;
+include protocol PVRManager;
+include protocol PVideoBridge;
+include "mozilla/GfxMessageUtils.h";
+
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PTexture is the IPDL glue between a TextureClient and a TextureHost.
+ */
+sync protocol PTexture {
+ manager PImageBridge or PCompositorBridge or PVRManager or PVideoBridge;
+
+child:
+ async __delete__();
+
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+
+ /**
+ * Synchronously tell the compositor side to remove the texture.
+ */
+ sync DestroySync();
+
+ async RecycleTexture(TextureFlags aTextureFlags);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PVideoBridge.ipdl b/gfx/layers/ipc/PVideoBridge.ipdl
new file mode 100644
index 000000000..3fca7ac75
--- /dev/null
+++ b/gfx/layers/ipc/PVideoBridge.ipdl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PVideoBridge protocol is used to share textures from the video decoders
+ * to the compositor.
+ */
+sync protocol PVideoBridge
+{
+ manages PTexture;
+
+parent:
+ async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend,
+ TextureFlags aTextureFlags, uint64_t aSerial);
+};
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp
new file mode 100644
index 000000000..54a08eed3
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/RemoteContentController.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "MainThreadUtils.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/Unused.h"
+#include "Units.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+RemoteContentController::RemoteContentController()
+ : mCompositorThread(MessageLoop::current())
+ , mCanSend(true)
+{
+}
+
+RemoteContentController::~RemoteContentController()
+{
+}
+
+void
+RemoteContentController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendRequestContentRepaint(aFrameMetrics);
+ }
+}
+
+void
+RemoteContentController::HandleTapOnMainThread(TapType aTapType,
+ LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+}
+
+void
+RemoteContentController::HandleTap(TapType aTapType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ MOZ_ASSERT(MessageLoop::current() == mCompositorThread);
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (NS_IsMainThread()) {
+ HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ } else {
+ // We don't want to get the TabParent or call TabParent::SendHandleTap() from a non-main thread (this might happen
+ // on Android, where this is called from the Java UI thread)
+ NS_DispatchToMainThread(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, ScrollableLayerGuid, uint64_t>
+ (this, &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, aModifiers, aGuid, aInputBlockId));
+ }
+}
+
+void
+RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers)
+{
+ APZThreadUtils::AssertOnControllerThread();
+
+ // For now we only ever want to handle this NotifyPinchGesture message in
+ // the parent process, even if the APZ is sending it to a content process.
+
+ // If we're in the GPU process, try to find a handle to the parent process
+ // and send it there.
+ if (XRE_IsGPUProcess()) {
+ MOZ_ASSERT(MessageLoop::current() == mCompositorThread);
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
+ return;
+ }
+ }
+
+ // If we're in the parent process, handle it directly. We don't have a handle
+ // to the widget though, so we fish out the ChromeProcessController and
+ // delegate to that instead.
+ if (XRE_IsParentProcess()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId);
+ if (rootController) {
+ rootController->NotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
+ }
+ }
+}
+
+void
+RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs);
+#else
+ (MessageLoop::current() ? MessageLoop::current() : mCompositorThread)->
+ PostDelayedTask(Move(aTask), aDelayMs);
+#endif
+}
+
+bool
+RemoteContentController::IsRepaintThread()
+{
+ return MessageLoop::current() == mCompositorThread;
+}
+
+void
+RemoteContentController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask)
+{
+ mCompositorThread->PostTask(Move(aTask));
+}
+
+void
+RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid,
+ APZStateChange,
+ int>(this,
+ &RemoteContentController::NotifyAPZStateChange,
+ aGuid, aChange, aArg));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg);
+ }
+}
+
+void
+RemoteContentController::UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<float,
+ float, bool>(this,
+ &RemoteContentController::UpdateOverscrollVelocity,
+ aX, aY, aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendUpdateOverscrollVelocity(aX, aY, aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<float,
+ float, bool>(this,
+ &RemoteContentController::UpdateOverscrollOffset,
+ aX, aY, aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendUpdateOverscrollOffset(aX, aY, aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::SetScrollingRootContent(bool aIsRootContent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ mCompositorThread->PostTask(NewRunnableMethod<bool>(this,
+ &RemoteContentController::SetScrollingRootContent,
+ aIsRootContent));
+ return;
+ }
+ if (mCanSend) {
+ Unused << SendSetScrollingRootContent(aIsRootContent);
+ }
+}
+
+void
+RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent)
+{
+ if (MessageLoop::current() != mCompositorThread) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID,
+ nsString>(this,
+ &RemoteContentController::NotifyMozMouseScrollEvent,
+ aScrollId, aEvent));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void
+RemoteContentController::NotifyFlushComplete()
+{
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendNotifyFlushComplete();
+ }
+}
+
+void
+RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // This controller could possibly be kept alive longer after this
+ // by a RefPtr, but it is no longer valid to send messages.
+ mCanSend = false;
+}
+
+void
+RemoteContentController::Destroy()
+{
+ if (mCanSend) {
+ mCanSend = false;
+ Unused << SendDestroy();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h
new file mode 100644
index 000000000..d015ada4f
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_RemoteContentController_h
+#define mozilla_layers_RemoteContentController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/PAPZParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+}
+
+namespace layers {
+
+/**
+ * RemoteContentController implements PAPZChild and is used to access a
+ * GeckoContentController that lives in a different process.
+ *
+ * RemoteContentController lives on the compositor thread. All methods can
+ * be called off the compositor thread and will get dispatched to the right
+ * thread, with the exception of RequestContentRepaint and NotifyFlushComplete,
+ * which must be called on the repaint thread, which in this case is the compositor
+ * thread.
+ */
+class RemoteContentController : public GeckoContentController
+ , public PAPZParent
+{
+ using GeckoContentController::TapType;
+ using GeckoContentController::APZStateChange;
+
+public:
+ RemoteContentController();
+
+ virtual ~RemoteContentController();
+
+ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
+
+ virtual void HandleTap(TapType aTapType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+
+ virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
+
+ virtual bool IsRepaintThread() override;
+
+ virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+ virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange,
+ int aArg) override;
+
+ virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override;
+
+ virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override;
+
+ virtual void SetScrollingRootContent(bool aIsRootContent) override;
+
+ virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ virtual void NotifyFlushComplete() override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual void Destroy() override;
+
+private:
+ MessageLoop* mCompositorThread;
+ bool mCanSend;
+
+ void HandleTapOnMainThread(TapType aType,
+ LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_RemoteContentController_h
diff --git a/gfx/layers/ipc/ShadowLayerChild.cpp b/gfx/layers/ipc/ShadowLayerChild.cpp
new file mode 100644
index 000000000..62d260838
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerChild.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerChild.h"
+#include "Layers.h" // for Layer
+#include "ShadowLayers.h" // for ShadowableLayer
+
+namespace mozilla {
+namespace layers {
+
+ShadowLayerChild::ShadowLayerChild()
+ : mLayer(nullptr)
+{ }
+
+ShadowLayerChild::~ShadowLayerChild()
+{ }
+
+void
+ShadowLayerChild::SetShadowableLayer(ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(!mLayer);
+ mLayer = aLayer;
+}
+
+void
+ShadowLayerChild::ActorDestroy(ActorDestroyReason why)
+{
+ MOZ_ASSERT(AncestorDeletion != why,
+ "shadowable layer should have been cleaned up by now");
+
+ if (AbnormalShutdown == why && mLayer) {
+ // This is last-ditch emergency shutdown. Just have the layer
+ // forget its IPDL resources; IPDL-generated code will clean up
+ // automatically in this case.
+ mLayer->AsLayer()->Disconnect();
+ mLayer = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayerChild.h b/gfx/layers/ipc/ShadowLayerChild.h
new file mode 100644
index 000000000..38a7f9500
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerChild.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerChild_h
+#define mozilla_layers_ShadowLayerChild_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerChild.h" // for PLayerChild
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+
+class ShadowLayerChild : public PLayerChild
+{
+public:
+ ShadowLayerChild();
+ virtual ~ShadowLayerChild();
+
+ void SetShadowableLayer(ShadowableLayer* aLayer);
+ ShadowableLayer* layer() const { return mLayer; }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ ShadowableLayer* mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayerChild_h
diff --git a/gfx/layers/ipc/ShadowLayerParent.cpp b/gfx/layers/ipc/ShadowLayerParent.cpp
new file mode 100644
index 000000000..e0898715d
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerParent.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerParent.h"
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/layers/CanvasLayerComposite.h"
+#include "mozilla/layers/ColorLayerComposite.h"
+#include "mozilla/layers/ImageLayerComposite.h"
+#include "mozilla/layers/ContainerLayerComposite.h"
+
+namespace mozilla {
+namespace layers {
+
+ShadowLayerParent::ShadowLayerParent() : mLayer(nullptr)
+{
+}
+
+ShadowLayerParent::~ShadowLayerParent()
+{
+ Disconnect();
+}
+
+void
+ShadowLayerParent::Disconnect()
+{
+ if (mLayer) {
+ mLayer->Disconnect();
+ mLayer = nullptr;
+ }
+}
+
+void
+ShadowLayerParent::Bind(Layer* layer)
+{
+ if (mLayer != layer) {
+ Disconnect();
+ mLayer = layer;
+ }
+}
+
+void
+ShadowLayerParent::Destroy()
+{
+ // It's possible for Destroy() to come in just after this has been
+ // created, but just before the transaction in which Bind() would
+ // have been called. In that case, we'll ignore shadow-layers
+ // transactions from there on and never get a layer here.
+ Disconnect();
+}
+
+ContainerLayerComposite*
+ShadowLayerParent::AsContainerLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_CONTAINER
+ ? static_cast<ContainerLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+CanvasLayerComposite*
+ShadowLayerParent::AsCanvasLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_CANVAS
+ ? static_cast<CanvasLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+ColorLayerComposite*
+ShadowLayerParent::AsColorLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_COLOR
+ ? static_cast<ColorLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+ImageLayerComposite*
+ShadowLayerParent::AsImageLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE
+ ? static_cast<ImageLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+RefLayerComposite*
+ShadowLayerParent::AsRefLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_REF
+ ? static_cast<RefLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+PaintedLayerComposite*
+ShadowLayerParent::AsPaintedLayerComposite() const
+{
+ return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED
+ ? static_cast<PaintedLayerComposite*>(mLayer.get())
+ : nullptr;
+}
+
+void
+ShadowLayerParent::ActorDestroy(ActorDestroyReason why)
+{
+ switch (why) {
+ case AncestorDeletion:
+ NS_RUNTIMEABORT("shadow layer deleted out of order!");
+ return; // unreached
+
+ case Deletion:
+ // See comment near Destroy() above.
+ Disconnect();
+ break;
+
+ case AbnormalShutdown:
+ Disconnect();
+ break;
+
+ case NormalShutdown:
+ // let IPDL-generated code automatically clean up Shmems and so
+ // forth; our channel is disconnected anyway
+ break;
+
+ case FailedConstructor:
+ NS_RUNTIMEABORT("FailedConstructor isn't possible in PLayerTransaction");
+ return; // unreached
+ }
+
+ mLayer = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayerParent.h b/gfx/layers/ipc/ShadowLayerParent.h
new file mode 100644
index 000000000..d40cf34ff
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerParent.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerParent_h
+#define mozilla_layers_ShadowLayerParent_h
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerParent.h" // for PLayerParent
+
+namespace mozilla {
+namespace layers {
+
+class ContainerLayer;
+class Layer;
+
+class CanvasLayerComposite;
+class ColorLayerComposite;
+class ContainerLayerComposite;
+class ImageLayerComposite;
+class RefLayerComposite;
+class PaintedLayerComposite;
+
+class ShadowLayerParent : public PLayerParent
+{
+public:
+ ShadowLayerParent();
+
+ virtual ~ShadowLayerParent();
+
+ void Bind(Layer* layer);
+ void Destroy();
+
+ Layer* AsLayer() const { return mLayer; }
+
+ ContainerLayerComposite* AsContainerLayerComposite() const;
+ CanvasLayerComposite* AsCanvasLayerComposite() const;
+ ColorLayerComposite* AsColorLayerComposite() const;
+ ImageLayerComposite* AsImageLayerComposite() const;
+ RefLayerComposite* AsRefLayerComposite() const;
+ PaintedLayerComposite* AsPaintedLayerComposite() const;
+
+private:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ void Disconnect();
+
+ RefPtr<Layer> mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayerParent_h
diff --git a/gfx/layers/ipc/ShadowLayerUtils.h b/gfx/layers/ipc/ShadowLayerUtils.h
new file mode 100644
index 000000000..257cee81d
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_ShadowLayerUtils_h
+#define IPC_ShadowLayerUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "GLContextTypes.h"
+#include "SurfaceTypes.h"
+#include "mozilla/WidgetUtils.h"
+
+#if defined(XP_MACOSX)
+#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+#endif
+
+#if defined(MOZ_X11)
+# include "mozilla/layers/ShadowLayerUtilsX11.h"
+#else
+namespace mozilla { namespace layers {
+struct SurfaceDescriptorX11 {
+ bool operator==(const SurfaceDescriptorX11&) const { return false; }
+};
+} // namespace layers
+} // namespace mozilla
+#endif
+
+namespace IPC {
+
+#if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11)
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+ static void Write(Message*, const paramType&) {}
+ static bool Read(const Message*, PickleIterator*, paramType*) { return false; }
+};
+#endif // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11)
+
+template <>
+struct ParamTraits<mozilla::ScreenRotation>
+ : public ContiguousEnumSerializer<
+ mozilla::ScreenRotation,
+ mozilla::ROTATION_0,
+ mozilla::ROTATION_COUNT>
+{};
+
+} // namespace IPC
+
+#endif // IPC_ShadowLayerUtils_h
diff --git a/gfx/layers/ipc/ShadowLayerUtilsMac.cpp b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
new file mode 100644
index 000000000..cc5199ea8
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/PLayerTransaction.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/CompositorTypes.h"
+
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+}
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
new file mode 100644
index 000000000..6b9660054
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerUtilsX11.h"
+#include <X11/X.h> // for Drawable, XID
+#include <X11/Xlib.h> // for Display, Visual, etc
+#include <X11/extensions/Xrender.h> // for XRenderPictFormat, etc
+#include <X11/extensions/render.h> // for PictFormat
+#include "cairo-xlib.h"
+#include "X11UndefineNone.h"
+#include <stdint.h> // for uint32_t
+#include "GLDefs.h" // for GLenum
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxXlibSurface.h" // for gfxXlibSurface
+#include "gfx2DGlue.h" // for Moz2D transistion helpers
+#include "mozilla/X11Util.h" // for DefaultXDisplay, FinishX, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "gfxEnv.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class TextureImage;
+}
+
+namespace layers {
+
+// Return true if we're likely compositing using X and so should use
+// Xlib surfaces in shadow layers.
+static bool
+UsingXCompositing()
+{
+ if (!gfxEnv::LayersEnableXlibSurfaces()) {
+ return false;
+ }
+ return (gfxSurfaceType::Xlib ==
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType());
+}
+
+// LookReturn a pointer to |aFormat| that lives in the Xrender library.
+// All code using render formats assumes it doesn't need to copy.
+static XRenderPictFormat*
+GetXRenderPictFormatFromId(Display* aDisplay, PictFormat aFormatId)
+{
+ XRenderPictFormat tmplate;
+ tmplate.id = aFormatId;
+ return XRenderFindFormat(aDisplay, PictFormatID, &tmplate, 0);
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf,
+ bool aForwardGLX)
+ : mId(aSurf->XDrawable())
+ , mSize(aSurf->GetSize())
+ , mGLXPixmap(X11None)
+{
+ const XRenderPictFormat *pictFormat = aSurf->XRenderFormat();
+ if (pictFormat) {
+ mFormat = pictFormat->id;
+ } else {
+ mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid;
+ }
+
+#ifdef GL_PROVIDER_GLX
+ if (aForwardGLX) {
+ mGLXPixmap = aSurf->GetGLXPixmap();
+ }
+#endif
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize)
+ : mId(aDrawable)
+ , mFormat(aFormatID)
+ , mSize(aSize)
+ , mGLXPixmap(X11None)
+{ }
+
+already_AddRefed<gfxXlibSurface>
+SurfaceDescriptorX11::OpenForeign() const
+{
+ Display* display = DefaultXDisplay();
+ Screen* screen = DefaultScreenOfDisplay(display);
+
+ RefPtr<gfxXlibSurface> surf;
+ XRenderPictFormat* pictFormat = GetXRenderPictFormatFromId(display, mFormat);
+ if (pictFormat) {
+ surf = new gfxXlibSurface(screen, mId, pictFormat, mSize);
+ } else {
+ Visual* visual;
+ int depth;
+ FindVisualAndDepth(display, mFormat, &visual, &depth);
+ if (!visual)
+ return nullptr;
+
+ surf = new gfxXlibSurface(display, mId, visual, mSize);
+ }
+
+#ifdef GL_PROVIDER_GLX
+ if (mGLXPixmap)
+ surf->BindGLXPixmap(mGLXPixmap);
+#endif
+
+ return surf->CairoStatus() ? nullptr : surf.forget();
+}
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, then we need to finish all pending
+ // operations on the back buffers before handing them to the
+ // parent, otherwise the surface might be used by the parent's
+ // Display in between two operations queued by our Display.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, we need to finish all pending
+ // operations on the *front buffers* before handing them back to
+ // the child, even though they will be read operations.
+ // Otherwise, the child might start scribbling on new back buffers
+ // that are still participating in requests as old front buffers.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.h b/gfx/layers/ipc/ShadowLayerUtilsX11.h
new file mode 100644
index 000000000..bf64b09ff
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerUtilsX11_h
+#define mozilla_layers_ShadowLayerUtilsX11_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+
+#define MOZ_HAVE_SURFACEDESCRIPTORX11
+#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+typedef unsigned long XID;
+typedef XID Drawable;
+
+class gfxXlibSurface;
+
+namespace IPC {
+class Message;
+}
+
+namespace mozilla {
+namespace layers {
+
+struct SurfaceDescriptorX11 {
+ SurfaceDescriptorX11()
+ { }
+
+ explicit SurfaceDescriptorX11(gfxXlibSurface* aSurf, bool aForwardGLX = false);
+
+ SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize);
+
+ // Default copy ctor and operator= are OK
+
+ bool operator==(const SurfaceDescriptorX11& aOther) const {
+ // Define == as two descriptors having the same XID for now,
+ // ignoring size and render format. If the two indeed refer to
+ // the same valid XID, then size/format are "actually" the same
+ // anyway, regardless of the values of the fields in
+ // SurfaceDescriptorX11.
+ return mId == aOther.mId;
+ }
+
+ already_AddRefed<gfxXlibSurface> OpenForeign() const;
+
+ MOZ_INIT_OUTSIDE_CTOR Drawable mId;
+ MOZ_INIT_OUTSIDE_CTOR XID mFormat; // either a PictFormat or VisualID
+ MOZ_INIT_OUTSIDE_CTOR gfx::IntSize mSize;
+ MOZ_INIT_OUTSIDE_CTOR Drawable mGLXPixmap; // used to prevent multiple bindings to the same GLXPixmap in-process
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mId);
+ WriteParam(aMsg, aParam.mSize);
+ WriteParam(aMsg, aParam.mFormat);
+ WriteParam(aMsg, aParam.mGLXPixmap);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mId) &&
+ ReadParam(aMsg, aIter, &aResult->mSize) &&
+ ReadParam(aMsg, aIter, &aResult->mFormat) &&
+ ReadParam(aMsg, aIter, &aResult->mGLXPixmap)
+ );
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_ShadowLayerUtilsX11_h
diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp
new file mode 100644
index 000000000..88b88bde0
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -0,0 +1,1044 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "ShadowLayers.h"
+#include <set> // for _Rb_tree_const_iterator, etc
+#include <vector> // for vector
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceScope
+#include "ShadowLayerChild.h" // for ShadowLayerChild
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "gfxPlatform.h" // for gfxImageFormat, gfxPlatform
+//#include "gfxSharedImageSurface.h" // for gfxSharedImageSurface
+#include "ipc/IPCMessageUtils.h" // for gfxContentType, null_t
+#include "IPDLActor.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/LayersMessages.h" // for Edit, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/PCompositableChild.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "ShadowLayerUtils.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "mozilla/ReentrantMonitor.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+using namespace mozilla::ipc;
+
+class ClientTiledLayerBuffer;
+
+typedef nsTArray<SurfaceDescriptor> BufferArray;
+typedef std::vector<Edit> EditVector;
+typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+class Transaction
+{
+public:
+ Transaction()
+ : mTargetRotation(ROTATION_0)
+ , mSwapRequired(false)
+ , mOpen(false)
+ , mRotationChanged(false)
+ {}
+
+ void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation,
+ dom::ScreenOrientationInternal aOrientation)
+ {
+ mOpen = true;
+ mTargetBounds = aTargetBounds;
+ if (aRotation != mTargetRotation) {
+ // the first time this is called, mRotationChanged will be false if
+ // aRotation is 0, but we should be OK because for the first transaction
+ // we should only compose if it is non-empty. See the caller(s) of
+ // RotationChanged.
+ mRotationChanged = true;
+ }
+ mTargetRotation = aRotation;
+ mTargetOrientation = aOrientation;
+ }
+ void MarkSyncTransaction()
+ {
+ mSwapRequired = true;
+ }
+ void AddEdit(const Edit& aEdit)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mCset.push_back(aEdit);
+ }
+ void AddEdit(const CompositableOperation& aEdit)
+ {
+ AddEdit(Edit(aEdit));
+ }
+ void AddPaint(const Edit& aPaint)
+ {
+ AddNoSwapPaint(aPaint);
+ mSwapRequired = true;
+ }
+ void AddPaint(const CompositableOperation& aPaint)
+ {
+ AddNoSwapPaint(Edit(aPaint));
+ mSwapRequired = true;
+ }
+
+ void AddNoSwapPaint(const Edit& aPaint)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mPaints.push_back(aPaint);
+ }
+ void AddNoSwapPaint(const CompositableOperation& aPaint)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mPaints.push_back(Edit(aPaint));
+ }
+ void AddMutant(ShadowableLayer* aLayer)
+ {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mMutants.PutEntry(aLayer);
+ }
+ void End()
+ {
+ mCset.clear();
+ mPaints.clear();
+ mMutants.Clear();
+ mDestroyedActors.Clear();
+ mOpen = false;
+ mSwapRequired = false;
+ mRotationChanged = false;
+ }
+
+ bool Empty() const {
+ return mCset.empty() && mPaints.empty() && mMutants.IsEmpty()
+ && mDestroyedActors.IsEmpty();
+ }
+ bool RotationChanged() const {
+ return mRotationChanged;
+ }
+ bool Finished() const { return !mOpen && Empty(); }
+
+ bool Opened() const { return mOpen; }
+
+ EditVector mCset;
+ EditVector mPaints;
+ OpDestroyVector mDestroyedActors;
+ ShadowableLayerSet mMutants;
+ gfx::IntRect mTargetBounds;
+ ScreenRotation mTargetRotation;
+ dom::ScreenOrientationInternal mTargetOrientation;
+ bool mSwapRequired;
+
+private:
+ bool mOpen;
+ bool mRotationChanged;
+
+ // disabled
+ Transaction(const Transaction&);
+ Transaction& operator=(const Transaction&);
+};
+struct AutoTxnEnd {
+ explicit AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {}
+ ~AutoTxnEnd() { mTxn->End(); }
+ Transaction* mTxn;
+};
+
+void
+KnowsCompositor::IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier)
+{
+ mTextureFactoryIdentifier = aIdentifier;
+
+ mSyncObject = SyncObject::CreateSyncObject(aIdentifier.mSyncHandle);
+}
+
+KnowsCompositor::KnowsCompositor()
+ : mSerial(++sSerialCounter)
+{}
+
+KnowsCompositor::~KnowsCompositor()
+{}
+
+ShadowLayerForwarder::ShadowLayerForwarder(ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager)
+ , mMessageLoop(MessageLoop::current())
+ , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
+ , mIsFirstPaint(false)
+ , mWindowOverlayChanged(false)
+ , mPaintSyncId(0)
+{
+ mTxn = new Transaction();
+ mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(1000, "CompositableForwarder");
+}
+
+template<typename T>
+struct ReleaseOnMainThreadTask : public Runnable
+{
+ UniquePtr<T> mObj;
+
+ explicit ReleaseOnMainThreadTask(UniquePtr<T>& aObj)
+ : mObj(Move(aObj))
+ {}
+
+ NS_IMETHOD Run() override {
+ mObj = nullptr;
+ return NS_OK;
+ }
+};
+
+ShadowLayerForwarder::~ShadowLayerForwarder()
+{
+ MOZ_ASSERT(mTxn->Finished(), "unfinished transaction?");
+ delete mTxn;
+ if (mShadowManager) {
+ mShadowManager->SetForwarder(nullptr);
+ mShadowManager->Destroy();
+ }
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(
+ new ReleaseOnMainThreadTask<ActiveResourceTracker>(mActiveResourceTracker));
+ }
+}
+
+void
+ShadowLayerForwarder::BeginTransaction(const gfx::IntRect& aTargetBounds,
+ ScreenRotation aRotation,
+ dom::ScreenOrientationInternal aOrientation)
+{
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin(aTargetBounds, aRotation, aOrientation);
+}
+
+static PLayerChild*
+Shadow(ShadowableLayer* aLayer)
+{
+ return aLayer->GetShadow();
+}
+
+template<typename OpCreateT>
+static void
+CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer)
+{
+ aTxn->AddEdit(OpCreateT(nullptr, Shadow(aLayer)));
+}
+
+void
+ShadowLayerForwarder::CreatedPaintedLayer(ShadowableLayer* aThebes)
+{
+ CreatedLayer<OpCreatePaintedLayer>(mTxn, aThebes);
+}
+void
+ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer)
+{
+ CreatedLayer<OpCreateContainerLayer>(mTxn, aContainer);
+}
+void
+ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage)
+{
+ CreatedLayer<OpCreateImageLayer>(mTxn, aImage);
+}
+void
+ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor)
+{
+ CreatedLayer<OpCreateColorLayer>(mTxn, aColor);
+}
+void
+ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
+{
+ CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
+}
+void
+ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef)
+{
+ CreatedLayer<OpCreateRefLayer>(mTxn, aRef);
+}
+
+void
+ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant)
+{
+mTxn->AddMutant(aMutant);
+}
+
+void
+ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot)
+{
+ mTxn->AddEdit(OpSetRoot(nullptr, Shadow(aRoot)));
+}
+void
+ShadowLayerForwarder::InsertAfter(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter)
+{
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling() ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() : nullptr;
+ }
+
+ if (aAfter) {
+ mTxn->AddEdit(OpInsertAfter(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild),
+ nullptr, Shadow(aAfter)));
+ } else {
+ mTxn->AddEdit(OpPrependChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+ }
+}
+void
+ShadowLayerForwarder::RemoveChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild)
+{
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRemoveChild container=%p child=%p\n",
+ aContainer->AsLayer(), aChild->AsLayer()));
+
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ mTxn->AddEdit(OpRemoveChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+}
+void
+ShadowLayerForwarder::RepositionChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter)
+{
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling() ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() : nullptr;
+ }
+
+ if (aAfter) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRepositionChild container=%p child=%p after=%p",
+ aContainer->AsLayer(), aChild->AsLayer(), aAfter->AsLayer()));
+ mTxn->AddEdit(OpRepositionChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild),
+ nullptr, Shadow(aAfter)));
+ } else {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRaiseToTopChild container=%p child=%p",
+ aContainer->AsLayer(), aChild->AsLayer()));
+ mTxn->AddEdit(OpRaiseToTopChild(nullptr, Shadow(aContainer),
+ nullptr, Shadow(aChild)));
+ }
+}
+
+
+#ifdef DEBUG
+void
+ShadowLayerForwarder::CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const
+{
+ if (!aDescriptor) {
+ return;
+ }
+
+ if (aDescriptor->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aDescriptor->get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::TShmem) {
+ const Shmem& shmem = aDescriptor->get_SurfaceDescriptorBuffer().data().get_Shmem();
+ shmem.AssertInvariants();
+ MOZ_ASSERT(mShadowManager &&
+ mShadowManager->IsTrackingSharedMemory(shmem.mSegment));
+ }
+}
+#endif
+
+void
+ShadowLayerForwarder::UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapPaint(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTiledLayerBuffer(aTileLayerDescriptor)));
+}
+
+void
+ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddPaint(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
+}
+
+void
+ShadowLayerForwarder::UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ AutoTArray<TimedTexture,4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ ReadLockDescriptor readLock;
+ t.mTextureClient->SerializeReadLock(readLock);
+ textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
+ readLock,
+ t.mTimeStamp, t.mPictureRect,
+ t.mFrameID, t.mProducerID));
+ if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
+ && t.mTextureClient->HasIntermediateBuffer()) {
+
+ // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
+ // race with updates on the main thread. In this case we want the transaction
+ // to be synchronous.
+ mTxn->MarkSyncTransaction();
+ }
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+ OpUseTexture(textures)));
+}
+
+void
+ShadowLayerForwarder::UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite)
+{
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ MOZ_ASSERT(aTextureOnWhite);
+ MOZ_ASSERT(aTextureOnBlack);
+ MOZ_ASSERT(aCompositable->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
+ MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+
+ ReadLockDescriptor readLockW;
+ ReadLockDescriptor readLockB;
+ aTextureOnBlack->SerializeReadLock(readLockB);
+ aTextureOnWhite->SerializeReadLock(readLockW);
+
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
+ mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
+
+ mTxn->AddEdit(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpUseComponentAlphaTextures(
+ nullptr, aTextureOnBlack->GetIPDLActor(),
+ nullptr, aTextureOnWhite->GetIPDLActor(),
+ readLockB, readLockW)
+ )
+ );
+}
+
+static bool
+AddOpDestroy(Transaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+ if (!aTxn->Opened()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+ if (synchronously) {
+ aTxn->MarkSyncTransaction();
+ }
+
+ return true;
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+ return AddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+ return AddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
+void
+ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture)
+{
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
+ if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
+ // We don't have an actor anymore, don't try to use it!
+ return;
+ }
+
+ mTxn->AddEdit(
+ CompositableOperation(
+ nullptr, aCompositable->GetIPDLActor(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+ if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTxn->MarkSyncTransaction();
+ }
+}
+
+bool
+ShadowLayerForwarder::InWorkerThread()
+{
+ return MessageLoop::current() && (GetTextureForwarder()->GetMessageLoop()->id() == MessageLoop::current()->id());
+}
+
+void
+ShadowLayerForwarder::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations)
+{
+ // Cache new plugin widget configs here until we call update, at which
+ // point this data will get shipped over to chrome.
+ mPluginWindowData.Clear();
+ for (uint32_t idx = 0; idx < aConfigurations.Length(); idx++) {
+ const nsIWidget::Configuration& configuration = aConfigurations[idx];
+ mPluginWindowData.AppendElement(PluginWindowData(configuration.mWindowID,
+ configuration.mClipRegion,
+ configuration.mBounds,
+ configuration.mVisible));
+ }
+}
+
+void
+ShadowLayerForwarder::SendPaintTime(uint64_t aId, TimeDuration aPaintTime)
+{
+ if (!IPCOpen() ||
+ !mShadowManager->SendPaintTime(aId, aPaintTime)) {
+ NS_WARNING("Could not send paint times over IPC");
+ }
+}
+
+bool
+ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
+ const nsIntRegion& aRegionToClear,
+ uint64_t aId,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ bool* aSent)
+{
+ *aSent = false;
+
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ if (!IPCOpen()) {
+ return false;
+ }
+
+ GetCompositorBridgeChild()->WillEndTransaction();
+
+ MOZ_ASSERT(aId);
+
+ PROFILER_LABEL("ShadowLayerForwarder", "EndTransaction",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ RenderTraceScope rendertrace("Foward Transaction", "000091");
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ DiagnosticTypes diagnostics = gfxPlatform::GetPlatform()->GetLayerDiagnosticTypes();
+ if (mDiagnosticTypes != diagnostics) {
+ mDiagnosticTypes = diagnostics;
+ mTxn->AddEdit(OpSetDiagnosticTypes(diagnostics));
+ }
+ if (mWindowOverlayChanged) {
+ mTxn->AddEdit(OpWindowOverlayChanged());
+ }
+
+ AutoTxnEnd _(mTxn);
+
+ if (mTxn->Empty() && !mTxn->RotationChanged()) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] 0-length cset (?) and no rotation event, skipping Update()"));
+ return true;
+ }
+
+ if (!mTxn->mPaints.empty()) {
+ // With some platforms, telling the drawing backend that there will be no more
+ // drawing for this frame helps with preventing command queues from spanning
+ // across multiple frames.
+ gfxPlatform::GetPlatform()->FlushContentDrawing();
+ }
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] destroying buffers..."));
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] building transaction..."));
+
+ // We purposely add attribute-change ops to the final changeset
+ // before we add paint ops. This allows layers to record the
+ // attribute changes before new pixels arrive, which can be useful
+ // for setting up back/front buffers.
+ RenderTraceScope rendertrace2("Foward Transaction", "000092");
+ for (ShadowableLayerSet::Iterator it(&mTxn->mMutants);
+ !it.Done(); it.Next()) {
+ ShadowableLayer* shadow = it.Get()->GetKey();
+
+ if (!shadow->HasShadow()) {
+ continue;
+ }
+ Layer* mutant = shadow->AsLayer();
+ MOZ_ASSERT(!!mutant, "unshadowable layer?");
+
+ LayerAttributes attrs;
+ CommonLayerAttributes& common = attrs.common();
+ common.layerBounds() = mutant->GetLayerBounds();
+ common.visibleRegion() = mutant->GetVisibleRegion();
+ common.eventRegions() = mutant->GetEventRegions();
+ common.postXScale() = mutant->GetPostXScale();
+ common.postYScale() = mutant->GetPostYScale();
+ common.transform() = mutant->GetBaseTransform();
+ common.transformIsPerspective() = mutant->GetTransformIsPerspective();
+ common.contentFlags() = mutant->GetContentFlags();
+ common.opacity() = mutant->GetOpacity();
+ common.useClipRect() = !!mutant->GetClipRect();
+ common.clipRect() = (common.useClipRect() ?
+ *mutant->GetClipRect() : ParentLayerIntRect());
+ common.scrolledClip() = mutant->GetScrolledClip();
+ common.isFixedPosition() = mutant->GetIsFixedPosition();
+ if (mutant->GetIsFixedPosition()) {
+ common.fixedPositionScrollContainerId() = mutant->GetFixedPositionScrollContainerId();
+ common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor();
+ common.fixedPositionSides() = mutant->GetFixedPositionSides();
+ }
+ common.isStickyPosition() = mutant->GetIsStickyPosition();
+ if (mutant->GetIsStickyPosition()) {
+ common.stickyScrollContainerId() = mutant->GetStickyScrollContainerId();
+ common.stickyScrollRangeOuter() = mutant->GetStickyScrollRangeOuter();
+ common.stickyScrollRangeInner() = mutant->GetStickyScrollRangeInner();
+ } else {
+#ifdef MOZ_VALGRIND
+ // Initialize these so that Valgrind doesn't complain when we send them
+ // to another process.
+ common.stickyScrollContainerId() = 0;
+ common.stickyScrollRangeOuter() = LayerRect();
+ common.stickyScrollRangeInner() = LayerRect();
+#endif
+ }
+ common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId();
+ common.scrollbarDirection() = mutant->GetScrollbarDirection();
+ common.scrollbarThumbRatio() = mutant->GetScrollbarThumbRatio();
+ common.isScrollbarContainer() = mutant->IsScrollbarContainer();
+ common.mixBlendMode() = (int8_t)mutant->GetMixBlendMode();
+ common.forceIsolatedGroup() = mutant->GetForceIsolatedGroup();
+ if (Layer* maskLayer = mutant->GetMaskLayer()) {
+ common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
+ } else {
+ common.maskLayerChild() = nullptr;
+ }
+ common.maskLayerParent() = nullptr;
+ common.animations() = mutant->GetAnimations();
+ common.invalidRegion() = mutant->GetInvalidRegion().GetRegion();
+ common.scrollMetadata() = mutant->GetAllScrollMetadata();
+ for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) {
+ auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer());
+ common.ancestorMaskLayersChild().AppendElement(layer);
+ }
+ nsCString log;
+ mutant->GetDisplayListLog(log);
+ common.displayListLog() = log;
+
+ attrs.specific() = null_t();
+ mutant->FillSpecificAttributes(attrs.specific());
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
+
+ mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
+ }
+
+ AutoTArray<Edit, 10> cset;
+ size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size();
+ if (nCsets == 0 && !mTxn->RotationChanged()) {
+ return true;
+ }
+
+ cset.SetCapacity(nCsets);
+ if (!mTxn->mCset.empty()) {
+ cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size());
+ }
+ // Paints after non-paint ops, including attribute changes. See
+ // above.
+ if (!mTxn->mPaints.empty()) {
+ cset.AppendElements(&mTxn->mPaints.front(), mTxn->mPaints.size());
+ }
+
+ mWindowOverlayChanged = false;
+
+ TargetConfig targetConfig(mTxn->mTargetBounds,
+ mTxn->mTargetRotation,
+ mTxn->mTargetOrientation,
+ aRegionToClear);
+
+ if (!GetTextureForwarder()->IsSameProcess()) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
+ PlatformSyncBeforeUpdate();
+ }
+
+ profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END);
+ if (mTxn->mSwapRequired) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
+ RenderTraceScope rendertrace3("Forward Transaction", "000093");
+ if (!mShadowManager->SendUpdate(cset, mTxn->mDestroyedActors,
+ GetFwdTransactionId(),
+ aId, targetConfig, mPluginWindowData,
+ mIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction,
+ aTransactionStart, mPaintSyncId, aReplies)) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+ return false;
+ }
+ } else {
+ // If we don't require a swap we can call SendUpdateNoSwap which
+ // assumes that aReplies is empty (DEBUG assertion)
+ MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
+ RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
+ if (!mShadowManager->SendUpdateNoSwap(cset, mTxn->mDestroyedActors,
+ GetFwdTransactionId(),
+ aId, targetConfig, mPluginWindowData,
+ mIsFirstPaint, aScheduleComposite,
+ aPaintSequenceNumber, aIsRepeatTransaction,
+ aTransactionStart, mPaintSyncId)) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+ return false;
+ }
+ }
+
+ *aSent = true;
+ mIsFirstPaint = false;
+ mPaintSyncId = 0;
+ MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
+ return true;
+}
+
+void
+ShadowLayerForwarder::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+bool
+ShadowLayerForwarder::IPCOpen() const
+{
+ return HasShadowManager() && mShadowManager->IPCOpen();
+}
+
+/**
+ * We bail out when we have no shadow manager. That can happen when the
+ * layer manager is created by the preallocated process.
+ * See bug 914843 for details.
+ */
+PLayerChild*
+ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ ShadowLayerChild* child = new ShadowLayerChild();
+ if (!mShadowManager->SendPLayerConstructor(child)) {
+ return nullptr;
+ }
+
+ child->SetShadowableLayer(aLayer);
+ return child;
+}
+
+#if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+void
+ShadowLayerForwarder::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer)
+{
+#ifdef GFX_COMPOSITOR_LOGGING
+ printf("ShadowLayerForwarder::Connect(Compositable)\n");
+#endif
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(mShadowManager);
+ if (!IPCOpen()) {
+ return;
+ }
+ PCompositableChild* actor =
+ mShadowManager->SendPCompositableConstructor(aCompositable->GetTextureInfo());
+ if (!actor) {
+ return;
+ }
+ aCompositable->InitIPDLActor(actor);
+}
+
+void ShadowLayerForwarder::Attach(CompositableClient* aCompositable,
+ ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aCompositable);
+ mTxn->AddEdit(OpAttachCompositable(nullptr, Shadow(aLayer),
+ nullptr, aCompositable->GetIPDLActor()));
+}
+
+void ShadowLayerForwarder::AttachAsyncCompositable(uint64_t aCompositableID,
+ ShadowableLayer* aLayer)
+{
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aCompositableID != 0); // zero is always an invalid compositable id.
+ mTxn->AddEdit(OpAttachAsyncCompositable(nullptr, Shadow(aLayer),
+ aCompositableID));
+}
+
+void ShadowLayerForwarder::SetShadowManager(PLayerTransactionChild* aShadowManager)
+{
+ mShadowManager = static_cast<LayerTransactionChild*>(aShadowManager);
+ mShadowManager->SetForwarder(this);
+}
+
+void ShadowLayerForwarder::StopReceiveAsyncParentMessge()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SetForwarder(nullptr);
+}
+
+void ShadowLayerForwarder::ClearCachedResources()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendClearCachedResources();
+}
+
+void ShadowLayerForwarder::Composite()
+{
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendForceComposite();
+}
+
+bool
+IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
+{
+ return aSurface.type() != SurfaceDescriptor::T__None &&
+ aSurface.type() != SurfaceDescriptor::Tnull_t;
+}
+
+uint8_t*
+GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+ MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor));
+ MOZ_RELEASE_ASSERT(aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer, "GFX: surface descriptor is not the right type.");
+
+ auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data();
+ if (memOrShmem.type() == MemoryOrShmem::TShmem) {
+ return memOrShmem.get_Shmem().get<uint8_t>();
+ } else {
+ return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+ if (aDescriptor.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ return nullptr;
+ }
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(),
+ rgb.format());
+}
+
+already_AddRefed<gfx::DrawTarget>
+GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend)
+{
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
+ data, rgb.size(),
+ stride, rgb.format());
+}
+
+void
+DestroySurfaceDescriptor(IShmemAllocator* aAllocator, SurfaceDescriptor* aSurface)
+{
+ MOZ_ASSERT(aSurface);
+
+ SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer();
+ switch (desc.data().type()) {
+ case MemoryOrShmem::TShmem: {
+ aAllocator->DeallocShmem(desc.data().get_Shmem());
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t();
+ GfxMemoryImageReporter::WillFree(ptr);
+ delete [] ptr;
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("surface type not implemented!");
+ }
+ *aSurface = SurfaceDescriptor();
+}
+
+bool
+ShadowLayerForwarder::AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer)
+{
+ if (!IPCOpen()) {
+ return false;
+ }
+ return AllocSurfaceDescriptorWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS, aBuffer);
+}
+
+bool
+ShadowLayerForwarder::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer)
+{
+ if (!IPCOpen()) {
+ return false;
+ }
+ gfx::SurfaceFormat format =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent);
+ size_t size = ImageDataSerializer::ComputeRGBBufferSize(aSize, format);
+ if (!size) {
+ return false;
+ }
+
+ MemoryOrShmem bufferDesc;
+ if (GetTextureForwarder()->IsSameProcess()) {
+ uint8_t* data = new (std::nothrow) uint8_t[size];
+ if (!data) {
+ return false;
+ }
+ GfxMemoryImageReporter::DidAlloc(data);
+ memset(data, 0, size);
+ bufferDesc = reinterpret_cast<uintptr_t>(data);
+ } else {
+
+ mozilla::ipc::Shmem shmem;
+ if (!GetTextureForwarder()->AllocUnsafeShmem(size, OptimalShmemType(), &shmem)) {
+ return false;
+ }
+
+ bufferDesc = shmem;
+ }
+
+ // Use an intermediate buffer by default. Skipping the intermediate buffer is
+ // only possible in certain configurations so let's keep it simple here for now.
+ const bool hasIntermediateBuffer = true;
+ *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+ bufferDesc);
+
+ return true;
+}
+
+/* static */ bool
+ShadowLayerForwarder::IsShmem(SurfaceDescriptor* aSurface)
+{
+ return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
+ && (aSurface->get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::TShmem);
+}
+
+void
+ShadowLayerForwarder::DestroySurfaceDescriptor(SurfaceDescriptor* aSurface)
+{
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(IPCOpen());
+ if (!IPCOpen() || !aSurface) {
+ return;
+ }
+
+ ::mozilla::layers::DestroySurfaceDescriptor(GetTextureForwarder(), aSurface);
+}
+
+void
+ShadowLayerForwarder::UpdateFwdTransactionId()
+{
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge) {
+ compositorBridge->UpdateFwdTransactionId();
+ }
+}
+
+uint64_t
+ShadowLayerForwarder::GetFwdTransactionId()
+{
+ auto compositorBridge = GetCompositorBridgeChild();
+ MOZ_DIAGNOSTIC_ASSERT(compositorBridge);
+ return compositorBridge ? compositorBridge->GetFwdTransactionId() : 0;
+}
+
+CompositorBridgeChild*
+ShadowLayerForwarder::GetCompositorBridgeChild()
+{
+ if (mCompositorBridgeChild) {
+ return mCompositorBridgeChild;
+ }
+ if (!mShadowManager) {
+ return nullptr;
+ }
+ mCompositorBridgeChild = static_cast<CompositorBridgeChild*>(mShadowManager->Manager());
+ return mCompositorBridgeChild;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h
new file mode 100644
index 000000000..8b207eb1a
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayers_h
+#define mozilla_layers_ShadowLayers_h 1
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArrayForwardDeclare.h" // for InfallibleTArray
+#include "nsIWidget.h"
+#include <vector>
+#include "nsExpirationTracker.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayerManager;
+class CompositorBridgeChild;
+class EditReply;
+class FixedSizeSmallShmemSectionAllocator;
+class ImageContainer;
+class Layer;
+class PLayerChild;
+class PLayerTransactionChild;
+class LayerTransactionChild;
+class ShadowableLayer;
+class SurfaceDescriptor;
+class TextureClient;
+class ThebesBuffer;
+class ThebesBufferData;
+class Transaction;
+
+/**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource
+{
+public:
+ virtual void NotifyInactive() = 0;
+ nsExpirationState* GetExpirationState() { return &mExpirationState; }
+ bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+private:
+ nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3>
+{
+public:
+ ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName)
+ : nsExpirationTracker(aExpirationCycle, aName)
+ {}
+
+ virtual void NotifyExpired(ActiveResource* aResource) override
+ {
+ RemoveObject(aResource);
+ aResource->NotifyInactive();
+ }
+};
+
+/**
+ * We want to share layer trees across thread contexts and address
+ * spaces for several reasons; chief among them
+ *
+ * - a parent process can paint a child process's layer tree while
+ * the child process is blocked, say on content script. This is
+ * important on mobile devices where UI responsiveness is key.
+ *
+ * - a dedicated "compositor" process can asynchronously (wrt the
+ * browser process) composite and animate layer trees, allowing a
+ * form of pipeline parallelism between compositor/browser/content
+ *
+ * - a dedicated "compositor" process can take all responsibility for
+ * accessing the GPU, which is desirable on systems with
+ * buggy/leaky drivers because the compositor process can die while
+ * browser and content live on (and failover mechanisms can be
+ * installed to quickly bring up a replacement compositor)
+ *
+ * The Layers model has a crisply defined API, which makes it easy to
+ * safely "share" layer trees. The ShadowLayers API extends Layers to
+ * allow a remote, parent process to access a child process's layer
+ * tree.
+ *
+ * ShadowLayerForwarder publishes a child context's layer tree to a
+ * parent context. This comprises recording layer-tree modifications
+ * into atomic transactions and pushing them over IPC.
+ *
+ * LayerManagerComposite grafts layer subtrees published by child-context
+ * ShadowLayerForwarder(s) into a parent-context layer tree.
+ *
+ * (Advanced note: because our process tree may have a height >2, a
+ * non-leaf subprocess may both receive updates from child processes
+ * and publish them to parent processes. Put another way,
+ * LayerManagers may be both LayerManagerComposites and
+ * ShadowLayerForwarders.)
+ *
+ * There are only shadow types for layers that have different shadow
+ * vs. not-shadow behavior. ColorLayers and ContainerLayers behave
+ * the same way in both regimes (so far).
+ *
+ *
+ * The mecanism to shadow the layer tree on the compositor through IPC works as
+ * follows:
+ * The layer tree is managed on the content thread, and shadowed in the compositor
+ * thread. The shadow layer tree is only kept in sync with whatever happens in
+ * the content thread. To do this we use IPDL protocols. IPDL is a domain
+ * specific language that describes how two processes or thread should
+ * communicate. C++ code is generated from .ipdl files to implement the message
+ * passing, synchronization and serialization logic. To use the generated code
+ * we implement classes that inherit the generated IPDL actor. the ipdl actors
+ * of a protocol PX are PXChild or PXParent (the generated class), and we
+ * conventionally implement XChild and XParent. The Parent side of the protocol
+ * is the one that lives on the compositor thread. Think of IPDL actors as
+ * endpoints of communication. they are useful to send messages and also to
+ * dispatch the message to the right actor on the other side. One nice property
+ * of an IPDL actor is that when an actor, say PXChild is sent in a message, the
+ * PXParent comes out in the other side. we use this property a lot to dispatch
+ * messages to the right layers and compositable, each of which have their own
+ * ipdl actor on both side.
+ *
+ * Most of the synchronization logic happens in layer transactions and
+ * compositable transactions.
+ * A transaction is a set of changes to the layers and/or the compositables
+ * that are sent and applied together to the compositor thread to keep the
+ * LayerComposite in a coherent state.
+ * Layer transactions maintain the shape of the shadow layer tree, and
+ * synchronize the texture data held by compositables. Layer transactions
+ * are always between the content thread and the compositor thread.
+ * Compositable transactions are subset of a layer transaction with which only
+ * compositables and textures can be manipulated, and does not always originate
+ * from the content thread. (See CompositableForwarder.h and ImageBridgeChild.h)
+ */
+
+class ShadowLayerForwarder final : public LayersIPCActor
+ , public CompositableForwarder
+ , public LegacySurfaceDescriptorAllocator
+{
+ friend class ClientLayerManager;
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ShadowLayerForwarder, override);
+
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ /**
+ * Adds an edit in the layers transaction in order to attach
+ * the corresponding compositable and layer on the compositor side.
+ * Connect must have been called on aCompositable beforehand.
+ */
+ void Attach(CompositableClient* aCompositable,
+ ShadowableLayer* aLayer);
+
+ /**
+ * Adds an edit in the transaction in order to attach a Compositable that
+ * is not managed by this ShadowLayerForwarder (for example, by ImageBridge
+ * in the case of async-video).
+ * Since the compositable is not managed by this forwarder, we can't use
+ * the compositable or it's IPDL actor here, so we use an ID instead, that
+ * is matched on the compositor side.
+ */
+ void AttachAsyncCompositable(uint64_t aCompositableID,
+ ShadowableLayer* aLayer);
+
+ /**
+ * Begin recording a transaction to be forwarded atomically to a
+ * LayerManagerComposite.
+ */
+ void BeginTransaction(const gfx::IntRect& aTargetBounds,
+ ScreenRotation aRotation,
+ mozilla::dom::ScreenOrientationInternal aOrientation);
+
+ /**
+ * The following methods may only be called after BeginTransaction()
+ * but before EndTransaction(). They mirror the LayerManager
+ * interface in Layers.h.
+ */
+
+ /**
+ * Notify the shadow manager that a new, "real" layer has been
+ * created, and a corresponding shadow layer should be created in
+ * the compositing process.
+ */
+ void CreatedPaintedLayer(ShadowableLayer* aThebes);
+ void CreatedContainerLayer(ShadowableLayer* aContainer);
+ void CreatedImageLayer(ShadowableLayer* aImage);
+ void CreatedColorLayer(ShadowableLayer* aColor);
+ void CreatedCanvasLayer(ShadowableLayer* aCanvas);
+ void CreatedRefLayer(ShadowableLayer* aRef);
+
+ /**
+ * At least one attribute of |aMutant| has changed, and |aMutant|
+ * needs to sync to its shadow layer. This initial implementation
+ * forwards all attributes when any is mutated.
+ */
+ void Mutated(ShadowableLayer* aMutant);
+
+ void SetRoot(ShadowableLayer* aRoot);
+ /**
+ * Insert |aChild| after |aAfter| in |aContainer|. |aAfter| can be
+ * nullptr to indicated that |aChild| should be appended to the end of
+ * |aContainer|'s child list.
+ */
+ void InsertAfter(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+ void RemoveChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild);
+ void RepositionChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+
+ /**
+ * Set aMaskLayer as the mask on aLayer.
+ * Note that only image layers are properly supported
+ * LayerTransactionParent::UpdateMask and accompanying ipdl
+ * will need changing to update properties for other kinds
+ * of mask layer.
+ */
+ void SetMask(ShadowableLayer* aLayer,
+ ShadowableLayer* aMaskLayer);
+
+ /**
+ * See CompositableForwarder::UseTiledLayerBuffer
+ */
+ void UseTiledLayerBuffer(CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
+
+ bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+ bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by aLayer
+ * and aIdentifier has been updated to aThebesBuffer.
+ */
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override;
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ /**
+ * Used for debugging to tell the compositor how long this frame took to paint.
+ */
+ void SendPaintTime(uint64_t aId, TimeDuration aPaintTime);
+
+ /**
+ * End the current transaction and forward it to LayerManagerComposite.
+ * |aReplies| are directions from the LayerManagerComposite to the
+ * caller of EndTransaction().
+ */
+ bool EndTransaction(InfallibleTArray<EditReply>* aReplies,
+ const nsIntRegion& aRegionToClear,
+ uint64_t aId,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ const mozilla::TimeStamp& aTransactionStart,
+ bool* aSent);
+
+ /**
+ * Set an actor through which layer updates will be pushed.
+ */
+ void SetShadowManager(PLayerTransactionChild* aShadowManager);
+
+ /**
+ * Layout calls here to cache current plugin widget configuration
+ * data. We ship this across with the rest of the layer updates when
+ * we update. Chrome handles applying these changes.
+ */
+ void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations);
+
+ void StopReceiveAsyncParentMessge();
+
+ void ClearCachedResources();
+
+ void Composite();
+
+ /**
+ * True if this is forwarding to a LayerManagerComposite.
+ */
+ bool HasShadowManager() const { return !!mShadowManager; }
+ LayerTransactionChild* GetShadowManager() const { return mShadowManager.get(); }
+
+ virtual void WindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+ /**
+ * The following Alloc/Open/Destroy interfaces abstract over the
+ * details of working with surfaces that are shared across
+ * processes. They provide the glue between C++ Layers and the
+ * LayerComposite IPC system.
+ *
+ * The basic lifecycle is
+ *
+ * - a Layer needs a buffer. Its ShadowableLayer subclass calls
+ * AllocBuffer(), then calls one of the Created*Buffer() methods
+ * above to transfer the (temporary) front buffer to its
+ * LayerComposite in the other process. The Layer needs a
+ * gfxASurface to paint, so the ShadowableLayer uses
+ * OpenDescriptor(backBuffer) to get that surface, and hands it
+ * out to the Layer.
+ *
+ * - a Layer has painted new pixels. Its ShadowableLayer calls one
+ * of the Painted*Buffer() methods above with the back buffer
+ * descriptor. This notification is forwarded to the LayerComposite,
+ * which uses OpenDescriptor() to access the newly-painted pixels.
+ * The LayerComposite then updates its front buffer in a Layer- and
+ * platform-dependent way, and sends a surface descriptor back to
+ * the ShadowableLayer that becomes its new back back buffer.
+ *
+ * - a Layer wants to destroy its buffers. Its ShadowableLayer
+ * calls Destroyed*Buffer(), which gives up control of the back
+ * buffer descriptor. The actual back buffer surface is then
+ * destroyed using DestroySharedSurface() just before notifying
+ * the parent process. When the parent process is notified, the
+ * LayerComposite also calls DestroySharedSurface() on its front
+ * buffer, and the double-buffer pair is gone.
+ */
+
+ virtual bool IPCOpen() const override;
+
+ /**
+ * Construct a shadow of |aLayer| on the "other side", at the
+ * LayerManagerComposite.
+ */
+ PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer);
+
+ /**
+ * Flag the next paint as the first for a document.
+ */
+ void SetIsFirstPaint() { mIsFirstPaint = true; }
+
+ void SetPaintSyncId(int32_t aSyncId) { mPaintSyncId = aSyncId; }
+
+ void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
+ static void PlatformSyncBeforeUpdate();
+
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) override;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) override;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override;
+
+ virtual void UpdateFwdTransactionId() override;
+ virtual uint64_t GetFwdTransactionId() override;
+
+ bool InForwarderThread() override {
+ return NS_IsMainThread();
+ }
+
+ // Returns true if aSurface wraps a Shmem.
+ static bool IsShmem(SurfaceDescriptor* aSurface);
+
+ TextureForwarder* GetTextureForwarder() override { return GetCompositorBridgeChild(); }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); }
+protected:
+ virtual ~ShadowLayerForwarder();
+
+ explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager);
+
+#ifdef DEBUG
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const;
+#else
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {}
+#endif
+
+ bool InWorkerThread();
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ RefPtr<LayerTransactionChild> mShadowManager;
+ RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+
+private:
+
+ ClientLayerManager* mClientLayerManager;
+ Transaction* mTxn;
+ MessageLoop* mMessageLoop;
+ DiagnosticTypes mDiagnosticTypes;
+ bool mIsFirstPaint;
+ bool mWindowOverlayChanged;
+ int32_t mPaintSyncId;
+ InfallibleTArray<PluginWindowData> mPluginWindowData;
+ UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
+};
+
+class CompositableClient;
+
+/**
+ * A ShadowableLayer is a Layer can be shared with a parent context
+ * through a ShadowLayerForwarder. A ShadowableLayer maps to a
+ * Shadow*Layer in a parent context.
+ *
+ * Note that ShadowLayers can themselves be ShadowableLayers.
+ */
+class ShadowableLayer
+{
+public:
+ virtual ~ShadowableLayer() {}
+
+ virtual Layer* AsLayer() = 0;
+
+ /**
+ * True if this layer has a shadow in a parent process.
+ */
+ bool HasShadow() { return !!mShadow; }
+
+ /**
+ * Return the IPC handle to a Shadow*Layer referring to this if one
+ * exists, nullptr if not.
+ */
+ PLayerChild* GetShadow() { return mShadow; }
+
+ virtual CompositableClient* GetCompositableClient() { return nullptr; }
+protected:
+ ShadowableLayer() : mShadow(nullptr) {}
+
+ PLayerChild* mShadow;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayers_h
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
new file mode 100644
index 000000000..6a1bbcac0
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedPlanarYCbCrImage.h"
+#include <stddef.h> // for size_t
+#include <stdio.h> // for printf
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Types.h" // for SurfaceFormat::SurfaceFormat::YUV
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for Image::AddRef
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+
+SharedPlanarYCbCrImage::SharedPlanarYCbCrImage(ImageClient* aCompositable)
+: mCompositable(aCompositable)
+{
+ MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
+}
+
+SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() {
+ MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
+
+ if (mCompositable->GetAsyncID() != 0 &&
+ !InImageBridgeChildThread()) {
+ if (mTextureClient) {
+ ADDREF_MANUALLY(mTextureClient);
+ ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
+ mTextureClient = nullptr;
+ }
+ }
+}
+
+size_t
+SharedPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ // NB: Explicitly skipping mTextureClient, the memory is already reported
+ // at time of allocation in GfxMemoryImageReporter.
+ // Not owned:
+ // - mCompositable
+ return 0;
+}
+
+TextureClient*
+SharedPlanarYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient.get();
+}
+
+uint8_t*
+SharedPlanarYCbCrImage::GetBuffer()
+{
+ // This should never be used
+ MOZ_ASSERT(false);
+ return nullptr;
+}
+
+already_AddRefed<gfx::SourceSurface>
+SharedPlanarYCbCrImage::GetAsSourceSurface()
+{
+ if (!IsValid()) {
+ NS_WARNING("Can't get as surface");
+ return nullptr;
+ }
+ return PlanarYCbCrImage::GetAsSourceSurface();
+}
+
+bool
+SharedPlanarYCbCrImage::CopyData(const PlanarYCbCrData& aData)
+{
+ // If mTextureClient has not already been allocated (through Allocate(aData))
+ // allocate it. This code path is slower than the one used when Allocate has
+ // been called since it will trigger a full copy.
+ PlanarYCbCrData data = aData;
+ if (!mTextureClient && !Allocate(data)) {
+ return false;
+ }
+
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ MOZ_ASSERT(false, "Failed to lock the texture.");
+ return false;
+ }
+
+ if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
+ MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
+ return false;
+ }
+ mTextureClient->MarkImmutable();
+ return true;
+}
+
+// needs to be overriden because the parent class sets mBuffer which we
+// do not want to happen.
+uint8_t*
+SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
+{
+ MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
+ size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize);
+ if (!size) {
+ return nullptr;
+ }
+
+ // XXX Add YUVColorSpace handling. Use YUVColorSpace::BT601 for now.
+ mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(),
+ size,
+ YUVColorSpace::BT601,
+ mCompositable->GetTextureFlags());
+
+ // get new buffer _without_ setting mBuffer.
+ if (!mTextureClient) {
+ return nullptr;
+ }
+
+ // update buffer size
+ mBufferSize = size;
+
+ MappedYCbCrTextureData mapped;
+ if (mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ // The caller expects a pointer to the beginning of the writable part of the
+ // buffer which is where the y channel starts by default.
+ return mapped.y.data;
+ } else {
+ MOZ_CRASH("GFX: Cannot borrow mapped YCbCr data");
+ }
+}
+
+bool
+SharedPlanarYCbCrImage::AdoptData(const Data &aData)
+{
+ // AdoptData is used to update YUV plane offsets without (re)allocating
+ // memory previously allocated with AllocateAndGetNewBuffer().
+
+ MOZ_ASSERT(mTextureClient, "This Image should have already allocated data");
+ if (!mTextureClient) {
+ return false;
+ }
+ mData = aData;
+ mSize = aData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+
+ uint8_t *base = GetBuffer();
+ uint32_t yOffset = aData.mYChannel - base;
+ uint32_t cbOffset = aData.mCbChannel - base;
+ uint32_t crOffset = aData.mCrChannel - base;
+
+ auto fwd = mCompositable->GetForwarder();
+ bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+ fwd->GetCompositorBackendType());
+
+ static_cast<BufferTextureData*>(mTextureClient->GetInternalData())->SetDesciptor(
+ YCbCrDescriptor(aData.mYSize, aData.mCbCrSize, yOffset, cbOffset, crOffset,
+ aData.mStereoMode, aData.mYUVColorSpace, hasIntermediateBuffer)
+ );
+
+ return true;
+}
+
+bool
+SharedPlanarYCbCrImage::IsValid() {
+ return mTextureClient && mTextureClient->IsValid();
+}
+
+bool
+SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData)
+{
+ MOZ_ASSERT(!mTextureClient,
+ "This image already has allocated data");
+ static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
+
+ if (!mCompositable->HasTextureClientRecycler()) {
+ // Initialize TextureClientRecycler
+ mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
+ }
+
+ {
+ YCbCrTextureClientAllocationHelper helper(aData, mCompositable->GetTextureFlags());
+ mTextureClient = mCompositable->GetTextureClientRecycler()->CreateOrRecycle(helper);
+ }
+
+ if (!mTextureClient) {
+ NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
+ return false;
+ }
+
+ MappedYCbCrTextureData mapped;
+ // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
+ // pointers out of the TextureClient and keeps them around, which works only
+ // because the underlyin BufferTextureData is always mapped in memory even outside
+ // of the lock/unlock interval. That's sad and new code should follow this example.
+ if (!mTextureClient->Lock(OpenMode::OPEN_READ) || !mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ MOZ_CRASH("GFX: Cannot lock or borrow mapped YCbCr");
+ }
+
+ aData.mYChannel = mapped.y.data;
+ aData.mCbChannel = mapped.cb.data;
+ aData.mCrChannel = mapped.cr.data;
+
+ // copy some of aData's values in mData (most of them)
+ mData.mYChannel = aData.mYChannel;
+ mData.mCbChannel = aData.mCbChannel;
+ mData.mCrChannel = aData.mCrChannel;
+ mData.mYSize = aData.mYSize;
+ mData.mCbCrSize = aData.mCbCrSize;
+ mData.mPicX = aData.mPicX;
+ mData.mPicY = aData.mPicY;
+ mData.mPicSize = aData.mPicSize;
+ mData.mStereoMode = aData.mStereoMode;
+ mData.mYUVColorSpace = aData.mYUVColorSpace;
+ // those members are not always equal to aData's, due to potentially different
+ // packing.
+ mData.mYSkip = 0;
+ mData.mCbSkip = 0;
+ mData.mCrSkip = 0;
+ mData.mYStride = mData.mYSize.width;
+ mData.mCbCrStride = mData.mCbCrSize.width;
+
+ // do not set mBuffer like in PlanarYCbCrImage because the later
+ // will try to manage this memory without knowing it belongs to a
+ // shmem.
+ mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(mData.mYSize, mData.mCbCrSize);
+ mSize = mData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+
+ mTextureClient->Unlock();
+
+ return mBufferSize > 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.h b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
new file mode 100644
index 000000000..0c1b6e9c8
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+
+#ifndef MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+#define MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+
+class SharedPlanarYCbCrImage : public PlanarYCbCrImage
+{
+public:
+ explicit SharedPlanarYCbCrImage(ImageClient* aCompositable);
+
+protected:
+ ~SharedPlanarYCbCrImage();
+
+public:
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+ virtual uint8_t* GetBuffer() override;
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual bool CopyData(const PlanarYCbCrData& aData) override;
+ virtual bool AdoptData(const Data &aData) override;
+
+ virtual bool Allocate(PlanarYCbCrData& aData);
+ virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
+
+ virtual bool IsValid() override;
+
+ virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ImageClient> mCompositable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp
new file mode 100644
index 000000000..bb3bb968c
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -0,0 +1,113 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedRGBImage.h"
+#include "ImageTypes.h" // for ImageFormat::SHARED_RGB, etc
+#include "Shmem.h" // for Shmem
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "mozilla/gfx/Point.h" // for IntSIze
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h" // for BufferTextureClient, etc
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+// Just big enough for a 1080p RGBA32 frame
+#define MAX_FRAME_SIZE (16 * 1024 * 1024)
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<Image>
+CreateSharedRGBImage(ImageContainer *aImageContainer,
+ gfx::IntSize aSize,
+ gfxImageFormat aImageFormat)
+{
+ NS_ASSERTION(aImageFormat == gfx::SurfaceFormat::A8R8G8B8_UINT32 ||
+ aImageFormat == gfx::SurfaceFormat::X8R8G8B8_UINT32 ||
+ aImageFormat == gfx::SurfaceFormat::R5G6B5_UINT16,
+ "RGB formats supported only");
+
+ if (!aImageContainer) {
+ NS_WARNING("No ImageContainer to allocate SharedRGBImage");
+ return nullptr;
+ }
+
+ RefPtr<SharedRGBImage> rgbImage = aImageContainer->CreateSharedRGBImage();
+ if (!rgbImage) {
+ NS_WARNING("Failed to create SharedRGBImage");
+ return nullptr;
+ }
+ if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
+ NS_WARNING("Failed to allocate a shared image");
+ return nullptr;
+ }
+ return rgbImage.forget();
+}
+
+SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
+: Image(nullptr, ImageFormat::SHARED_RGB)
+, mCompositable(aCompositable)
+{
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::~SharedRGBImage()
+{
+ MOZ_COUNT_DTOR(SharedRGBImage);
+
+ if (mCompositable->GetAsyncID() != 0 &&
+ !InImageBridgeChildThread()) {
+ ADDREF_MANUALLY(mTextureClient);
+ ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
+ mTextureClient = nullptr;
+ }
+}
+
+bool
+SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+{
+ mSize = aSize;
+ mTextureClient = mCompositable->CreateBufferTextureClient(aFormat, aSize,
+ gfx::BackendType::NONE,
+ TextureFlags::DEFAULT);
+ return !!mTextureClient;
+}
+
+uint8_t*
+SharedRGBImage::GetBuffer()
+{
+ MappedTextureData mapped;
+ if (mTextureClient && mTextureClient->BorrowMappedData(mapped)) {
+ return mapped.data;
+ }
+ return 0;
+}
+
+gfx::IntSize
+SharedRGBImage::GetSize()
+{
+ return mSize;
+}
+
+TextureClient*
+SharedRGBImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient.get();
+}
+
+already_AddRefed<gfx::SourceSurface>
+SharedRGBImage::GetAsSourceSurface()
+{
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedRGBImage.h b/gfx/layers/ipc/SharedRGBImage.h
new file mode 100644
index 000000000..2c6009c19
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.h
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHAREDRGBIMAGE_H_
+#define SHAREDRGBIMAGE_H_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint8_t
+#include "ImageContainer.h" // for ISharedImage, Image, etc
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "nsCOMPtr.h" // for already_AddRefed
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+
+already_AddRefed<Image> CreateSharedRGBImage(ImageContainer* aImageContainer,
+ gfx::IntSize aSize,
+ gfxImageFormat aImageFormat);
+
+/**
+ * Stores RGB data in shared memory
+ * It is assumed that the image width and stride are equal
+ */
+class SharedRGBImage : public Image
+{
+public:
+ explicit SharedRGBImage(ImageClient* aCompositable);
+
+protected:
+ ~SharedRGBImage();
+
+public:
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual uint8_t* GetBuffer() override;
+
+ gfx::IntSize GetSize() override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+private:
+ gfx::IntSize mSize;
+ RefPtr<ImageClient> mCompositable;
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SynchronousTask.h b/gfx/layers/ipc/SynchronousTask.h
new file mode 100644
index 000000000..fc20e2843
--- /dev/null
+++ b/gfx/layers/ipc/SynchronousTask.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SYNCHRONOUSTASK_H
+#define MOZILLA_GFX_SYNCHRONOUSTASK_H
+
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+namespace mozilla {
+namespace layers {
+
+// Helper that creates a monitor and a "done" flag, then enters the monitor.
+// This can go away when we switch ImageBridge to an XPCOM thread.
+class MOZ_STACK_CLASS SynchronousTask
+{
+ friend class AutoCompleteTask;
+
+public:
+ explicit SynchronousTask(const char* name)
+ : mMonitor(name),
+ mAutoEnter(mMonitor),
+ mDone(false)
+ {}
+
+ void Wait() {
+ while (!mDone) {
+ mMonitor.Wait();
+ }
+ }
+
+private:
+ void Complete() {
+ mDone = true;
+ mMonitor.NotifyAll();
+ }
+
+private:
+ ReentrantMonitor mMonitor;
+ ReentrantMonitorAutoEnter mAutoEnter;
+ bool mDone;
+};
+
+class MOZ_STACK_CLASS AutoCompleteTask
+{
+public:
+ explicit AutoCompleteTask(SynchronousTask* aTask)
+ : mTask(aTask),
+ mAutoEnter(aTask->mMonitor)
+ {
+ }
+ ~AutoCompleteTask() {
+ mTask->Complete();
+ }
+
+private:
+ SynchronousTask* mTask;
+ ReentrantMonitorAutoEnter mAutoEnter;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/ipc/TextureForwarder.h b/gfx/layers/ipc/TextureForwarder.h
new file mode 100644
index 000000000..0d06fa5f5
--- /dev/null
+++ b/gfx/layers/ipc/TextureForwarder.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER
+#define MOZILLA_LAYERS_TEXTUREFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/KnowsCompositor.h"
+
+namespace mozilla {
+namespace ipc {
+class IShmemAllocator;
+}
+namespace layers {
+
+/**
+ * An abstract interface for classes that implement the autogenerated
+ * IPDL actor class. Lets us check if they are still valid for IPC.
+ */
+class LayersIPCActor {
+public:
+ virtual bool IPCOpen() const { return true; }
+};
+
+/**
+ * An abstract interface for LayersIPCActors that implement a top-level
+ * IPDL protocol so also have their own channel.
+ * Has their own MessageLoop for message dispatch, and can allocate
+ * shmem.
+ */
+class LayersIPCChannel : public LayersIPCActor
+ , public mozilla::ipc::IShmemAllocator {
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop* GetMessageLoop() const = 0;
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() { return nullptr; }
+
+ virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
+
+protected:
+ virtual ~LayersIPCChannel() {}
+};
+
+/**
+ * An abstract interface for classes that can allocate PTexture objects
+ * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity
+ * since all our implementations use both, but could be independant if needed.
+ */
+class TextureForwarder : public LayersIPCChannel {
+public:
+ /**
+ * Create a TextureChild/Parent pair as as well as the TextureHost on the parent side.
+ */
+ virtual PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h b/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
new file mode 100644
index 000000000..e64705478
--- /dev/null
+++ b/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
+#define THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_
+
+#include "base/message_loop.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+inline MessageLoop* GetMainLoopAssertingMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return MessageLoop::current();
+}
+
+inline MessageLoop* GetMainLoop()
+{
+ static MessageLoop* sMainLoop = GetMainLoopAssertingMainThread();
+ return sMainLoop;
+}
+
+struct HelperForMainThreadDestruction
+{
+ HelperForMainThreadDestruction()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ GetMainLoop();
+ }
+
+ ~HelperForMainThreadDestruction()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+};
+
+template<typename T>
+struct DeleteOnMainThreadTask : public Runnable
+{
+ T* mToDelete;
+ explicit DeleteOnMainThreadTask(T* aToDelete) : mToDelete(aToDelete) {}
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ mToDelete->DeleteToBeCalledOnMainThread();
+ return NS_OK;
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(_class) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return (nsrefcnt) count; \
+ } \
+ void DeleteToBeCalledOnMainThread() { \
+ MOZ_ASSERT(NS_IsMainThread()); \
+ NS_LOG_RELEASE(this, 0, #_class); \
+ delete this; \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ if (count == 0) { \
+ if (NS_IsMainThread()) { \
+ DeleteToBeCalledOnMainThread(); \
+ } else { \
+ NS_DispatchToMainThread( \
+ new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \
+ } \
+ } else { \
+ NS_LOG_RELEASE(this, count, #_class); \
+ } \
+ return count; \
+ } \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+private: \
+ ::mozilla::layers::HelperForMainThreadDestruction mHelperForMainThreadDestruction; \
+public:
+
+#endif
diff --git a/gfx/layers/ipc/VideoBridgeChild.cpp b/gfx/layers/ipc/VideoBridgeChild.cpp
new file mode 100644
index 000000000..9651c563e
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeChild.h"
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+
+namespace mozilla {
+namespace layers {
+
+StaticRefPtr<VideoBridgeChild> sVideoBridgeChildSingleton;
+
+/* static */ void
+VideoBridgeChild::Startup()
+{
+ sVideoBridgeChildSingleton = new VideoBridgeChild();
+ RefPtr<VideoBridgeParent> parent = new VideoBridgeParent();
+
+ MessageLoop* loop = CompositorThreadHolder::Loop();
+
+ sVideoBridgeChildSingleton->Open(parent->GetIPCChannel(),
+ loop,
+ ipc::ChildSide);
+ sVideoBridgeChildSingleton->mIPDLSelfRef = sVideoBridgeChildSingleton;
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+}
+
+/* static */ void
+VideoBridgeChild::Shutdown()
+{
+ if (sVideoBridgeChildSingleton) {
+ sVideoBridgeChildSingleton->Close();
+ sVideoBridgeChildSingleton = nullptr;
+ }
+}
+
+VideoBridgeChild::VideoBridgeChild()
+ : mMessageLoop(MessageLoop::current())
+ , mCanSend(true)
+{
+}
+
+VideoBridgeChild::~VideoBridgeChild()
+{
+}
+
+VideoBridgeChild*
+VideoBridgeChild::GetSingleton()
+{
+ return sVideoBridgeChildSingleton;
+}
+
+bool
+VideoBridgeChild::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ return PVideoBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ MOZ_ASSERT(CanSend());
+ return PVideoBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
+{
+ return PVideoBridgeChild::DeallocShmem(aShmem);
+}
+
+PTextureChild*
+VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t& aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool
+VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor)
+{
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+void
+VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+}
+
+void
+VideoBridgeChild::DeallocPVideoBridgeChild()
+{
+ mIPDLSelfRef = nullptr;
+}
+
+PTextureChild*
+VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
+}
+
+bool VideoBridgeChild::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/VideoBridgeChild.h b/gfx/layers/ipc/VideoBridgeChild.h
new file mode 100644
index 000000000..f5677008e
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H
+#define MOZILLA_GFX_VIDEOBRIDGECHILD_H
+
+#include "mozilla/layers/PVideoBridgeChild.h"
+#include "ISurfaceAllocator.h"
+#include "TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+class VideoBridgeChild final : public PVideoBridgeChild
+ , public TextureForwarder
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override);
+
+ static void Startup();
+ static void Shutdown();
+
+ static VideoBridgeChild* GetSingleton();
+
+ // PVideoBridgeChild
+ PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ bool DeallocPTextureChild(PTextureChild* actor) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPVideoBridgeChild() override;
+
+
+ // ISurfaceAllocator
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // TextureForwarder
+ PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial) override;
+
+ // ClientIPCAllocator
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+ MessageLoop * GetMessageLoop() const override { return mMessageLoop; }
+ void CancelWaitForRecycle(uint64_t aTextureId) override { MOZ_ASSERT(false, "NO RECYCLING HERE"); }
+
+ // ISurfaceAllocator
+ bool IsSameProcess() const override;
+
+ bool CanSend() { return mCanSend; }
+
+private:
+ VideoBridgeChild();
+ ~VideoBridgeChild();
+
+ RefPtr<VideoBridgeChild> mIPDLSelfRef;
+ MessageLoop* mMessageLoop;
+ bool mCanSend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/VideoBridgeParent.cpp b/gfx/layers/ipc/VideoBridgeParent.cpp
new file mode 100644
index 000000000..fce2184c8
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -0,0 +1,125 @@
+/* vim: set ts=2 sw=2 et tw=80: */
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeParent.h"
+#include "mozilla/layers/TextureHost.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+
+static VideoBridgeParent* sVideoBridgeSingleton;
+
+VideoBridgeParent::VideoBridgeParent()
+ : mClosed(false)
+{
+ mSelfRef = this;
+ sVideoBridgeSingleton = this;
+}
+
+VideoBridgeParent::~VideoBridgeParent()
+{
+ sVideoBridgeSingleton = nullptr;
+}
+
+/* static */ VideoBridgeParent*
+VideoBridgeParent::GetSingleton()
+{
+ return sVideoBridgeSingleton;
+}
+
+TextureHost*
+VideoBridgeParent::LookupTexture(uint64_t aSerial)
+{
+ return TextureHost::AsTextureHost(mTextureMap[aSerial]);
+}
+
+void
+VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+}
+
+void
+VideoBridgeParent::DeallocPVideoBridgeParent()
+{
+ mSelfRef = nullptr;
+}
+
+PTextureParent*
+VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial)
+{
+ PTextureParent* parent =
+ TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+ mTextureMap[aSerial] = parent;
+ return parent;
+}
+
+bool
+VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor)
+{
+ mTextureMap.erase(TextureHost::GetTextureSerial(actor));
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+void
+VideoBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+ MOZ_ASSERT(false, "AsyncMessages not supported");
+}
+
+bool
+VideoBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool
+VideoBridgeParent::AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem)
+{
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+void
+VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
+{
+ if (mClosed) {
+ return;
+ }
+ PVideoBridgeParent::DeallocShmem(aShmem);
+}
+
+bool
+VideoBridgeParent::IsSameProcess() const
+{
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void
+VideoBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/VideoBridgeParent.h b/gfx/layers/ipc/VideoBridgeParent.h
new file mode 100644
index 000000000..e5560acab
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_VideoBridgeParent_h_
+#define gfx_layers_ipc_VideoBridgeParent_h_
+
+#include "mozilla/layers/PVideoBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class VideoBridgeParent final : public PVideoBridgeParent,
+ public HostIPCAllocator,
+ public ShmemAllocator
+{
+public:
+ VideoBridgeParent();
+ ~VideoBridgeParent();
+
+ static VideoBridgeParent* GetSingleton();
+ TextureHost* LookupTexture(uint64_t aSerial);
+
+ // PVideoBridgeParent
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial) override;
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override
+ {
+ return OtherPid();
+ }
+ void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+ void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ISurfaceAllocator
+ ShmemAllocator* AsShmemAllocator() override { return this; }
+ bool IsSameProcess() const override;
+ bool IPCOpen() const override { return !mClosed; }
+
+ // ShmemAllocator
+ bool AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ void DeallocShmem(ipc::Shmem& aShmem) override;
+
+private:
+ void DeallocPVideoBridgeParent() override;
+
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<VideoBridgeParent> mSelfRef;
+
+ std::map<uint64_t, PTextureParent*> mTextureMap;
+
+ bool mClosed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_VideoBridgeParent_h_
diff --git a/gfx/layers/layerviewer/hide.png b/gfx/layers/layerviewer/hide.png
new file mode 100644
index 000000000..9a92e2c1b
--- /dev/null
+++ b/gfx/layers/layerviewer/hide.png
Binary files differ
diff --git a/gfx/layers/layerviewer/index.html b/gfx/layers/layerviewer/index.html
new file mode 100644
index 000000000..3ad835df5
--- /dev/null
+++ b/gfx/layers/layerviewer/index.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>GFX Display List & Layer Visualizer</title>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="tree.css">
+ <script src="layerTreeView.js"></script>
+ <style>
+
+.csstooltip
+{
+ z-index: 5;
+ background: white;
+ border: solid 1px black;
+ position: absolute;
+ padding: 5px;
+ margin: 5px;
+ max-width: 300px;
+}
+ </style>
+ </head>
+ <body>
+ <h1>GFX Layers dump visualizer:</h1>
+ Paste your display list or layers dump into this textarea:<br>
+ <textarea id="input_layers_dump" style="width:100%; height: 80%;" cols="80" rows="10">
+ClientLayerManager (0x1264f5000)
+ ClientContainerLayer (0x1263fe200) [visible=< (x=0, y=0, w=1457, h=1163); >] [opaqueContent] [metrics0={ [cb=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [sr=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [s=(0,0)] [dp=(x=0.000000, y=0.000000, w=1457.000000, h=1163.000000)] [cdp=(x=0.000000, y=0.000000, w=0.000000, h=0.000000)] [color=rgba(0, 0, 0, 0.000000)] [scrollId=3] [z=1] }]
+ ClientTiledPaintedLayer (0x1263b3600) [bounds=(x=-1, y=0, w=1458, h=1163)] [visible=< (x=0, y=0, w=1457, h=79); >] { hitregion=< (x=0, y=0, w=1457, h=47); (x=-1, y=47, w=1458, h=24); (x=0, y=71, w=1457, h=1092); > dispatchtocontentregion=< (x=68, y=9, w=1375, h=31); (x=944, y=47, w=280, h=24); >} [opaqueContent] [valid=< (x=0, y=0, w=1457, h=79); >]
+ SingleTiledContentClient (0x126f80680)
+ ClientContainerLayer (0x122a33f00) [clip=(x=0, y=79, w=1457, h=1084)] [visible=< (x=0, y=79, w=1457, h=1084); >] [opaqueContent]
+ ClientTiledPaintedLayer (0x11e11a700) [bounds=(x=0, y=79, w=1457, h=1084)] [visible=< (x=0, y=79, w=1457, h=1084); >] { hitregion=< (x=0, y=79, w=1457, h=1084); > dispatchtocontentregion=< (x=0, y=125, w=1457, h=1034); >} [opaqueContent] [valid=< (x=0, y=79, w=1457, h=1084); >]
+ SingleTiledContentClient (0x1226d52c0)
+ </textarea>
+ <br>
+ <input type="button" value="Process pasted log" onclick="log_pasted()" />
+ <br>
+ <br>
+ Help: To get a layers dump go to about:config and set layout.display-list.dump;true or layers.dump;true.
+ <script>
+function log_pasted() {
+ var container = parseMultiLineDump(document.getElementById("input_layers_dump").value);
+ document.body.innerHTML = "";
+ document.body.appendChild(container);
+}
+ </script>
+ </body>
+</html>
diff --git a/gfx/layers/layerviewer/layerTreeView.js b/gfx/layers/layerviewer/layerTreeView.js
new file mode 100644
index 000000000..e5ecac251
--- /dev/null
+++ b/gfx/layers/layerviewer/layerTreeView.js
@@ -0,0 +1,885 @@
+function toFixed(num, fixed) {
+ fixed = fixed || 0;
+ fixed = Math.pow(10, fixed);
+ return Math.floor(num * fixed) / fixed;
+}
+function createElement(name, props) {
+ var el = document.createElement(name);
+
+ for (var key in props) {
+ if (key === "style") {
+ for (var styleName in props.style) {
+ el.style[styleName] = props.style[styleName];
+ }
+ } else {
+ el[key] = props[key];
+ }
+ }
+
+ return el;
+}
+
+function parseDisplayList(lines) {
+ var root = {
+ line: "DisplayListRoot 0",
+ name: "DisplayListRoot",
+ address: "0x0",
+ frame: "Root",
+ children: [],
+ };
+
+ var objectAtIndentation = {
+ "-1": root,
+ };
+
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+
+ var layerObject = {
+ line: line,
+ children: [],
+ }
+ if (!root) {
+ root = layerObject;
+ }
+
+ var matches = line.match("(\\s*)(\\w+)\\sp=(\\w+)\\sf=(.*?)\\((.*?)\\)\\s(z=(\\w+)\\s)?(.*?)?( layer=(\\w+))?$");
+ if (!matches) {
+ dump("Failed to match: " + line + "\n");
+ continue;
+ }
+
+ var indentation = Math.floor(matches[1].length / 2);
+ objectAtIndentation[indentation] = layerObject;
+ var parent = objectAtIndentation[indentation - 1];
+ if (parent) {
+ parent.children.push(layerObject);
+ }
+
+ layerObject.name = matches[2];
+ layerObject.address = matches[3]; // Use 0x prefix to be consistent with layer dump
+ layerObject.frame = matches[4];
+ layerObject.contentDescriptor = matches[5];
+ layerObject.z = matches[7];
+ var rest = matches[8];
+ if (matches[10]) { // WrapList don't provide a layer
+ layerObject.layer = matches[10];
+ }
+ layerObject.rest = rest;
+
+ // the content node name doesn't have a prefix, this makes the parsing easier
+ rest = "content" + rest;
+
+ var fields = {};
+ var nesting = 0;
+ var startIndex;
+ var lastSpace = -1;
+ var lastFieldStart = -1;
+ for (var j = 0; j < rest.length; j++) {
+ if (rest.charAt(j) == '(') {
+ nesting++;
+ if (nesting == 1) {
+ startIndex = j;
+ }
+ } else if (rest.charAt(j) == ')') {
+ nesting--;
+ if (nesting == 0) {
+ var name = rest.substring(lastSpace + 1, startIndex);
+ var value = rest.substring(startIndex + 1, j);
+
+ var rectMatches = value.match("^(.*?),(.*?),(.*?),(.*?)$")
+ if (rectMatches) {
+ layerObject[name] = [
+ parseFloat(rectMatches[1]),
+ parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]),
+ parseFloat(rectMatches[4]),
+ ];
+ } else {
+ layerObject[name] = value;
+ }
+ }
+ } else if (nesting == 0 && rest.charAt(j) == ' ') {
+ lastSpace = j;
+ }
+ }
+ //dump("FIELDS: " + JSON.stringify(fields) + "\n");
+ }
+ return root;
+}
+
+function trim(s){
+ return ( s || '' ).replace( /^\s+|\s+$/g, '' );
+}
+
+function getDataURI(str) {
+ if (str.indexOf("data:image/png;base64,") == 0) {
+ return str;
+ }
+
+ var matches = str.match("data:image/lz4bgra;base64,([0-9]+),([0-9]+),([0-9]+),(.*)");
+ if (!matches)
+ return null;
+
+ var canvas = document.createElement("canvas");
+ var w = parseInt(matches[1]);
+ var stride = parseInt(matches[2]);
+ var h = parseInt(matches[3]);
+ canvas.width = w;
+ canvas.height = h;
+
+ // TODO handle stride
+
+ var binary_string = window.atob(matches[4]);
+ var len = binary_string.length;
+ var bytes = new Uint8Array(len);
+ var decoded = new Uint8Array(stride * h);
+ for (var i = 0; i < len; i++) {
+ var ascii = binary_string.charCodeAt(i);
+ bytes[i] = ascii;
+ }
+
+ var ctxt = canvas.getContext("2d");
+ var out = ctxt.createImageData(w, h);
+ buffer = LZ4_uncompressChunk(bytes, decoded);
+
+ for (var x = 0; x < w; x++) {
+ for (var y = 0; y < h; y++) {
+ out.data[4 * x + 4 * y * w + 0] = decoded[4 * x + y * stride + 2];
+ out.data[4 * x + 4 * y * w + 1] = decoded[4 * x + y * stride + 1];
+ out.data[4 * x + 4 * y * w + 2] = decoded[4 * x + y * stride + 0];
+ out.data[4 * x + 4 * y * w + 3] = decoded[4 * x + y * stride + 3];
+ }
+ }
+
+ ctxt.putImageData(out, 0, 0);
+ return canvas.toDataURL();
+}
+
+function parseLayers(layersDumpLines) {
+ function parseMatrix2x3(str) {
+ str = trim(str);
+
+ // Something like '[ 1 0; 0 1; 0 158; ]'
+ var matches = str.match("^\\[ (.*?) (.*?); (.*?) (.*?); (.*?) (.*?); \\]$");
+ if (!matches) {
+ return null;
+ }
+
+ var matrix = [
+ [parseFloat(matches[1]), parseFloat(matches[2])],
+ [parseFloat(matches[3]), parseFloat(matches[4])],
+ [parseFloat(matches[5]), parseFloat(matches[6])],
+ ];
+
+ return matrix;
+ }
+ function parseColor(str) {
+ str = trim(str);
+
+ // Something like 'rgba(0, 0, 0, 0)'
+ var colorMatches = str.match("^rgba\\((.*), (.*), (.*), (.*)\\)$");
+ if (!colorMatches) {
+ return null;
+ }
+
+ var color = {
+ r: colorMatches[1],
+ g: colorMatches[2],
+ b: colorMatches[3],
+ a: colorMatches[4],
+ };
+ return color;
+ }
+ function parseFloat_cleo(str) {
+ str = trim(str);
+
+ // Something like 2.000
+ if (parseFloat(str) == str) {
+ return parseFloat(str);
+ }
+
+ return null;
+ }
+ function parseRect2D(str) {
+ str = trim(str);
+
+ // Something like '(x=0, y=0, w=2842, h=158)'
+ var rectMatches = str.match("^\\(x=(.*?), y=(.*?), w=(.*?), h=(.*?)\\)$");
+ if (!rectMatches) {
+ return null;
+ }
+
+ var rect = [
+ parseFloat(rectMatches[1]), parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]), parseFloat(rectMatches[4]),
+ ];
+ return rect;
+ }
+ function parseRegion(str) {
+ str = trim(str);
+
+ // Something like '< (x=0, y=0, w=2842, h=158); (x=0, y=1718, w=2842, h=500); >'
+ if (str.charAt(0) != '<' || str.charAt(str.length - 1) != '>') {
+ return null;
+ }
+
+ var region = [];
+ str = trim(str.substring(1, str.length - 1));
+ while (str != "") {
+ var rectMatches = str.match("^\\(x=(.*?), y=(.*?), w=(.*?), h=(.*?)\\);(.*)$");
+ if (!rectMatches) {
+ return null;
+ }
+
+ var rect = [
+ parseFloat(rectMatches[1]), parseFloat(rectMatches[2]),
+ parseFloat(rectMatches[3]), parseFloat(rectMatches[4]),
+ ];
+ str = trim(rectMatches[5]);
+ region.push(rect);
+ }
+ return region;
+ }
+
+ var LAYERS_LINE_REGEX = "(\\s*)(\\w+)\\s\\((\\w+)\\)(.*)";
+
+ var root;
+ var objectAtIndentation = [];
+ for (var i = 0; i < layersDumpLines.length; i++) {
+ // Something like 'ThebesLayerComposite (0x12104cc00) [shadow-visible=< (x=0, y=0, w=1920, h=158); >] [visible=< (x=0, y=0, w=1920, h=158); >] [opaqueContent] [valid=< (x=0, y=0, w=1920, h=2218); >]'
+ var line = layersDumpLines[i].name || layersDumpLines[i];
+
+ var tileMatches = line.match("(\\s*)Tile \\(x=(.*), y=(.*)\\): (.*)");
+ if (tileMatches) {
+ var indentation = Math.floor(matches[1].length / 2);
+ var x = tileMatches[2];
+ var y = tileMatches[3];
+ var dataUri = tileMatches[4];
+ var parent = objectAtIndentation[indentation - 1];
+ var tiles = parent.tiles || {};
+
+ tiles[x] = tiles[x] || {};
+ tiles[x][y] = dataUri;
+
+ parent.tiles = tiles;
+
+ continue;
+ }
+
+ var surfaceMatches = line.match("(\\s*)Surface: (.*)");
+ if (surfaceMatches) {
+ var indentation = Math.floor(matches[1].length / 2);
+ var parent = objectAtIndentation[indentation - 1] || objectAtIndentation[indentation - 2];
+
+ var surfaceURI = surfaceMatches[2];
+ if (parent.surfaceURI != null) {
+ console.log("error: surfaceURI already set for this layer " + parent.line);
+ }
+ parent.surfaceURI = surfaceURI;
+
+ // Look for the buffer-rect offset
+ var contentHostLine = layersDumpLines[i - 2].name || layersDumpLines[i - 2];
+ var matches = contentHostLine.match(LAYERS_LINE_REGEX);
+ if (matches) {
+ var contentHostRest = matches[4];
+ parent.contentHostProp = {};
+ parseProperties(contentHostRest, parent.contentHostProp);
+ }
+
+ continue;
+ }
+
+ var layerObject = {
+ line: line,
+ children: [],
+ }
+ if (!root) {
+ root = layerObject;
+ }
+
+ var matches = line.match(LAYERS_LINE_REGEX);
+ if (!matches) {
+ continue; // Something like a texturehost dump. Safe to ignore
+ }
+
+ if (matches[2].indexOf("TiledContentHost") != -1 ||
+ matches[2].indexOf("GrallocTextureHostOGL") != -1 ||
+ matches[2].indexOf("ContentHost") != -1 ||
+ matches[2].indexOf("ContentClient") != -1 ||
+ matches[2].indexOf("MemoryTextureHost") != -1 ||
+ matches[2].indexOf("ImageHost") != -1) {
+ continue; // We're already pretty good at visualizing these
+ }
+
+ var indentation = Math.floor(matches[1].length / 2);
+ objectAtIndentation[indentation] = layerObject;
+ for (var c = indentation + 1; c < objectAtIndentation.length; c++) {
+ objectAtIndentation[c] = null;
+ }
+ if (indentation > 0) {
+ var parent = objectAtIndentation[indentation - 1];
+ while (!parent) {
+ indentation--;
+ parent = objectAtIndentation[indentation - 1];
+ }
+
+ parent.children.push(layerObject);
+ }
+
+ layerObject.name = matches[2];
+ layerObject.address = matches[3];
+
+ var rest = matches[4];
+
+ function parseProperties(rest, layerObject) {
+ var fields = [];
+ var nesting = 0;
+ var startIndex;
+ for (var j = 0; j < rest.length; j++) {
+ if (rest.charAt(j) == '[') {
+ nesting++;
+ if (nesting == 1) {
+ startIndex = j;
+ }
+ } else if (rest.charAt(j) == ']') {
+ nesting--;
+ if (nesting == 0) {
+ fields.push(rest.substring(startIndex + 1, j));
+ }
+ }
+ }
+
+ for (var j = 0; j < fields.length; j++) {
+ // Something like 'valid=< (x=0, y=0, w=1920, h=2218); >' or 'opaqueContent'
+ var field = fields[j];
+ //dump("FIELD: " + field + "\n");
+ var parts = field.split("=", 2);
+ var fieldName = parts[0];
+ var rest = field.substring(fieldName.length + 1);
+ if (parts.length == 1) {
+ layerObject[fieldName] = "true";
+ layerObject[fieldName].type = "bool";
+ continue;
+ }
+ var float = parseFloat_cleo(rest);
+ if (float) {
+ layerObject[fieldName] = float;
+ layerObject[fieldName].type = "float";
+ continue;
+ }
+ var region = parseRegion(rest);
+ if (region) {
+ layerObject[fieldName] = region;
+ layerObject[fieldName].type = "region";
+ continue;
+ }
+ var rect = parseRect2D(rest);
+ if (rect) {
+ layerObject[fieldName] = rect;
+ layerObject[fieldName].type = "rect2d";
+ continue;
+ }
+ var matrix = parseMatrix2x3(rest);
+ if (matrix) {
+ layerObject[fieldName] = matrix;
+ layerObject[fieldName].type = "matrix2x3";
+ continue;
+ }
+ var color = parseColor(rest);
+ if (color) {
+ layerObject[fieldName] = color;
+ layerObject[fieldName].type = "color";
+ continue;
+ }
+ if (rest[0] == '{' && rest[rest.length - 1] == '}') {
+ var object = {};
+ parseProperties(rest.substring(1, rest.length - 2).trim(), object);
+ layerObject[fieldName] = object;
+ layerObject[fieldName].type = "object";
+ continue;
+ }
+ fieldName = fieldName.split(" ")[0];
+ layerObject[fieldName] = rest[0];
+ layerObject[fieldName].type = "string";
+ }
+ }
+ parseProperties(rest, layerObject);
+
+ if (!layerObject['shadow-transform']) {
+ // No shadow transform = identify
+ layerObject['shadow-transform'] = [[1, 0], [0, 1], [0, 0]];
+ }
+
+ // Compute screenTransformX/screenTransformY
+ // TODO Fully support transforms
+ if (layerObject['shadow-transform'] && layerObject['transform']) {
+ layerObject['screen-transform'] = [layerObject['shadow-transform'][2][0], layerObject['shadow-transform'][2][1]];
+ var currIndentation = indentation - 1;
+ while (currIndentation >= 0) {
+ var transform = objectAtIndentation[currIndentation]['shadow-transform'] || objectAtIndentation[currIndentation]['transform'];
+ if (transform) {
+ layerObject['screen-transform'][0] += transform[2][0];
+ layerObject['screen-transform'][1] += transform[2][1];
+ }
+ currIndentation--;
+ }
+ }
+
+ //dump("Fields: " + JSON.stringify(fields) + "\n");
+ }
+ root.compositeTime = layersDumpLines.compositeTime;
+ //dump("OBJECTS: " + JSON.stringify(root) + "\n");
+ return root;
+}
+function populateLayers(root, displayList, pane, previewParent, hasSeenRoot, contentScale, rootPreviewParent) {
+
+ contentScale = contentScale || 1;
+ rootPreviewParent = rootPreviewParent || previewParent;
+
+ function getDisplayItemForLayer(displayList) {
+ var items = [];
+ if (!displayList) {
+ return items;
+ }
+ if (displayList.layer == root.address) {
+ items.push(displayList);
+ }
+ for (var i = 0; i < displayList.children.length; i++) {
+ var subDisplayItems = getDisplayItemForLayer(displayList.children[i]);
+ for (var j = 0; j < subDisplayItems.length; j++) {
+ items.push(subDisplayItems[j]);
+ }
+ }
+ return items;
+ }
+ var elem = createElement("div", {
+ className: "layerObjectDescription",
+ textContent: root.line,
+ style: {
+ whiteSpace: "pre",
+ },
+ onmouseover: function() {
+ if (this.layerViewport) {
+ this.layerViewport.classList.add("layerHover");
+ }
+ },
+ onmouseout: function() {
+ if (this.layerViewport) {
+ this.layerViewport.classList.remove("layerHover");
+ }
+ },
+ });
+ var icon = createElement("img", {
+ src: "show.png",
+ style: {
+ width: "12px",
+ height: "12px",
+ marginLeft: "4px",
+ marginRight: "4px",
+ cursor: "pointer",
+ },
+ onclick: function() {
+ if (this.layerViewport) {
+ if (this.layerViewport.style.visibility == "hidden") {
+ this.layerViewport.style.visibility = "";
+ this.src = "show.png"
+ } else {
+ this.layerViewport.style.visibility = "hidden";
+ this.src = "hide.png"
+ }
+ }
+ }
+ });
+ elem.insertBefore(icon, elem.firstChild);
+ pane.appendChild(elem);
+
+ if (root["shadow-visible"] || root["visible"]) {
+ var visibleRegion = root["shadow-visible"] || root["visible"];
+ var layerViewport = createElement("div", {
+ id: root.address + "_viewport",
+ style: {
+ position: "absolute",
+ pointerEvents: "none",
+ },
+ });
+ elem.layerViewport = layerViewport;
+ icon.layerViewport = layerViewport;
+ var layerViewportMatrix = [1, 0, 0, 1, 0, 0];
+ if (root["shadow-clip"] || root["clip"]) {
+ var clip = root["shadow-clip"] || root["clip"]
+ var clipElem = createElement("div", {
+ id: root.address + "_clip",
+ style: {
+ left: clip[0]+"px",
+ top: clip[1]+"px",
+ width: clip[2]+"px",
+ height: clip[3]+"px",
+ position: "absolute",
+ overflow: "hidden",
+ pointerEvents: "none",
+ },
+ });
+ layerViewportMatrix[4] += -clip[0];
+ layerViewportMatrix[5] += -clip[1];
+ layerViewport.style.transform = "translate(-" + clip[0] + "px, -" + clip[1] + "px" + ")";
+ }
+ if (root["shadow-transform"] || root["transform"]) {
+ var matrix = root["shadow-transform"] || root["transform"];
+ layerViewportMatrix[0] = matrix[0][0];
+ layerViewportMatrix[1] = matrix[0][1];
+ layerViewportMatrix[2] = matrix[1][0];
+ layerViewportMatrix[3] = matrix[1][1];
+ layerViewportMatrix[4] += matrix[2][0];
+ layerViewportMatrix[5] += matrix[2][1];
+ }
+ layerViewport.style.transform = "matrix(" + layerViewportMatrix[0] + "," + layerViewportMatrix[1] + "," + layerViewportMatrix[2] + "," + layerViewportMatrix[3] + "," + layerViewportMatrix[4] + "," + layerViewportMatrix[5] + ")";
+ if (!hasSeenRoot) {
+ hasSeenRoot = true;
+ layerViewport.style.transform = "scale(" + 1/contentScale + "," + 1/contentScale + ")";
+ }
+ if (clipElem) {
+ previewParent.appendChild(clipElem);
+ clipElem.appendChild(layerViewport);
+ } else {
+ previewParent.appendChild(layerViewport);
+ }
+ previewParent = layerViewport;
+ for (var i = 0; i < visibleRegion.length; i++) {
+ var rect2d = visibleRegion[i];
+ var layerPreview = createElement("div", {
+ id: root.address + "_visible_part" + i + "-" + visibleRegion.length,
+ className: "layerPreview",
+ style: {
+ position: "absolute",
+ left: rect2d[0] + "px",
+ top: rect2d[1] + "px",
+ width: rect2d[2] + "px",
+ height: rect2d[3] + "px",
+ overflow: "hidden",
+ border: "solid 1px black",
+ background: 'url("noise.png"), linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2))',
+ },
+ });
+ layerViewport.appendChild(layerPreview);
+
+ function isInside(rect1, rect2) {
+ if (rect1[0] + rect1[2] < rect2[0] && rect2[0] + rect2[2] < rect1[0] &&
+ rect1[1] + rect1[3] < rect2[1] && rect2[1] + rect2[3] < rect1[1]) {
+ return true;
+ }
+ return true;
+ }
+
+ var hasImg = false;
+ // Add tile img objects for this part
+ var previewOffset = rect2d;
+
+ if (root.tiles) {
+ hasImg = true;
+ for (var x in root.tiles) {
+ for (var y in root.tiles[x]) {
+ if (isInside(rect2d, [x, y, 512, 512])) {
+ var tileImgElem = createElement("img", {
+ src: getDataURI(root.tiles[x][y]),
+ style: {
+ position: "absolute",
+ left: (x - previewOffset[0]) + "px",
+ top: (y - previewOffset[1]) + "px",
+ pointerEvents: "auto",
+ },
+ });
+ layerPreview.appendChild(tileImgElem);
+ }
+ }
+ }
+ layerPreview.style.background = "";
+ } else if (root.surfaceURI) {
+ hasImg = true;
+ var offsetX = 0;
+ var offsetY = 0;
+ if (root.contentHostProp && root.contentHostProp['buffer-rect']) {
+ offsetX = root.contentHostProp['buffer-rect'][0];
+ offsetY = root.contentHostProp['buffer-rect'][1];
+ }
+ var surfaceImgElem = createElement("img", {
+ src: getDataURI(root.surfaceURI),
+ style: {
+ position: "absolute",
+ left: (offsetX - previewOffset[0]) + "px",
+ top: (offsetY - previewOffset[1]) + "px",
+ pointerEvents: "auto",
+ },
+ });
+ layerPreview.appendChild(surfaceImgElem);
+ layerPreview.style.background = "";
+ } else if (root.color) {
+ hasImg = true;
+ layerPreview.style.background = "rgba(" + root.color.r + ", " + root.color.g + ", " + root.color.b + ", " + root.color.a + ")";
+ }
+
+ if (hasImg || true) {
+ layerPreview.mouseoverElem = elem;
+ layerPreview.onmouseenter = function() {
+ this.mouseoverElem.onmouseover();
+ }
+ layerPreview.onmouseout = function() {
+ this.mouseoverElem.onmouseout();
+ }
+ }
+ }
+
+ var layerDisplayItems = getDisplayItemForLayer(displayList);
+ for (var i = 0; i < layerDisplayItems.length; i++) {
+ var displayItem = layerDisplayItems[i];
+ var displayElem = createElement("div", {
+ className: "layerObjectDescription",
+ textContent: " " + trim(displayItem.line),
+ style: {
+ whiteSpace: "pre",
+ },
+ displayItem: displayItem,
+ layerViewport: layerViewport,
+ onmouseover: function() {
+ if (this.diPreview) {
+ this.diPreview.classList.add("displayHover");
+
+ var description = "";
+ if (this.displayItem.contentDescriptor) {
+ description += "Content: " + this.displayItem.contentDescriptor;
+ } else {
+ description += "Content: Unknown";
+ }
+ description += "<br>Item: " + this.displayItem.name + " (" + this.displayItem.address + ")";
+ description += "<br>Layer: " + root.name + " (" + root.address + ")";
+ if (this.displayItem.frame) {
+ description += "<br>Frame: " + this.displayItem.frame;
+ }
+ if (this.displayItem.layerBounds) {
+ description += "<br>Bounds: [" + toFixed(this.displayItem.layerBounds[0] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[1] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[2] / 60, 2) + ", " + toFixed(this.displayItem.layerBounds[3] / 60, 2) + "] (CSS Pixels)";
+ }
+ if (this.displayItem.z) {
+ description += "<br>Z: " + this.displayItem.z;
+ }
+ // At the end
+ if (this.displayItem.rest) {
+ description += "<br>" + this.displayItem.rest;
+ }
+
+ var box = this.diPreview.getBoundingClientRect();
+ var pageBox = document.body.getBoundingClientRect();
+ this.diPreview.tooltip = createElement("div", {
+ className: "csstooltip",
+ innerHTML: description,
+ style: {
+ top: Math.min(box.bottom, document.documentElement.clientHeight - 150) + "px",
+ left: box.left + "px",
+ }
+ });
+
+ document.body.appendChild(this.diPreview.tooltip);
+ }
+ },
+ onmouseout: function() {
+ if (this.diPreview) {
+ this.diPreview.classList.remove("displayHover");
+ document.body.removeChild(this.diPreview.tooltip);
+ }
+ },
+ });
+
+ var icon = createElement("img", {
+ style: {
+ width: "12px",
+ height: "12px",
+ marginLeft: "4px",
+ marginRight: "4px",
+ }
+ });
+ displayElem.insertBefore(icon, displayElem.firstChild);
+ pane.appendChild(displayElem);
+ // bounds doesn't adjust for within the layer. It's not a bad fallback but
+ // will have the wrong offset
+ var rect2d = displayItem.layerBounds || displayItem.bounds;
+ if (rect2d) { // This doesn't place them corectly
+ var appUnitsToPixels = 60 / contentScale;
+ diPreview = createElement("div", {
+ id: "displayitem_" + displayItem.content + "_" + displayItem.address,
+ className: "layerPreview",
+ style: {
+ position: "absolute",
+ left: rect2d[0]/appUnitsToPixels + "px",
+ top: rect2d[1]/appUnitsToPixels + "px",
+ width: rect2d[2]/appUnitsToPixels + "px",
+ height: rect2d[3]/appUnitsToPixels + "px",
+ border: "solid 1px gray",
+ pointerEvents: "auto",
+ },
+ displayElem: displayElem,
+ onmouseover: function() {
+ this.displayElem.onmouseover();
+ },
+ onmouseout: function() {
+ this.displayElem.onmouseout();
+ },
+ });
+
+ layerViewport.appendChild(diPreview);
+ displayElem.diPreview = diPreview;
+ }
+ }
+ }
+
+ for (var i = 0; i < root.children.length; i++) {
+ populateLayers(root.children[i], displayList, pane, previewParent, hasSeenRoot, contentScale, rootPreviewParent);
+ }
+}
+
+// This function takes a stdout snippet and finds the frames
+function parseMultiLineDump(log) {
+ var lines = log.split("\n");
+
+ var container = createElement("div", {
+ style: {
+ height: "100%",
+ position: "relative",
+ },
+ });
+
+ var layerManagerFirstLine = "[a-zA-Z]*LayerManager \\(.*$\n";
+ var nextLineStartWithSpace = "([ \\t].*$\n)*";
+ var layersRegex = "(" + layerManagerFirstLine + nextLineStartWithSpace + ")";
+
+ var startLine = "Painting --- after optimization:\n";
+ var endLine = "Painting --- layer tree:"
+ var displayListRegex = "(" + startLine + "(.*\n)*?" + endLine + ")";
+
+ var regex = new RegExp(layersRegex + "|" + displayListRegex, "gm");
+ var matches = log.match(regex);
+ console.log(matches);
+ window.matches = matches;
+
+ var matchList = createElement("span", {
+ style: {
+ height: "95%",
+ width: "10%",
+ position: "relative",
+ border: "solid black 2px",
+ display: "inline-block",
+ float: "left",
+ overflow: "auto",
+ },
+ });
+ container.appendChild(matchList);
+ var contents = createElement("span", {
+ style: {
+ height: "95%",
+ width: "88%",
+ display: "inline-block",
+ },
+ textContent: "Click on a frame on the left to view the layer tree",
+ });
+ container.appendChild(contents);
+
+ var lastDisplayList = null;
+ var frameID = 1;
+ for (let i = 0; i < matches.length; i++) {
+ var currMatch = matches[i];
+
+ if (currMatch.indexOf(startLine) == 0) {
+ // Display list match
+ var matchLines = matches[i].split("\n")
+ lastDisplayList = parseDisplayList(matchLines);
+ } else {
+ // Layer tree match:
+ let displayList = lastDisplayList;
+ lastDisplayList = null;
+ var currFrameDiv = createElement("a", {
+ style: {
+ padding: "3px",
+ display: "block",
+ },
+ href: "#",
+ textContent: "LayerTree " + (frameID++),
+ onclick: function() {
+ contents.innerHTML = "";
+ var matchLines = matches[i].split("\n")
+ var dumpDiv = parseDump(matchLines, displayList);
+ contents.appendChild(dumpDiv);
+ }
+ });
+ matchList.appendChild(currFrameDiv);
+ }
+ }
+
+ return container;
+}
+
+function parseDump(log, displayList, compositeTitle, compositeTime) {
+ compositeTitle |= "";
+ compositeTime |= 0
+
+ var container = createElement("div", {
+ style: {
+ background: "white",
+ height: "100%",
+ position: "relative",
+ },
+ });
+
+ if (compositeTitle == null && compositeTime == null) {
+ var titleDiv = createElement("div", {
+ className: "treeColumnHeader",
+ style: {
+ width: "100%",
+ },
+ textContent: compositeTitle + (compositeTitle ? " (near " + compositeTime.toFixed(0) + " ms)" : ""),
+ });
+ container.appendChild(titleDiv);
+ }
+
+ var mainDiv = createElement("div", {
+ style: {
+ position: "absolute",
+ top: "16px",
+ left: "0px",
+ right: "0px",
+ bottom: "0px",
+ },
+ });
+ container.appendChild(mainDiv);
+
+ var layerListPane = createElement("div", {
+ style: {
+ cssFloat: "left",
+ height: "100%",
+ width: "300px",
+ overflowY: "scroll",
+ },
+ });
+ mainDiv.appendChild(layerListPane);
+
+ var previewDiv = createElement("div", {
+ style: {
+ position: "absolute",
+ left: "300px",
+ right: "0px",
+ top: "0px",
+ bottom: "0px",
+ overflow: "auto",
+ },
+ });
+ mainDiv.appendChild(previewDiv);
+
+ var root = parseLayers(log);
+ populateLayers(root, displayList, layerListPane, previewDiv);
+ return container;
+}
+
+function tab_showLayersDump(layersDumpLines, compositeTitle, compositeTime) {
+ var container = parseDump(layersDumpLines, compositeTitle, compositeTime);
+
+ gTabWidget.addTab("LayerTree", container);
+ gTabWidget.selectTab("LayerTree");
+}
+
diff --git a/gfx/layers/layerviewer/noise.png b/gfx/layers/layerviewer/noise.png
new file mode 100755
index 000000000..01d340aaa
--- /dev/null
+++ b/gfx/layers/layerviewer/noise.png
Binary files differ
diff --git a/gfx/layers/layerviewer/show.png b/gfx/layers/layerviewer/show.png
new file mode 100644
index 000000000..7038b660c
--- /dev/null
+++ b/gfx/layers/layerviewer/show.png
Binary files differ
diff --git a/gfx/layers/layerviewer/tree.css b/gfx/layers/layerviewer/tree.css
new file mode 100644
index 000000000..6b26d729b
--- /dev/null
+++ b/gfx/layers/layerviewer/tree.css
@@ -0,0 +1,36 @@
+html, body {
+ height: 100%;
+ overflow: hidden;
+}
+.layerObjectDescription:hover {
+ background-color: #E8E8E8;
+}
+
+.layerHover > .layerPreview::after {
+ position: absolute;
+ top: 0; right: 0; bottom: 0; left: 0;
+ background-color: inherit;
+ content: "";
+ background-color: rgba(0,0,0,0.2);
+ box-shadow: -2px 2px 0 #FFF;
+}
+
+@keyframes layerHoverAnimation {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.2); }
+ 100% { transform: scale(1); }
+}
+
+.displayHover {
+ background: rgba(0, 128, 0, 0.8);
+}
+
+.layerHover > .layerPreview {
+ animation: layerHoverAnimation 200ms;
+ animation-transform-origin: 50% 50%;
+ background: gold !important;
+ box-shadow: 10px 10px 5px #888888;
+ border-color: blue !important;
+ z-index: 10;
+}
+
diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build
new file mode 100644
index 000000000..2a2fa1169
--- /dev/null
+++ b/gfx/layers/moz.build
@@ -0,0 +1,451 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('apz/**'):
+ BUG_COMPONENT = ('Core', 'Panning and Zooming')
+
+EXPORTS += [
+ 'basic/BasicCanvasLayer.h',
+ 'basic/BasicImplData.h',
+ 'basic/BasicLayers.h',
+ 'basic/BasicLayersImpl.h',
+ 'basic/BasicPaintedLayer.h',
+ 'client/ClientCanvasLayer.h',
+ 'client/ClientContainerLayer.h',
+ 'client/ClientLayerManager.h',
+ 'client/ClientPaintedLayer.h',
+ 'client/ClientTiledPaintedLayer.h',
+ 'composite/CompositableHost.h',
+ 'composite/ImageHost.h',
+ 'CompositorTypes.h',
+ 'CopyableCanvasLayer.h',
+ 'D3D9SurfaceImage.h',
+ 'FrameMetrics.h',
+ 'GLImages.h',
+ 'GPUVideoImage.h',
+ 'ImageContainer.h',
+ 'ImageLayers.h',
+ 'ImageTypes.h',
+ 'IMFYCbCrImage.h',
+ 'ipc/ThreadSafeRefcountingWithMainThreadDestruction.h',
+ 'Layers.h',
+ 'LayerScope.h',
+ 'LayersLogging.h',
+ 'LayerSorter.h',
+ 'LayersTypes.h',
+ 'LayerTreeInvalidation.h',
+ 'LayerUserData.h',
+ 'opengl/Composer2D.h',
+ 'opengl/OGLShaderProgram.h',
+ 'opengl/TexturePoolOGL.h',
+ 'protobuf/LayerScopePacket.pb.h',
+ 'ReadbackLayer.h',
+ 'TiledLayerBuffer.h',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'D3D11ShareHandleImage.cpp',
+ ]
+ UNIFIED_SOURCES += [
+ 'D3D9SurfaceImage.cpp',
+ 'IMFYCbCrImage.cpp',
+ 'TextureDIB.cpp',
+ ]
+ EXPORTS.mozilla.layers += [
+ 'TextureDIB.h',
+ ]
+ if CONFIG['MOZ_ENABLE_D3D9_LAYER']:
+ EXPORTS += [
+ 'd3d9/DeviceManagerD3D9.h',
+ ]
+ EXPORTS.mozilla.layers += [
+ 'd3d9/CompositorD3D9.h',
+ 'd3d9/TextureD3D9.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'd3d9/TextureD3D9.cpp',
+ ]
+ SOURCES += [
+ 'd3d9/CompositorD3D9.cpp',
+ 'd3d9/DeviceManagerD3D9.cpp',
+ 'd3d9/Nv3DVUtils.cpp',
+ ]
+ if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
+ EXPORTS.mozilla.layers += [
+ 'd3d11/CompositorD3D11.h',
+ 'd3d11/ReadbackManagerD3D11.h',
+ 'd3d11/TextureD3D11.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'd3d11/TextureD3D11.cpp',
+ ]
+ SOURCES += [
+ 'd3d11/CompositorD3D11.cpp',
+ 'd3d11/ReadbackManagerD3D11.cpp',
+ ]
+
+EXPORTS.gfxipc += [
+ 'ipc/ShadowLayerUtils.h',
+]
+
+EXPORTS.mozilla.dom += [
+ 'apz/util/CheckerboardReportService.h',
+]
+
+EXPORTS.mozilla.layers += [
+ 'apz/public/CompositorController.h',
+ 'apz/public/GeckoContentController.h',
+ 'apz/public/IAPZCTreeManager.h',
+ 'apz/public/MetricsSharingController.h',
+ # exporting things from apz/src is temporary until we extract a
+ # proper interface for the code there
+ 'apz/src/APZCTreeManager.h',
+ 'apz/src/APZUtils.h',
+ 'apz/src/AsyncDragMetrics.h',
+ 'apz/src/AsyncPanZoomAnimation.h',
+ 'apz/src/TouchCounter.h',
+ 'apz/testutil/APZTestData.h',
+ 'apz/util/ActiveElementManager.h',
+ 'apz/util/APZCCallbackHelper.h',
+ 'apz/util/APZEventState.h',
+ 'apz/util/APZThreadUtils.h',
+ 'apz/util/ChromeProcessController.h',
+ 'apz/util/ContentProcessController.h',
+ 'apz/util/DoubleTapToZoom.h',
+ 'apz/util/InputAPZContext.h',
+ 'apz/util/ScrollInputMethods.h',
+ 'apz/util/ScrollLinkedEffectDetector.h',
+ 'apz/util/TouchActionHelper.h',
+ 'AsyncCanvasRenderer.h',
+ 'AtomicRefCountedWithFinalize.h',
+ 'AxisPhysicsModel.h',
+ 'AxisPhysicsMSDModel.h',
+ 'basic/BasicCompositor.h',
+ 'basic/MacIOSurfaceTextureHostBasic.h',
+ 'basic/TextureHostBasic.h',
+ 'BSPTree.h',
+ 'BufferTexture.h',
+ 'client/CanvasClient.h',
+ 'client/CompositableChild.h',
+ 'client/CompositableClient.h',
+ 'client/ContentClient.h',
+ 'client/GPUVideoTextureClient.h',
+ 'client/ImageClient.h',
+ 'client/SingleTiledContentClient.h',
+ 'client/TextureClient.h',
+ 'client/TextureClientPool.h',
+ 'client/TextureClientRecycleAllocator.h',
+ 'client/TextureClientSharedSurface.h',
+ 'client/TiledContentClient.h',
+ 'composite/AsyncCompositionManager.h',
+ 'composite/CanvasLayerComposite.h',
+ 'composite/ColorLayerComposite.h',
+ 'composite/ContainerLayerComposite.h',
+ 'composite/ContentHost.h',
+ 'composite/FrameUniformityData.h',
+ 'composite/GPUVideoTextureHost.h',
+ 'composite/ImageHost.h',
+ 'composite/ImageLayerComposite.h',
+ 'composite/LayerManagerComposite.h',
+ 'composite/PaintedLayerComposite.h',
+ 'composite/TextureHost.h',
+ 'composite/TiledContentHost.h',
+ 'Compositor.h',
+ 'CompositorTypes.h',
+ 'D3D11ShareHandleImage.h',
+ 'D3D9SurfaceImage.h',
+ 'Effects.h',
+ 'ImageDataSerializer.h',
+ 'ipc/APZChild.h',
+ 'ipc/APZCTreeManagerChild.h',
+ 'ipc/APZCTreeManagerParent.h',
+ 'ipc/CompositableForwarder.h',
+ 'ipc/CompositableTransactionParent.h',
+ 'ipc/CompositorBridgeChild.h',
+ 'ipc/CompositorBridgeParent.h',
+ 'ipc/CompositorThread.h',
+ 'ipc/CrossProcessCompositorBridgeParent.h',
+ 'ipc/GonkNativeHandle.h',
+ 'ipc/GonkNativeHandleUtils.h',
+ 'ipc/ImageBridgeChild.h',
+ 'ipc/ImageBridgeParent.h',
+ 'ipc/ImageContainerChild.h',
+ 'ipc/ImageContainerParent.h',
+ 'ipc/ISurfaceAllocator.h',
+ 'ipc/KnowsCompositor.h',
+ 'ipc/LayerAnimationUtils.h',
+ 'ipc/LayerTransactionChild.h',
+ 'ipc/LayerTransactionParent.h',
+ 'ipc/LayerTreeOwnerTracker.h',
+ 'ipc/RemoteContentController.h',
+ 'ipc/ShadowLayerChild.h',
+ 'ipc/ShadowLayers.h',
+ 'ipc/SharedPlanarYCbCrImage.h',
+ 'ipc/SharedRGBImage.h',
+ 'ipc/SynchronousTask.h',
+ 'ipc/TextureForwarder.h',
+ 'ipc/VideoBridgeChild.h',
+ 'ipc/VideoBridgeParent.h',
+ 'LayerMetricsWrapper.h',
+ 'LayersTypes.h',
+ 'opengl/CompositingRenderTargetOGL.h',
+ 'opengl/CompositorOGL.h',
+ 'opengl/MacIOSurfaceTextureClientOGL.h',
+ 'opengl/MacIOSurfaceTextureHostOGL.h',
+ 'opengl/TextureClientOGL.h',
+ 'opengl/TextureHostOGL.h',
+ 'PersistentBufferProvider.h',
+ 'RenderTrace.h',
+ 'TextureWrapperImage.h',
+ 'TransactionIdAllocator.h',
+]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS.mozilla.layers += [
+ 'basic/TextureClientX11.h',
+ 'basic/X11TextureSourceBasic.h',
+ 'composite/X11TextureHost.h',
+ 'ipc/ShadowLayerUtilsX11.h',
+ 'opengl/X11TextureSourceOGL.h',
+ ]
+ SOURCES += [
+ 'basic/TextureClientX11.cpp',
+ 'basic/X11BasicCompositor.cpp',
+ 'basic/X11TextureSourceBasic.cpp',
+ 'composite/X11TextureHost.cpp',
+ 'ipc/ShadowLayerUtilsX11.cpp',
+ 'opengl/X11TextureSourceOGL.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS.mozilla.layers += [
+ 'opengl/GLManager.h',
+ ]
+ EXPORTS += [
+ 'MacIOSurfaceHelpers.h',
+ 'MacIOSurfaceImage.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'opengl/GLManager.cpp',
+ ]
+ SOURCES += [
+ 'ipc/ShadowLayerUtilsMac.cpp',
+ 'MacIOSurfaceHelpers.cpp',
+ 'MacIOSurfaceImage.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ UNIFIED_SOURCES += [
+ 'apz/src/AndroidAPZ.cpp',
+ ]
+
+UNIFIED_SOURCES += [
+ 'apz/public/IAPZCTreeManager.cpp',
+ 'apz/src/APZCTreeManager.cpp',
+ 'apz/src/AsyncPanZoomController.cpp',
+ 'apz/src/Axis.cpp',
+ 'apz/src/CheckerboardEvent.cpp',
+ 'apz/src/DragTracker.cpp',
+ 'apz/src/GestureEventListener.cpp',
+ 'apz/src/HitTestingTreeNode.cpp',
+ 'apz/src/InputBlockState.cpp',
+ 'apz/src/InputQueue.cpp',
+ 'apz/src/OverscrollHandoffState.cpp',
+ 'apz/src/PotentialCheckerboardDurationTracker.cpp',
+ 'apz/src/QueuedInput.cpp',
+ 'apz/src/TouchCounter.cpp',
+ 'apz/src/WheelScrollAnimation.cpp',
+ 'apz/testutil/APZTestData.cpp',
+ 'apz/util/ActiveElementManager.cpp',
+ 'apz/util/APZCCallbackHelper.cpp',
+ 'apz/util/APZEventState.cpp',
+ 'apz/util/APZThreadUtils.cpp',
+ 'apz/util/CheckerboardReportService.cpp',
+ 'apz/util/ChromeProcessController.cpp',
+ 'apz/util/ContentProcessController.cpp',
+ 'apz/util/DoubleTapToZoom.cpp',
+ 'apz/util/InputAPZContext.cpp',
+ 'apz/util/ScrollLinkedEffectDetector.cpp',
+ 'apz/util/TouchActionHelper.cpp',
+ 'AsyncCanvasRenderer.cpp',
+ 'AxisPhysicsModel.cpp',
+ 'AxisPhysicsMSDModel.cpp',
+ 'basic/BasicCanvasLayer.cpp',
+ 'basic/BasicColorLayer.cpp',
+ 'basic/BasicCompositor.cpp',
+ 'basic/BasicContainerLayer.cpp',
+ 'basic/BasicImages.cpp',
+ 'basic/BasicLayerManager.cpp',
+ 'basic/BasicLayersImpl.cpp',
+ 'basic/BasicPaintedLayer.cpp',
+ 'basic/TextureHostBasic.cpp',
+ 'BSPTree.cpp',
+ 'BufferTexture.cpp',
+ 'BufferUnrotate.cpp',
+ 'client/CanvasClient.cpp',
+ 'client/ClientCanvasLayer.cpp',
+ 'client/ClientColorLayer.cpp',
+ 'client/ClientContainerLayer.cpp',
+ 'client/ClientImageLayer.cpp',
+ 'client/ClientLayerManager.cpp',
+ 'client/ClientPaintedLayer.cpp',
+ 'client/ClientTiledPaintedLayer.cpp',
+ 'client/CompositableChild.cpp',
+ 'client/CompositableClient.cpp',
+ 'client/ContentClient.cpp',
+ 'client/GPUVideoTextureClient.cpp',
+ 'client/ImageClient.cpp',
+ 'client/SingleTiledContentClient.cpp',
+ 'client/TextureClient.cpp',
+ 'client/TextureClientPool.cpp',
+ 'client/TextureClientRecycleAllocator.cpp',
+ 'client/TextureClientSharedSurface.cpp',
+ 'client/TiledContentClient.cpp',
+ 'composite/AsyncCompositionManager.cpp',
+ 'composite/CanvasLayerComposite.cpp',
+ 'composite/ColorLayerComposite.cpp',
+ 'composite/CompositableHost.cpp',
+ 'composite/ContainerLayerComposite.cpp',
+ 'composite/ContentHost.cpp',
+ 'composite/FPSCounter.cpp',
+ 'composite/FrameUniformityData.cpp',
+ 'composite/GPUVideoTextureHost.cpp',
+ 'composite/ImageHost.cpp',
+ 'composite/ImageLayerComposite.cpp',
+ 'composite/LayerManagerComposite.cpp',
+ 'composite/PaintedLayerComposite.cpp',
+ 'composite/TextRenderer.cpp',
+ 'composite/TextureHost.cpp',
+ 'composite/TiledContentHost.cpp',
+ 'Compositor.cpp',
+ 'CopyableCanvasLayer.cpp',
+ 'Effects.cpp',
+ 'FrameMetrics.cpp',
+ 'GLImages.cpp',
+ 'ImageDataSerializer.cpp',
+ 'ImageLayers.cpp',
+ 'ipc/APZChild.cpp',
+ 'ipc/APZCTreeManagerChild.cpp',
+ 'ipc/APZCTreeManagerParent.cpp',
+ 'ipc/CompositableForwarder.cpp',
+ 'ipc/CompositableTransactionParent.cpp',
+ 'ipc/CompositorBench.cpp',
+ 'ipc/CompositorBridgeChild.cpp',
+ 'ipc/CompositorBridgeParent.cpp',
+ 'ipc/CompositorThread.cpp',
+ 'ipc/CrossProcessCompositorBridgeParent.cpp',
+ 'ipc/ImageBridgeChild.cpp',
+ 'ipc/ImageBridgeParent.cpp',
+ 'ipc/ImageContainerChild.cpp',
+ 'ipc/ImageContainerParent.cpp',
+ 'ipc/ISurfaceAllocator.cpp',
+ 'ipc/LayerAnimationUtils.cpp',
+ 'ipc/LayerTransactionChild.cpp',
+ 'ipc/LayerTransactionParent.cpp',
+ 'ipc/LayerTreeOwnerTracker.cpp',
+ 'ipc/RemoteContentController.cpp',
+ 'ipc/ShadowLayerChild.cpp',
+ 'ipc/ShadowLayerParent.cpp',
+ 'ipc/ShadowLayers.cpp',
+ 'ipc/SharedPlanarYCbCrImage.cpp',
+ 'ipc/SharedRGBImage.cpp',
+ 'ipc/VideoBridgeChild.cpp',
+ 'ipc/VideoBridgeParent.cpp',
+ 'LayerScope.cpp',
+ 'LayersLogging.cpp',
+ 'LayerSorter.cpp',
+ 'LayersTypes.cpp',
+ 'opengl/CompositingRenderTargetOGL.cpp',
+ 'opengl/CompositorOGL.cpp',
+ 'opengl/GLBlitTextureImageHelper.cpp',
+ 'opengl/OGLShaderProgram.cpp',
+ 'opengl/TextureClientOGL.cpp',
+ 'opengl/TextureHostOGL.cpp',
+ 'opengl/TexturePoolOGL.cpp',
+ 'protobuf/LayerScopePacket.pb.cc',
+ 'ReadbackProcessor.cpp',
+ 'RenderTrace.cpp',
+ 'RotatedBuffer.cpp',
+ 'TextureWrapperImage.cpp',
+]
+
+SOURCES += [
+ 'basic/BasicImageLayer.cpp',
+ 'ImageContainer.cpp',
+ 'Layers.cpp',
+ 'LayerTreeInvalidation.cpp',
+ 'PersistentBufferProvider.cpp',
+]
+
+# Disable RTTI in google protocol buffer
+DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
+
+# Workaround compiler bug (Bug 795594)
+if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64':
+ for src in [
+ 'Layers.cpp',
+ 'LayerTreeInvalidation.cpp',
+ ]:
+ SOURCES[src].no_pgo = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'basic/MacIOSurfaceTextureHostBasic.cpp',
+ 'opengl/MacIOSurfaceTextureClientOGL.cpp',
+ 'opengl/MacIOSurfaceTextureHostOGL.cpp',
+ ]
+
+IPDL_SOURCES = [
+ 'ipc/LayersMessages.ipdlh',
+ 'ipc/LayersSurfaces.ipdlh',
+ 'ipc/PAPZ.ipdl',
+ 'ipc/PAPZCTreeManager.ipdl',
+ 'ipc/PCompositable.ipdl',
+ 'ipc/PCompositorBridge.ipdl',
+ 'ipc/PImageBridge.ipdl',
+ 'ipc/PImageContainer.ipdl',
+ 'ipc/PLayer.ipdl',
+ 'ipc/PLayerTransaction.ipdl',
+ 'ipc/PTexture.ipdl',
+ 'ipc/PVideoBridge.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/docshell/base', # for nsDocShell.h
+ '/layout/base', # for TouchManager.h
+ '/layout/generic', # for nsTextFrame.h
+ '/media/libyuv/include', # for libyuv.h
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_DEBUG']:
+ DEFINES['D3D_DEBUG_INFO'] = True
+
+if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
+ DEFINES['MOZ_ENABLE_D3D10_LAYER'] = True
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['apz/test/gtest']
+
+MOCHITEST_MANIFESTS += ['apz/test/mochitest/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['apz/test/mochitest/chrome.ini']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['MOZ_ENABLE_SKIA']:
+ UNIFIED_SOURCES += [
+ 'composite/PaintCounter.cpp',
+ ]
diff --git a/gfx/layers/opengl/Composer2D.h b/gfx/layers/opengl/Composer2D.h
new file mode 100644
index 000000000..a7cdcea36
--- /dev/null
+++ b/gfx/layers/opengl/Composer2D.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_Composer2D_h
+#define mozilla_layers_Composer2D_h
+
+#include "gfxTypes.h"
+#include "nsISupportsImpl.h"
+
+/**
+ * Many platforms have dedicated hardware for simple composition.
+ * This hardware is usually faster or more power efficient than the
+ * GPU. However, in exchange for this better performance, generality
+ * has to be sacrificed: no 3d transforms, no intermediate surfaces,
+ * no special shader effects, loss of other goodies depending on the
+ * platform.
+ *
+ * Composer2D is a very simple interface to this class of hardware
+ * that allows an implementation to "try rendering" with the fast
+ * path. If the given layer tree requires more generality than the
+ * hardware provides, the implementation should bail and have the
+ * layer manager fall back on full GPU composition.
+ */
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+class Composer2D {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Composer2D)
+
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~Composer2D() {}
+
+public:
+ /**
+ * Return true if |aRoot| met the implementation's criteria for fast
+ * composition and the render was successful. Return false to fall
+ * back on the GPU.
+ *
+ * Currently, when TryRender() returns true, the entire framebuffer
+ * must have been rendered.
+ */
+ virtual bool TryRenderWithHwc(Layer* aRoot,
+ nsIWidget* aWidget,
+ bool aGeometryChanged,
+ bool aHasImageHostOverlays) = 0;
+
+ /**
+ * Return true if Composer2D does composition. Return false if Composer2D
+ * failed the composition.
+ */
+ virtual bool Render(nsIWidget* aWidget) = 0;
+
+ /**
+ * Return true if Composer2D has a fast composition hardware.
+ * Return false if Composer2D does not have a fast composition hardware.
+ */
+ virtual bool HasHwc() = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_Composer2D_h
diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
new file mode 100644
index 000000000..c05b8edfd
--- /dev/null
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositingRenderTargetOGL.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+CompositingRenderTargetOGL::~CompositingRenderTargetOGL()
+{
+ if (mGL && mGL->MakeCurrent()) {
+ mGL->fDeleteTextures(1, &mTextureHandle);
+ mGL->fDeleteFramebuffers(1, &mFBO);
+ }
+}
+
+void
+CompositingRenderTargetOGL::BindTexture(GLenum aTextureUnit, GLenum aTextureTarget)
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ MOZ_ASSERT(mTextureHandle != 0);
+ mGL->fActiveTexture(aTextureUnit);
+ mGL->fBindTexture(aTextureTarget, mTextureHandle);
+}
+
+void
+CompositingRenderTargetOGL::BindRenderTarget()
+{
+ bool needsClear = false;
+
+ if (mInitParams.mStatus != InitParams::INITIALIZED) {
+ InitializeImpl();
+ if (mInitParams.mInit == INIT_MODE_CLEAR) {
+ needsClear = true;
+ mClearOnBind = false;
+ }
+ } else {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ // The main framebuffer (0) of non-offscreen contexts
+ // might be backed by a EGLSurface that needs to be renewed.
+ if (mFBO == 0 && !mGL->IsOffscreen()) {
+ mGL->RenewSurface(mCompositor->GetWidget()->RealWidget());
+ result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ }
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Framebuffer not complete -- CheckFramebufferStatus returned 0x%x, "
+ "GLContext=%p, IsOffscreen()=%d, mFBO=%d, aFBOTextureTarget=0x%x, "
+ "aRect.width=%d, aRect.height=%d",
+ result, mGL, mGL->IsOffscreen(), mFBO, mInitParams.mFBOTextureTarget,
+ mInitParams.mSize.width, mInitParams.mSize.height);
+ NS_WARNING(msg.get());
+ }
+ }
+
+ needsClear = mClearOnBind;
+ }
+
+ if (needsClear) {
+ ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGL, 0, 0, mInitParams.mSize.width,
+ mInitParams.mSize.height);
+ mGL->fClearColor(0.0, 0.0, 0.0, 0.0);
+ mGL->fClearDepth(0.0);
+ mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+#ifdef MOZ_DUMP_PAINTING
+already_AddRefed<DataSourceSurface>
+CompositingRenderTargetOGL::Dump(Compositor* aCompositor)
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL();
+ return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat());
+}
+#endif
+
+void
+CompositingRenderTargetOGL::InitializeImpl()
+{
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
+
+ //TODO: call mGL->GetBackbufferFB(), use that
+ GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ mInitParams.mFBOTextureTarget,
+ mTextureHandle,
+ 0);
+
+ // Making this call to fCheckFramebufferStatus prevents a crash on
+ // PowerVR. See bug 695246.
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Framebuffer not complete -- error 0x%x, aFBOTextureTarget 0x%x, mFBO %d, mTextureHandle %d, aRect.width %d, aRect.height %d",
+ result, mInitParams.mFBOTextureTarget, mFBO, mTextureHandle, mInitParams.mSize.width, mInitParams.mSize.height);
+ NS_ERROR(msg.get());
+ }
+
+ mInitParams.mStatus = InitParams::INITIALIZED;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.h b/gfx/layers/opengl/CompositingRenderTargetOGL.h
new file mode 100644
index 000000000..501701d6f
--- /dev/null
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+#define MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_FRAMEBUFFER, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize, IntSizeTyped
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_WARNING
+#include "nsString.h" // for nsAutoCString
+
+
+namespace mozilla {
+namespace gl {
+ class BindableTexture;
+} // namespace gl
+namespace gfx {
+ class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class TextureSource;
+
+class CompositingRenderTargetOGL : public CompositingRenderTarget
+{
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class CompositorOGL;
+
+ // For lazy initialisation of the GL stuff
+ struct InitParams
+ {
+ InitParams() : mStatus(NO_PARAMS) {}
+ InitParams(const gfx::IntSize& aSize,
+ const gfx::IntSize& aPhySize,
+ GLenum aFBOTextureTarget,
+ SurfaceInitMode aInit)
+ : mStatus(READY)
+ , mSize(aSize)
+ , mPhySize(aPhySize)
+ , mFBOTextureTarget(aFBOTextureTarget)
+ , mInit(aInit)
+ {}
+
+ enum {
+ NO_PARAMS,
+ READY,
+ INITIALIZED
+ } mStatus;
+ /*
+ * Users of render target would draw in logical size, but it is
+ * actually drawn to a surface in physical size. GL surfaces have
+ * a limitation on their size, a smaller surface would be
+ * allocated for the render target if the caller requests in a
+ * size too big.
+ */
+ gfx::IntSize mSize; // Logical size, the expected by callers.
+ gfx::IntSize mPhySize; // Physical size, the real size of the surface.
+ GLenum mFBOTextureTarget;
+ SurfaceInitMode mInit;
+ };
+
+public:
+ CompositingRenderTargetOGL(CompositorOGL* aCompositor, const gfx::IntPoint& aOrigin,
+ GLuint aTexure, GLuint aFBO)
+ : CompositingRenderTarget(aOrigin)
+ , mInitParams()
+ , mCompositor(aCompositor)
+ , mGL(aCompositor->gl())
+ , mTextureHandle(aTexure)
+ , mFBO(aFBO)
+ {
+ MOZ_ASSERT(mGL);
+ }
+
+ ~CompositingRenderTargetOGL();
+
+ virtual const char* Name() const override { return "CompositingRenderTargetOGL"; }
+
+ /**
+ * Create a render target around the default FBO, for rendering straight to
+ * the window.
+ */
+ static already_AddRefed<CompositingRenderTargetOGL>
+ RenderTargetForWindow(CompositorOGL* aCompositor,
+ const gfx::IntSize& aSize)
+ {
+ RefPtr<CompositingRenderTargetOGL> result
+ = new CompositingRenderTargetOGL(aCompositor, gfx::IntPoint(), 0, 0);
+ result->mInitParams = InitParams(aSize, aSize, 0, INIT_MODE_NONE);
+ result->mInitParams.mStatus = InitParams::INITIALIZED;
+ return result.forget();
+ }
+
+ /**
+ * Some initialisation work on the backing FBO and texture.
+ * We do this lazily so that when we first set this render target on the
+ * compositor we do not have to re-bind the FBO after unbinding it, or
+ * alternatively leave the FBO bound after creation.
+ */
+ void Initialize(const gfx::IntSize& aSize,
+ const gfx::IntSize& aPhySize,
+ GLenum aFBOTextureTarget,
+ SurfaceInitMode aInit)
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::NO_PARAMS, "Initialized twice?");
+ // postpone initialization until we actually want to use this render target
+ mInitParams = InitParams(aSize, aPhySize, aFBOTextureTarget, aInit);
+ }
+
+ void BindTexture(GLenum aTextureUnit, GLenum aTextureTarget);
+
+ /**
+ * Call when we want to draw into our FBO
+ */
+ void BindRenderTarget();
+
+ bool IsWindow() { return GetFBO() == 0; }
+
+ GLuint GetFBO() const
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ return mFBO;
+ }
+
+ GLuint GetTextureHandle() const
+ {
+ MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
+ return mTextureHandle;
+ }
+
+ // TextureSourceOGL
+ TextureSourceOGL* AsSourceOGL() override
+ {
+ // XXX - Bug 900770
+ MOZ_ASSERT(false, "CompositingRenderTargetOGL should not be used as a TextureSource");
+ return nullptr;
+ }
+ gfx::IntSize GetSize() const override
+ {
+ return mInitParams.mSize;
+ }
+
+ gfx::SurfaceFormat GetFormat() const override
+ {
+ // XXX - Should it be implemented ? is the above assert true ?
+ MOZ_ASSERT(false, "Not implemented");
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) override;
+#endif
+
+ const gfx::IntSize& GetInitSize() const {
+ return mInitParams.mSize;
+ }
+
+private:
+ /**
+ * Actually do the initialisation. Note that we leave our FBO bound, and so
+ * calling this method is only suitable when about to use this render target.
+ */
+ void InitializeImpl();
+
+ InitParams mInitParams;
+ /**
+ * There is temporary a cycle between the compositor and the render target,
+ * each having a strong ref to the other. The compositor's reference to
+ * the target is always cleared at the end of a frame.
+ */
+ RefPtr<CompositorOGL> mCompositor;
+ GLContext* mGL;
+ GLuint mTextureHandle;
+ GLuint mFBO;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SURFACEOGL_H */
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
new file mode 100644
index 000000000..bbe1b4657
--- /dev/null
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -0,0 +1,1916 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorOGL.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t
+#include <stdlib.h> // for free, malloc
+#include "GLContextProvider.h" // for GLContextProvider
+#include "GLContext.h" // for GLContext
+#include "GLUploadHelpers.h"
+#include "Layers.h" // for WriteSnapshotToDumpFile
+#include "LayerScope.h" // for LayerScope
+#include "gfxCrashReporterUtils.h" // for ScopedGfxFeatureReporter
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils, etc
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/gfxVars.h" // for gfxVars
+#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
+#include "mozilla/layers/CompositingRenderTargetOGL.h"
+#include "mozilla/layers/Effects.h" // for EffectChain, TexturedEffect, etc
+#include "mozilla/layers/TextureHost.h" // for TextureSource, etc
+#include "mozilla/layers/TextureHostOGL.h" // for TextureSourceOGL, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAppRunner.h"
+#include "nsAString.h"
+#include "nsIConsoleService.h" // for nsIConsoleService, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLiteralString.h" // for NS_LITERAL_STRING
+#include "nsMathUtils.h" // for NS_roundf
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsServiceManagerUtils.h" // for do_GetService
+#include "nsString.h" // for nsString, nsAutoCString, etc
+#include "ScopedGLHelpers.h"
+#include "GLReadTexImageHelper.h"
+#include "GLBlitTextureImageHelper.h"
+#include "HeapCopyOfStackArray.h"
+
+#if MOZ_WIDGET_ANDROID
+#include "TexturePoolOGL.h"
+#endif
+
+#include "GeckoProfiler.h"
+
+namespace mozilla {
+
+using namespace std;
+using namespace gfx;
+
+namespace layers {
+
+using namespace mozilla::gl;
+
+static const GLuint kCoordinateAttributeIndex = 0;
+static const GLuint kTexCoordinateAttributeIndex = 1;
+
+static void
+BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
+ GLenum aTexUnit, const gfx::Matrix4x4& aTransform)
+{
+ MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31);
+ aSourceMask->BindTexture(aTexUnit, gfx::SamplingFilter::LINEAR);
+ aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
+ aProgram->SetMaskLayerTransform(aTransform);
+}
+
+void
+CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit)
+{
+ MOZ_ASSERT(aBackdrop);
+
+ mGLContext->fActiveTexture(aTexUnit);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
+ mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+ aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
+}
+
+CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget,
+ int aSurfaceWidth, int aSurfaceHeight,
+ bool aUseExternalSurfaceSize)
+ : Compositor(aWidget, aParent)
+ , mWidgetSize(-1, -1)
+ , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
+ , mHasBGRA(0)
+ , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
+ , mFrameInProgress(false)
+ , mDestroyed(false)
+ , mViewportSize(0, 0)
+ , mCurrentProgram(nullptr)
+{
+ MOZ_COUNT_CTOR(CompositorOGL);
+}
+
+CompositorOGL::~CompositorOGL()
+{
+ MOZ_COUNT_DTOR(CompositorOGL);
+ Destroy();
+}
+
+already_AddRefed<mozilla::gl::GLContext>
+CompositorOGL::CreateContext()
+{
+ RefPtr<GLContext> context;
+
+ // Used by mock widget to create an offscreen context
+ nsIWidget* widget = mWidget->RealWidget();
+ void* widgetOpenGLContext = widget ? widget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT) : nullptr;
+ if (widgetOpenGLContext) {
+ GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
+ return already_AddRefed<GLContext>(alreadyRefed);
+ }
+
+#ifdef XP_WIN
+ if (gfxEnv::LayersPreferEGL()) {
+ printf_stderr("Trying GL layers...\n");
+ context = gl::GLContextProviderEGL::CreateForCompositorWidget(mWidget, false);
+ }
+#endif
+
+ // Allow to create offscreen GL context for main Layer Manager
+ if (!context && gfxEnv::LayersPreferOffscreen()) {
+ SurfaceCaps caps = SurfaceCaps::ForRGB();
+ caps.preserve = false;
+ caps.bpp16 = gfxVars::OffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
+
+ nsCString discardFailureId;
+ context = GLContextProvider::CreateOffscreen(mSurfaceSize,
+ caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+ &discardFailureId);
+ }
+
+ if (!context) {
+ context = gl::GLContextProvider::CreateForCompositorWidget(mWidget,
+ gfxVars::RequiresAcceleratedGLContextForCompositorOGL());
+ }
+
+ if (!context) {
+ NS_WARNING("Failed to create CompositorOGL context");
+ }
+
+ return context.forget();
+}
+
+void
+CompositorOGL::Destroy()
+{
+ Compositor::Destroy();
+
+ if (mTexturePool) {
+ mTexturePool->Clear();
+ mTexturePool = nullptr;
+ }
+
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void
+CompositorOGL::CleanupResources()
+{
+ if (!mGLContext)
+ return;
+
+ RefPtr<GLContext> ctx = mGLContext->GetSharedContext();
+ if (!ctx) {
+ ctx = mGLContext;
+ }
+
+ if (!ctx->MakeCurrent()) {
+ // Leak resources!
+ mQuadVBO = 0;
+ mTriangleVBO = 0;
+ mGLContext = nullptr;
+ mPrograms.clear();
+ return;
+ }
+
+ for (std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.begin();
+ iter != mPrograms.end();
+ iter++) {
+ delete iter->second;
+ }
+ mPrograms.clear();
+
+ ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mQuadVBO) {
+ ctx->fDeleteBuffers(1, &mQuadVBO);
+ mQuadVBO = 0;
+ }
+
+ if (mTriangleVBO) {
+ ctx->fDeleteBuffers(1, &mTriangleVBO);
+ mTriangleVBO = 0;
+ }
+
+ mGLContext->MakeCurrent();
+
+ mBlitTextureImageHelper = nullptr;
+
+ mContextStateTracker.DestroyOGL(mGLContext);
+
+ // On the main thread the Widget will be destroyed soon and calling MakeCurrent
+ // after that could cause a crash (at least with GLX, see bug 1059793), unless
+ // context is marked as destroyed.
+ // There may be some textures still alive that will try to call MakeCurrent on
+ // the context so let's make sure it is marked destroyed now.
+ mGLContext->MarkDestroyed();
+
+ mGLContext = nullptr;
+}
+
+bool
+CompositorOGL::Initialize(nsCString* const out_failureReason)
+{
+ ScopedGfxFeatureReporter reporter("GL Layers");
+
+ // Do not allow double initialization
+ MOZ_ASSERT(mGLContext == nullptr, "Don't reinitialize CompositorOGL");
+
+ mGLContext = CreateContext();
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!mGLContext){
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_ANDROID_CONTEXT";
+ NS_RUNTIMEABORT("We need a context on Android");
+ }
+#endif
+
+ if (!mGLContext){
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_CREATE_CONTEXT";
+ return false;
+ }
+
+ MakeCurrent();
+
+ mHasBGRA =
+ mGLContext->IsExtensionSupported(gl::GLContext::EXT_texture_format_BGRA8888) ||
+ mGLContext->IsExtensionSupported(gl::GLContext::EXT_bgra);
+
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ // initialise a common shader to check that we can actually compile a shader
+ RefPtr<EffectSolidColor> effect = new EffectSolidColor(Color(0, 0, 0, 0));
+ ShaderConfigOGL config = GetShaderConfigFor(effect);
+ if (!GetShaderProgramFor(config)) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_COMPILE_SHADER";
+ return false;
+ }
+
+ if (mGLContext->WorkAroundDriverBugs()) {
+ /**
+ * We'll test the ability here to bind NPOT textures to a framebuffer, if
+ * this fails we'll try ARB_texture_rectangle.
+ */
+
+ GLenum textureTargets[] = {
+ LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_NONE
+ };
+
+ if (!mGLContext->IsGLES()) {
+ // No TEXTURE_RECTANGLE_ARB available on ES2
+ textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
+ }
+
+ mFBOTextureTarget = LOCAL_GL_NONE;
+
+ GLuint testFBO = 0;
+ mGLContext->fGenFramebuffers(1, &testFBO);
+ GLuint testTexture = 0;
+
+ for (uint32_t i = 0; i < ArrayLength(textureTargets); i++) {
+ GLenum target = textureTargets[i];
+ if (!target)
+ continue;
+
+ mGLContext->fGenTextures(1, &testTexture);
+ mGLContext->fBindTexture(target, testTexture);
+ mGLContext->fTexParameteri(target,
+ LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexParameteri(target,
+ LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexImage2D(target,
+ 0,
+ LOCAL_GL_RGBA,
+ 5, 3, /* sufficiently NPOT */
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+
+ // unbind this texture, in preparation for binding it to the FBO
+ mGLContext->fBindTexture(target, 0);
+
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, testFBO);
+ mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ target,
+ testTexture,
+ 0);
+
+ if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) ==
+ LOCAL_GL_FRAMEBUFFER_COMPLETE)
+ {
+ mFBOTextureTarget = target;
+ mGLContext->fDeleteTextures(1, &testTexture);
+ break;
+ }
+
+ mGLContext->fDeleteTextures(1, &testTexture);
+ }
+
+ if (testFBO) {
+ mGLContext->fDeleteFramebuffers(1, &testFBO);
+ }
+
+ if (mFBOTextureTarget == LOCAL_GL_NONE) {
+ /* Unable to find a texture target that works with FBOs and NPOT textures */
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_TEXTURE_TARGET";
+ return false;
+ }
+ } else {
+ // not trying to work around driver bugs, so TEXTURE_2D should just work
+ mFBOTextureTarget = LOCAL_GL_TEXTURE_2D;
+ }
+
+ // back to default framebuffer, to avoid confusion
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
+ /* If we're using TEXTURE_RECTANGLE, then we must have the ARB
+ * extension -- the EXT variant does not provide support for
+ * texture rectangle access inside GLSL (sampler2DRect,
+ * texture2DRect).
+ */
+ if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle)){
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_ARB_EXT";
+ return false;
+ }
+ }
+
+ // Create a VBO for triangle vertices.
+ mGLContext->fGenBuffers(1, &mTriangleVBO);
+
+ /* Create a simple quad VBO */
+ mGLContext->fGenBuffers(1, &mQuadVBO);
+
+ // 4 quads, with the number of the quad (vertexID) encoded in w.
+ GLfloat vertices[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f, 0.0f,
+
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 0.0f, 1.0f,
+
+ 0.0f, 0.0f, 0.0f, 2.0f,
+ 1.0f, 0.0f, 0.0f, 2.0f,
+ 0.0f, 1.0f, 0.0f, 2.0f,
+ 1.0f, 0.0f, 0.0f, 2.0f,
+ 0.0f, 1.0f, 0.0f, 2.0f,
+ 1.0f, 1.0f, 0.0f, 2.0f,
+
+ 0.0f, 0.0f, 0.0f, 3.0f,
+ 1.0f, 0.0f, 0.0f, 3.0f,
+ 0.0f, 1.0f, 0.0f, 3.0f,
+ 1.0f, 0.0f, 0.0f, 3.0f,
+ 0.0f, 1.0f, 0.0f, 3.0f,
+ 1.0f, 1.0f, 0.0f, 3.0f,
+ };
+ HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+ verticesOnHeap.ByteLength(),
+ verticesOnHeap.Data(),
+ LOCAL_GL_STATIC_DRAW);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ nsCOMPtr<nsIConsoleService>
+ console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+ if (console) {
+ nsString msg;
+ msg +=
+ NS_LITERAL_STRING("OpenGL compositor Initialized Succesfully.\nVersion: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VERSION)));
+ msg += NS_LITERAL_STRING("\nVendor: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VENDOR)));
+ msg += NS_LITERAL_STRING("\nRenderer: ");
+ msg += NS_ConvertUTF8toUTF16(
+ nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_RENDERER)));
+ msg += NS_LITERAL_STRING("\nFBO Texture Target: ");
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D)
+ msg += NS_LITERAL_STRING("TEXTURE_2D");
+ else
+ msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
+ console->LogStringMessage(msg.get());
+ }
+
+ reporter.SetSuccessful();
+
+ return true;
+}
+
+/*
+ * Returns a size that is equal to, or larger than and closest to,
+ * aSize where both width and height are powers of two.
+ * If the OpenGL setup is capable of using non-POT textures,
+ * then it will just return aSize.
+ */
+static IntSize
+CalculatePOTSize(const IntSize& aSize, GLContext* gl)
+{
+ if (CanUploadNonPowerOfTwo(gl))
+ return aSize;
+
+ return IntSize(RoundUpPow2(aSize.width), RoundUpPow2(aSize.height));
+}
+
+gfx::Rect
+CompositorOGL::GetTextureCoordinates(gfx::Rect textureRect, TextureSource* aTexture)
+{
+ // If the OpenGL setup does not support non-power-of-two textures then the
+ // texture's width and height will have been increased to the next
+ // power-of-two (unless already a power of two). In that case we must scale
+ // the texture coordinates to account for that.
+ if (!CanUploadNonPowerOfTwo(mGLContext)) {
+ const IntSize& textureSize = aTexture->GetSize();
+ const IntSize potSize = CalculatePOTSize(textureSize, mGLContext);
+ if (potSize != textureSize) {
+ const float xScale = (float)textureSize.width / (float)potSize.width;
+ const float yScale = (float)textureSize.height / (float)potSize.height;
+ textureRect.Scale(xScale, yScale);
+ }
+ }
+
+ return textureRect;
+}
+
+void
+CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget);
+ // Logical surface size.
+ const gfx::IntSize& size = aRenderTarget->mInitParams.mSize;
+ // Physical surface size.
+ const gfx::IntSize& phySize = aRenderTarget->mInitParams.mPhySize;
+
+ // Set the viewport correctly.
+ mGLContext->fViewport(0, 0, phySize.width, phySize.height);
+
+ mViewportSize = size;
+
+ if (!aRenderTarget->HasComplexProjection()) {
+ // We flip the view matrix around so that everything is right-side up; we're
+ // drawing directly into the window's back buffer, so this keeps things
+ // looking correct.
+ // XXX: We keep track of whether the window size changed, so we could skip
+ // this update if it hadn't changed since the last call.
+
+ // Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
+ // 2, 2) and flip the contents.
+ Matrix viewMatrix;
+ if (mGLContext->IsOffscreen() && !gIsGtest) {
+ // In case of rendering via GL Offscreen context, disable Y-Flipping
+ viewMatrix.PreTranslate(-1.0, -1.0);
+ viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
+ } else {
+ viewMatrix.PreTranslate(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+ }
+
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+ // If we're drawing directly to the window then we want to offset
+ // drawing by the render offset.
+ if (!mTarget && mCurrentRenderTarget->IsWindow()) {
+ viewMatrix.PreTranslate(mRenderOffset.x, mRenderOffset.y);
+ }
+
+ Matrix4x4 matrix3d = Matrix4x4::From2D(viewMatrix);
+ matrix3d._33 = 0.0f;
+ mProjMatrix = matrix3d;
+ mGLContext->fDepthRange(0.0f, 1.0f);
+ } else {
+ // XXX take into account mRenderOffset
+ bool depthEnable;
+ float zNear, zFar;
+ aRenderTarget->GetProjection(mProjMatrix, depthEnable, zNear, zFar);
+ mGLContext->fDepthRange(zNear, zFar);
+ }
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorOGL::CreateRenderTarget(const IntRect &aRect, SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!gl()) {
+ // CompositingRenderTargetOGL does not work without a gl context.
+ return nullptr;
+ }
+
+ GLuint tex = 0;
+ GLuint fbo = 0;
+ IntRect rect = aRect;
+ IntSize FBOSize;
+ CreateFBOWithTexture(rect, false, 0, &fbo, &tex, &FBOSize);
+ RefPtr<CompositingRenderTargetOGL> surface
+ = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo);
+ surface->Initialize(aRect.Size(), FBOSize, mFBOTextureTarget, aInit);
+ return surface.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorOGL::CreateRenderTargetFromSource(const IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const IntPoint &aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!gl()) {
+ return nullptr;
+ }
+
+ GLuint tex = 0;
+ GLuint fbo = 0;
+ const CompositingRenderTargetOGL* sourceSurface
+ = static_cast<const CompositingRenderTargetOGL*>(aSource);
+ IntRect sourceRect(aSourcePoint, aRect.Size());
+ if (aSource) {
+ CreateFBOWithTexture(sourceRect, true, sourceSurface->GetFBO(),
+ &fbo, &tex);
+ } else {
+ CreateFBOWithTexture(sourceRect, true, 0,
+ &fbo, &tex);
+ }
+
+ RefPtr<CompositingRenderTargetOGL> surface
+ = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo);
+ surface->Initialize(aRect.Size(),
+ sourceRect.Size(),
+ mFBOTextureTarget,
+ INIT_MODE_NONE);
+ return surface.forget();
+}
+
+void
+CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
+{
+ MOZ_ASSERT(aSurface);
+ CompositingRenderTargetOGL* surface
+ = static_cast<CompositingRenderTargetOGL*>(aSurface);
+ if (mCurrentRenderTarget != surface) {
+ mCurrentRenderTarget = surface;
+ if (mCurrentRenderTarget) {
+ mContextStateTracker.PopOGLSection(gl(), "Frame");
+ }
+ mContextStateTracker.PushOGLSection(gl(), "Frame");
+ surface->BindRenderTarget();
+ }
+
+ PrepareViewport(mCurrentRenderTarget);
+}
+
+CompositingRenderTarget*
+CompositorOGL::GetCurrentRenderTarget() const
+{
+ return mCurrentRenderTarget;
+}
+
+static GLenum
+GetFrameBufferInternalFormat(GLContext* gl,
+ GLuint aFrameBuffer,
+ mozilla::widget::CompositorWidget* aWidget)
+{
+ if (aFrameBuffer == 0) { // default framebuffer
+ return aWidget->GetGLFrameBufferFormat();
+ }
+ return LOCAL_GL_RGBA;
+}
+
+void
+CompositorOGL::ClearRect(const gfx::Rect& aRect)
+{
+ // Map aRect to OGL coordinates, origin:bottom-left
+ GLint y = mViewportSize.height - (aRect.y + aRect.height);
+
+ ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGLContext, aRect.x, y, aRect.width, aRect.height);
+ mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
+ mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+}
+
+void
+CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect *aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect *aClipRectOut,
+ IntRect *aRenderBoundsOut)
+{
+ PROFILER_LABEL("CompositorOGL", "BeginFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame");
+
+ gfx::IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = gfx::IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ rect = gfx::IntRect(aRenderBounds.x, aRenderBounds.y, aRenderBounds.width, aRenderBounds.height);
+ }
+
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = rect;
+ }
+
+ GLint width = rect.width;
+ GLint height = rect.height;
+
+ // We can't draw anything to something with no area
+ // so just return
+ if (width == 0 || height == 0)
+ return;
+
+ // We're about to actually draw a frame.
+ mFrameInProgress = true;
+
+ // If the widget size changed, we have to force a MakeCurrent
+ // to make sure that GL sees the updated widget size.
+ if (mWidgetSize.width != width ||
+ mWidgetSize.height != height)
+ {
+ MakeCurrent(ForceMakeCurrent);
+
+ mWidgetSize.width = width;
+ mWidgetSize.height = height;
+ } else {
+ MakeCurrent();
+ }
+
+ mPixelsPerFrame = width * height;
+ mPixelsFilled = 0;
+
+#ifdef MOZ_WIDGET_ANDROID
+ TexturePoolOGL::Fill(gl());
+#endif
+
+ // Default blend function implements "OVER"
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ RefPtr<CompositingRenderTargetOGL> rt =
+ CompositingRenderTargetOGL::RenderTargetForWindow(this,
+ IntSize(width, height));
+ SetRenderTarget(rt);
+
+#ifdef DEBUG
+ mWindowRenderTarget = mCurrentRenderTarget;
+#endif
+
+ if (aClipRectOut && !aClipRectIn) {
+ aClipRectOut->SetRect(0, 0, width, height);
+ }
+
+ mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b, mClearColor.a);
+ mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+}
+
+void
+CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect,
+ bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ GLuint *aFBO, GLuint *aTexture,
+ gfx::IntSize* aAllocSize)
+{
+ *aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer,
+ aAllocSize);
+ mGLContext->fGenFramebuffers(1, aFBO);
+}
+
+GLuint
+CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer, IntSize* aAllocSize)
+{
+ // we're about to create a framebuffer backed by textures to use as an intermediate
+ // surface. What to do if its size (as given by aRect) would exceed the
+ // maximum texture size supported by the GL? The present code chooses the compromise
+ // of just clamping the framebuffer's size to the max supported size.
+ // This gives us a lower resolution rendering of the intermediate surface (children layers).
+ // See bug 827170 for a discussion.
+ IntRect clampedRect = aRect;
+ int32_t maxTexSize = GetMaxTextureSize();
+ clampedRect.width = std::min(clampedRect.width, maxTexSize);
+ clampedRect.height = std::min(clampedRect.height, maxTexSize);
+
+ GLuint tex;
+
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+ mGLContext->fGenTextures(1, &tex);
+ mGLContext->fBindTexture(mFBOTextureTarget, tex);
+
+ if (aCopyFromSource) {
+ GLuint curFBO = mCurrentRenderTarget->GetFBO();
+ if (curFBO != aSourceFrameBuffer) {
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aSourceFrameBuffer);
+ }
+
+ // We're going to create an RGBA temporary fbo. But to
+ // CopyTexImage() from the current framebuffer, the framebuffer's
+ // format has to be compatible with the new texture's. So we
+ // check the format of the framebuffer here and take a slow path
+ // if it's incompatible.
+ GLenum format =
+ GetFrameBufferInternalFormat(gl(), aSourceFrameBuffer, mWidget);
+
+ bool isFormatCompatibleWithRGBA
+ = gl()->IsGLES() ? (format == LOCAL_GL_RGBA)
+ : true;
+
+ if (isFormatCompatibleWithRGBA) {
+ mGLContext->fCopyTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.x, FlipY(clampedRect.y + clampedRect.height),
+ clampedRect.width, clampedRect.height,
+ 0);
+ } else {
+ // Curses, incompatible formats. Take a slow path.
+
+ // RGBA
+ size_t bufferSize = clampedRect.width * clampedRect.height * 4;
+ auto buf = MakeUnique<uint8_t[]>(bufferSize);
+
+ mGLContext->fReadPixels(clampedRect.x, clampedRect.y,
+ clampedRect.width, clampedRect.height,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ buf.get());
+ mGLContext->fTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.width, clampedRect.height,
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ buf.get());
+ }
+
+ GLenum error = mGLContext->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Texture initialization failed! -- error 0x%x, Source %d, Source format %d, RGBA Compat %d",
+ error, aSourceFrameBuffer, format, isFormatCompatibleWithRGBA);
+ NS_ERROR(msg.get());
+ }
+ } else {
+ mGLContext->fTexImage2D(mFBOTextureTarget,
+ 0,
+ LOCAL_GL_RGBA,
+ clampedRect.width, clampedRect.height,
+ 0,
+ LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE,
+ nullptr);
+ }
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fBindTexture(mFBOTextureTarget, 0);
+
+ if (aAllocSize) {
+ aAllocSize->width = clampedRect.width;
+ aAllocSize->height = clampedRect.height;
+ }
+
+ return tex;
+}
+
+ShaderConfigOGL
+CompositorOGL::GetShaderConfigFor(Effect *aEffect,
+ MaskType aMask,
+ gfx::CompositionOp aOp,
+ bool aColorMatrix,
+ bool aDEAAEnabled) const
+{
+ ShaderConfigOGL config;
+
+ switch(aEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ config.SetRenderColor(true);
+ break;
+ case EffectTypes::YCBCR:
+ config.SetYCbCr(true);
+ break;
+ case EffectTypes::NV12:
+ config.SetNV12(true);
+ config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ config.SetComponentAlpha(true);
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffect);
+ gfx::SurfaceFormat format = effectComponentAlpha->mOnWhite->GetFormat();
+ config.SetRBSwap(format == gfx::SurfaceFormat::B8G8R8A8 ||
+ format == gfx::SurfaceFormat::B8G8R8X8);
+ TextureSourceOGL* source = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ config.SetTextureTarget(source->GetTextureTarget());
+ break;
+ }
+ case EffectTypes::RENDER_TARGET:
+ config.SetTextureTarget(mFBOTextureTarget);
+ break;
+ default:
+ {
+ MOZ_ASSERT(aEffect->mType == EffectTypes::RGB);
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffect);
+ TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
+ MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8);
+ MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16 ||
+ source->GetFormat() == gfx::SurfaceFormat::YUV422 );
+ config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
+ source->GetFormat());
+ if (!texturedEffect->mPremultiplied) {
+ config.SetNoPremultipliedAlpha();
+ }
+ break;
+ }
+ }
+ config.SetColorMatrix(aColorMatrix);
+ config.SetMask(aMask == MaskType::Mask);
+ config.SetDEAA(aDEAAEnabled);
+ config.SetCompositionOp(aOp);
+ return config;
+}
+
+ShaderProgramOGL*
+CompositorOGL::GetShaderProgramFor(const ShaderConfigOGL &aConfig)
+{
+ std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.find(aConfig);
+ if (iter != mPrograms.end())
+ return iter->second;
+
+ ProgramProfileOGL profile = ProgramProfileOGL::GetProfileFor(aConfig);
+ ShaderProgramOGL *shader = new ShaderProgramOGL(gl(), profile);
+ if (!shader->Initialize()) {
+ delete shader;
+ return nullptr;
+ }
+
+ mPrograms[aConfig] = shader;
+ return shader;
+}
+
+void
+CompositorOGL::ActivateProgram(ShaderProgramOGL* aProg)
+{
+ if (mCurrentProgram != aProg) {
+ gl()->fUseProgram(aProg->GetProgram());
+ mCurrentProgram = aProg;
+ }
+}
+
+void
+CompositorOGL::ResetProgram()
+{
+ mCurrentProgram = nullptr;
+}
+
+static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true)
+{
+ if (BlendOpIsMixBlendMode(aBlendMode)) {
+ // Mix-blend modes require an extra step (or more) that cannot be expressed
+ // in the fixed-function blending capabilities of opengl. We handle them
+ // separately in shaders, and the shaders assume we will use our default
+ // blend function for compositing (premultiplied OP_OVER).
+ return false;
+ }
+ if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
+ return false;
+ }
+
+ GLenum srcBlend;
+ GLenum dstBlend;
+ GLenum srcAlphaBlend = LOCAL_GL_ONE;
+ GLenum dstAlphaBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+
+ switch (aBlendMode) {
+ case gfx::CompositionOp::OP_OVER:
+ MOZ_ASSERT(!aIsPremultiplied);
+ srcBlend = LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case gfx::CompositionOp::OP_SOURCE:
+ srcBlend = aIsPremultiplied ? LOCAL_GL_ONE : LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ZERO;
+ srcAlphaBlend = LOCAL_GL_ONE;
+ dstAlphaBlend = LOCAL_GL_ZERO;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported blend mode!");
+ return false;
+ }
+
+ aGL->fBlendFuncSeparate(srcBlend, dstBlend,
+ srcAlphaBlend, dstAlphaBlend);
+ return true;
+}
+
+gfx::Point3D
+CompositorOGL::GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2)
+{
+ // Return standard coefficients for a line between aPoint1 and aPoint2
+ // for standard line equation:
+ //
+ // Ax + By + C = 0
+ //
+ // A = (p1.y – p2.y)
+ // B = (p2.x – p1.x)
+ // C = (p1.x * p2.y) – (p2.x * p1.y)
+
+ gfx::Point3D coeffecients;
+ coeffecients.x = aPoint1.y - aPoint2.y;
+ coeffecients.y = aPoint2.x - aPoint1.x;
+ coeffecients.z = aPoint1.x * aPoint2.y - aPoint2.x * aPoint1.y;
+
+ coeffecients *= 1.0f / sqrtf(coeffecients.x * coeffecients.x +
+ coeffecients.y * coeffecients.y);
+
+ // Offset outwards by 0.5 pixel as the edge is considered to be 1 pixel
+ // wide and included within the interior of the polygon
+ coeffecients.z += 0.5f;
+
+ return coeffecients;
+}
+
+void
+CompositorOGL::DrawQuad(const Rect& aRect,
+ const IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ PROFILER_LABEL("CompositorOGL", "DrawQuad",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ DrawGeometry(aRect, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+}
+
+void
+CompositorOGL::DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ PROFILER_LABEL("CompositorOGL", "DrawTriangle",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ DrawGeometry(aTriangle, aClipRect, aEffectChain,
+ aOpacity, aTransform, aVisibleRect);
+}
+
+template<typename Geometry>
+void
+CompositorOGL::DrawGeometry(const Geometry& aGeometry,
+ const IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ MOZ_ASSERT(mFrameInProgress, "frame not started");
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+
+ MakeCurrent();
+
+ IntPoint offset = mCurrentRenderTarget->GetOrigin();
+ IntSize size = mCurrentRenderTarget->GetSize();
+
+ Rect renderBound(0, 0, size.width, size.height);
+ renderBound.IntersectRect(renderBound, Rect(aClipRect));
+ renderBound.MoveBy(offset);
+
+ Rect destRect = aTransform.TransformAndClipBounds(aGeometry, renderBound);
+
+ // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
+ // quads. Fix me.
+ mPixelsFilled += destRect.width * destRect.height;
+
+ // Do a simple culling if this rect is out of target buffer.
+ // Inflate a small size to avoid some numerical imprecision issue.
+ destRect.Inflate(1, 1);
+ destRect.MoveBy(-offset);
+ renderBound = Rect(0, 0, size.width, size.height);
+ if (!renderBound.Intersects(destRect)) {
+ return;
+ }
+
+ LayerScope::DrawBegin();
+
+ IntRect clipRect = aClipRect;
+ // aClipRect is in destination coordinate space (after all
+ // transforms and offsets have been applied) so if our
+ // drawing is going to be shifted by mRenderOffset then we need
+ // to shift the clip rect by the same amount.
+ if (!mTarget && mCurrentRenderTarget->IsWindow()) {
+ clipRect.MoveBy(mRenderOffset.x, mRenderOffset.y);
+ }
+
+ ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGLContext, clipRect.x, FlipY(clipRect.y + clipRect.height),
+ clipRect.width, clipRect.height);
+
+ MaskType maskType;
+ EffectMask* effectMask;
+ TextureSourceOGL* sourceMask = nullptr;
+ gfx::Matrix4x4 maskQuadTransform;
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ sourceMask = effectMask->mMaskTexture->AsSourceOGL();
+
+ // NS_ASSERTION(textureMask->IsAlpha(),
+ // "OpenGL mask layers must be backed by alpha surfaces");
+
+ // We're assuming that the gl backend won't cheat and use NPOT
+ // textures when glContext says it can't (which seems to happen
+ // on a mac when you force POT textures)
+ IntSize maskSize = CalculatePOTSize(effectMask->mSize, mGLContext);
+
+ const gfx::Matrix4x4& maskTransform = effectMask->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskSize));
+ bounds = maskTransform.As2D().TransformBounds(bounds);
+
+ maskQuadTransform._11 = 1.0f/bounds.width;
+ maskQuadTransform._22 = 1.0f/bounds.height;
+ maskQuadTransform._41 = float(-bounds.x)/bounds.width;
+ maskQuadTransform._42 = float(-bounds.y)/bounds.height;
+
+ maskType = MaskType::Mask;
+ } else {
+ maskType = MaskType::MaskNone;
+ }
+
+ // Determine the color if this is a color shader and fold the opacity into
+ // the color since color shaders don't have an opacity uniform.
+ Color color;
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::SOLID_COLOR) {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+ color = effectSolidColor->mColor;
+
+ Float opacity = aOpacity * color.a;
+ color.r *= opacity;
+ color.g *= opacity;
+ color.b *= opacity;
+ color.a = opacity;
+
+ // We can fold opacity into the color, so no need to consider it further.
+ aOpacity = 1.f;
+ }
+
+ bool createdMixBlendBackdropTexture = false;
+ GLuint mixBlendBackdrop = 0;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+ }
+
+ // Only apply DEAA to quads that have been transformed such that aliasing
+ // could be visible
+ bool bEnableAA = gfxPrefs::LayersDEAAEnabled() &&
+ !aTransform.Is2DIntegerTranslation();
+
+ bool colorMatrix = aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX];
+ ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect,
+ maskType, blendMode, colorMatrix,
+ bEnableAA);
+
+ config.SetOpacity(aOpacity != 1.f);
+ ApplyPrimitiveConfig(config, aGeometry);
+
+ ShaderProgramOGL *program = GetShaderProgramFor(config);
+ ActivateProgram(program);
+ program->SetProjectionMatrix(mProjMatrix);
+ program->SetLayerTransform(aTransform);
+ LayerScope::SetLayerTransform(aTransform);
+
+ if (colorMatrix) {
+ EffectColorMatrix* effectColorMatrix =
+ static_cast<EffectColorMatrix*>(aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX].get());
+ program->SetColorMatrix(effectColorMatrix->mColorMatrix);
+ }
+
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ gfx::Matrix4x4 backdropTransform;
+
+ if (gl()->IsExtensionSupported(GLContext::NV_texture_barrier)) {
+ // The NV_texture_barrier extension lets us read directly from the
+ // backbuffer. Let's do that.
+ // We need to tell OpenGL about this, so that it can make sure everything
+ // on the GPU is happening in the right order.
+ gl()->fTextureBarrier();
+ mixBlendBackdrop = mCurrentRenderTarget->GetTextureHandle();
+ } else {
+ gfx::IntRect rect = ComputeBackdropCopyRect(aGeometry, aClipRect,
+ aTransform, &backdropTransform);
+ mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
+ createdMixBlendBackdropTexture = true;
+ }
+ program->SetBackdropTransform(backdropTransform);
+ }
+
+ program->SetRenderOffset(offset.x, offset.y);
+ LayerScope::SetRenderOffset(offset.x, offset.y);
+
+ if (aOpacity != 1.f)
+ program->SetLayerOpacity(aOpacity);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ TextureSourceOGL* source = nullptr;
+ if (aEffectChain.mPrimaryEffect->mType == EffectTypes::COMPONENT_ALPHA) {
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ source = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ } else {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ source = texturedEffect->mTexture->AsSourceOGL();
+ }
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
+ program->SetTexCoordMultiplier(source->GetSize().width, source->GetSize().height);
+ }
+
+ // XXX kip - These calculations could be performed once per layer rather than
+ // for every tile. This might belong in Compositor.cpp once DEAA
+ // is implemented for DirectX.
+ if (bEnableAA) {
+ // Calculate the transformed vertices of aVisibleRect in screen space
+ // pixels, mirroring the calculations in the vertex shader
+ Matrix4x4 flatTransform = aTransform;
+ flatTransform.PostTranslate(-offset.x, -offset.y, 0.0f);
+ flatTransform *= mProjMatrix;
+
+ Rect viewportClip = Rect(-1.0f, -1.0f, 2.0f, 2.0f);
+ size_t edgeCount = 0;
+ Point3D coefficients[4];
+
+ Point points[Matrix4x4::kTransformAndClipRectMaxVerts];
+ size_t pointCount = flatTransform.TransformAndClipRect(aVisibleRect, viewportClip, points);
+ for (size_t i = 0; i < pointCount; i++) {
+ points[i] = Point((points[i].x * 0.5f + 0.5f) * mViewportSize.width,
+ (points[i].y * 0.5f + 0.5f) * mViewportSize.height);
+ }
+ if (pointCount > 2) {
+ // Use shoelace formula on a triangle in the clipped quad to determine if
+ // winding order is reversed. Iterate through the triangles until one is
+ // found with a non-zero area.
+ float winding = 0.0f;
+ size_t wp = 0;
+ while (winding == 0.0f && wp < pointCount) {
+ int wp1 = (wp + 1) % pointCount;
+ int wp2 = (wp + 2) % pointCount;
+ winding = (points[wp1].x - points[wp].x) * (points[wp1].y + points[wp].y) +
+ (points[wp2].x - points[wp1].x) * (points[wp2].y + points[wp1].y) +
+ (points[wp].x - points[wp2].x) * (points[wp].y + points[wp2].y);
+ wp++;
+ }
+ bool frontFacing = winding >= 0.0f;
+
+ // Calculate the line coefficients used by the DEAA shader to determine the
+ // sub-pixel coverage of the edge pixels
+ for (size_t i=0; i<pointCount; i++) {
+ const Point& p1 = points[i];
+ const Point& p2 = points[(i + 1) % pointCount];
+ // Create a DEAA edge for any non-straight lines, to a maximum of 4
+ if (p1.x != p2.x && p1.y != p2.y && edgeCount < 4) {
+ if (frontFacing) {
+ coefficients[edgeCount++] = GetLineCoefficients(p2, p1);
+ } else {
+ coefficients[edgeCount++] = GetLineCoefficients(p1, p2);
+ }
+ }
+ }
+ }
+
+ // The coefficients that are not needed must not cull any fragments.
+ // We fill these unused coefficients with a clipping plane that has no
+ // effect.
+ for (size_t i = edgeCount; i < 4; i++) {
+ coefficients[i] = Point3D(0.0f, 1.0f, mViewportSize.height);
+ }
+
+ // Set uniforms required by DEAA shader
+ Matrix4x4 transformInverted = aTransform;
+ transformInverted.Invert();
+ program->SetLayerTransformInverse(transformInverted);
+ program->SetDEAAEdges(coefficients);
+ program->SetVisibleCenter(aVisibleRect.Center());
+ program->SetViewportSize(mViewportSize);
+ }
+
+ bool didSetBlendMode = false;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ program->SetRenderColor(color);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
+ }
+
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+
+ BindAndDrawGeometry(program, aGeometry);
+ }
+ break;
+
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource *source = texturedEffect->mTexture;
+
+ didSetBlendMode = SetBlendMode(gl(), blendMode, texturedEffect->mPremultiplied);
+
+ gfx::SamplingFilter samplingFilter = texturedEffect->mSamplingFilter;
+
+ source->AsSourceOGL()->BindTexture(LOCAL_GL_TEXTURE0, samplingFilter);
+
+ program->SetTextureUnit(0);
+
+ Matrix4x4 textureTransform = source->AsSourceOGL()->GetTextureTransform();
+ program->SetTextureTransform(textureTransform);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
+ }
+
+ BindAndDrawGeometryWithTextureRect(program, aGeometry,
+ texturedEffect->mTextureCoords, source);
+ }
+ break;
+ case EffectTypes::YCBCR: {
+ EffectYCbCr* effectYCbCr =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceYCbCr = effectYCbCr->mTexture;
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
+ TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCb || !sourceCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectYCbCr->mSamplingFilter);
+ sourceCb->BindTexture(LOCAL_GL_TEXTURE1, effectYCbCr->mSamplingFilter);
+ sourceCr->BindTexture(LOCAL_GL_TEXTURE2, effectYCbCr->mSamplingFilter);
+
+ program->SetYCbCrTextureUnits(Y, Cb, Cr);
+ program->SetTextureTransform(Matrix4x4());
+ program->SetYUVColorSpace(effectYCbCr->mYUVColorSpace);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
+ }
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectYCbCr->mTextureCoords,
+ sourceYCbCr->GetSubSource(Y));
+ }
+ break;
+ case EffectTypes::NV12: {
+ EffectNV12* effectNV12 =
+ static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceNV12 = effectNV12->mTexture;
+ const int Y = 0, CbCr = 1;
+ TextureSourceOGL* sourceY = sourceNV12->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCbCr = sourceNV12->GetSubSource(CbCr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCbCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectNV12->mSamplingFilter);
+ sourceCbCr->BindTexture(LOCAL_GL_TEXTURE1, effectNV12->mSamplingFilter);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
+ program->SetCbCrTexCoordMultiplier(sourceCbCr->GetSize().width, sourceCbCr->GetSize().height);
+ }
+
+ program->SetNV12TextureUnits(Y, CbCr);
+ program->SetTextureTransform(Matrix4x4());
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
+ }
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectNV12->mTextureCoords,
+ sourceNV12->GetSubSource(Y));
+ }
+ break;
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<CompositingRenderTargetOGL> surface
+ = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
+
+ surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget);
+
+ // Drawing is always flipped, but when copying between surfaces we want to avoid
+ // this, so apply a flip here to cancel the other one out.
+ Matrix transform;
+ transform.PreTranslate(0.0, 1.0);
+ transform.PreScale(1.0f, -1.0f);
+ program->SetTextureTransform(Matrix4x4::From2D(transform));
+ program->SetTextureUnit(0);
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
+ }
+ if (mixBlendBackdrop) {
+ BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
+ }
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // 2DRect case, get the multiplier right for a sampler2DRect
+ program->SetTexCoordMultiplier(surface->GetSize().width,
+ surface->GetSize().height);
+ }
+
+ // Drawing is always flipped, but when copying between surfaces we want to avoid
+ // this. Pass true for the flip parameter to introduce a second flip
+ // that cancels the other one out.
+ didSetBlendMode = SetBlendMode(gl(), blendMode);
+ BindAndDrawGeometry(program, aGeometry);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA: {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ MOZ_ASSERT(blendMode == gfx::CompositionOp::OP_OVER, "Can't support blend modes with component alpha!");
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceOGL* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceOGL();
+ TextureSourceOGL* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceOGL();
+
+ if (!sourceOnBlack->IsValid() ||
+ !sourceOnWhite->IsValid()) {
+ NS_WARNING("Invalid layer texture for component alpha");
+ return;
+ }
+
+ sourceOnBlack->BindTexture(LOCAL_GL_TEXTURE0, effectComponentAlpha->mSamplingFilter);
+ sourceOnWhite->BindTexture(LOCAL_GL_TEXTURE1, effectComponentAlpha->mSamplingFilter);
+
+ program->SetBlackTextureUnit(0);
+ program->SetWhiteTextureUnit(1);
+ program->SetTextureTransform(Matrix4x4());
+
+ if (maskType != MaskType::MaskNone) {
+ BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
+ }
+ // Pass 1.
+ gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR,
+ LOCAL_GL_ONE, LOCAL_GL_ONE);
+ program->SetTexturePass2(false);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectComponentAlpha->mTextureCoords,
+ effectComponentAlpha->mOnBlack);
+
+ // Pass 2.
+ gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE,
+ LOCAL_GL_ONE, LOCAL_GL_ONE);
+ program->SetTexturePass2(true);
+ BindAndDrawGeometryWithTextureRect(program,
+ aGeometry,
+ effectComponentAlpha->mTextureCoords,
+ effectComponentAlpha->mOnBlack);
+
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ }
+ break;
+ default:
+ MOZ_ASSERT(false, "Unhandled effect type");
+ break;
+ }
+
+ if (didSetBlendMode) {
+ gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ }
+ if (createdMixBlendBackdropTexture) {
+ gl()->fDeleteTextures(1, &mixBlendBackdrop);
+ }
+
+ // in case rendering has used some other GL context
+ MakeCurrent();
+
+ LayerScope::DrawEnd(mGLContext, aEffectChain,
+ aGeometry.width, aGeometry.height);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTextureRect)
+{
+ BindAndDrawQuad(aProgram, aRect, aTextureRect);
+}
+
+void
+CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTextureRect)
+{
+ NS_ASSERTION(aProgram->HasInitialized(), "Shader program not correctly initialized");
+
+ const gfx::TexturedTriangle& t = aTriangle;
+ const gfx::Triangle& tex = t.textureCoords;
+
+ GLfloat vertices[] = {
+ t.p1.x, t.p1.y, 0.0f, 1.0f, tex.p1.x, tex.p1.y,
+ t.p2.x, t.p2.y, 0.0f, 1.0f, tex.p2.x, tex.p2.y,
+ t.p3.x, t.p3.y, 0.0f, 1.0f, tex.p3.x, tex.p3.y
+ };
+
+ HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTriangleVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+ verticesOnHeap.ByteLength(),
+ verticesOnHeap.Data(),
+ LOCAL_GL_STREAM_DRAW);
+
+ const GLsizei stride = 6 * sizeof(GLfloat);
+ InitializeVAO(kCoordinateAttributeIndex, 4, stride, 0);
+ InitializeVAO(kTexCoordinateAttributeIndex, 2, stride, 4 * sizeof(GLfloat));
+
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 3);
+
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fDisableVertexAttribArray(kTexCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+}
+
+// |aRect| is the rectangle we want to draw to. We will draw it with
+// up to 4 draw commands if necessary to avoid wrapping.
+// |aTexCoordRect| is the rectangle from the texture that we want to
+// draw using the given program.
+// |aTexture| is the texture we are drawing. Its actual size can be
+// larger than the rectangle given by |texCoordRect|.
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const Rect& aRect,
+ const Rect& aTexCoordRect,
+ TextureSource *aTexture)
+{
+ Rect scaledTexCoordRect = GetTextureCoordinates(aTexCoordRect, aTexture);
+ Rect layerRects[4];
+ Rect textureRects[4];
+ size_t rects = DecomposeIntoNoRepeatRects(aRect,
+ scaledTexCoordRect,
+ &layerRects,
+ &textureRects);
+
+ BindAndDrawQuads(aProg, rects, layerRects, textureRects);
+}
+
+void
+CompositorOGL::BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture)
+{
+ BindAndDrawGeometry(aProg, aTriangle,
+ GetTextureCoordinates(aTexCoordRect, aTexture));
+}
+
+void
+CompositorOGL::BindAndDrawQuads(ShaderProgramOGL *aProg,
+ int aQuads,
+ const Rect* aLayerRects,
+ const Rect* aTextureRects)
+{
+ NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ InitializeVAO(kCoordinateAttributeIndex, 4, 0, 0);
+
+ aProg->SetLayerRects(aLayerRects);
+ if (aProg->GetTextureCount() > 0) {
+ aProg->SetTextureRects(aTextureRects);
+ }
+
+ // We are using GL_TRIANGLES here because the Mac Intel drivers fail to properly
+ // process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+ LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects);
+}
+
+void
+CompositorOGL::InitializeVAO(const GLuint aAttrib, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset)
+{
+ mGLContext->fVertexAttribPointer(aAttrib, aComponents, LOCAL_GL_FLOAT,
+ LOCAL_GL_FALSE, aStride,
+ reinterpret_cast<GLvoid*>(aOffset));
+ mGLContext->fEnableVertexAttribArray(aAttrib);
+}
+
+void
+CompositorOGL::EndFrame()
+{
+ PROFILER_LABEL("CompositorOGL", "EndFrame",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::DumpCompositorTextures()) {
+ LayoutDeviceIntSize size;
+ if (mUseExternalSurfaceSize) {
+ size = LayoutDeviceIntSize(mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ size = mWidget->GetClientSize();
+ }
+ RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8);
+ if (target) {
+ CopyToTarget(target, nsIntPoint(), Matrix());
+ WriteSnapshotToDumpFile(this, target);
+ }
+ }
+#endif
+
+ mContextStateTracker.PopOGLSection(gl(), "Frame");
+
+ mFrameInProgress = false;
+
+ if (mTarget) {
+ CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+ mCurrentRenderTarget = nullptr;
+ Compositor::EndFrame();
+ return;
+ }
+
+ mCurrentRenderTarget = nullptr;
+
+ if (mTexturePool) {
+ mTexturePool->EndFrame();
+ }
+
+ mGLContext->SwapBuffers();
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ // Unbind all textures
+ for (GLuint i = 0; i <= 4; i++) {
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+ if (!mGLContext->IsGLES()) {
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+ }
+ }
+
+ Compositor::EndFrame();
+}
+
+void
+CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(!mTarget);
+ if (mTexturePool) {
+ mTexturePool->EndFrame();
+ }
+}
+
+void
+CompositorOGL::SetDestinationSurfaceSize(const IntSize& aSize)
+{
+ mSurfaceSize.width = aSize.width;
+ mSurfaceSize.height = aSize.height;
+}
+
+void
+CompositorOGL::CopyToTarget(DrawTarget* aTarget, const nsIntPoint& aTopLeft, const gfx::Matrix& aTransform)
+{
+ MOZ_ASSERT(aTarget);
+ IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ rect = IntRect(0, 0, mWidgetSize.width, mWidgetSize.height);
+ }
+ GLint width = rect.width;
+ GLint height = rect.height;
+
+ if ((int64_t(width) * int64_t(height) * int64_t(4)) > INT32_MAX) {
+ NS_ERROR("Widget size too big - integer overflow!");
+ return;
+ }
+
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (!mGLContext->IsGLES()) {
+ // GLES2 promises that binding to any custom FBO will attach
+ // to GL_COLOR_ATTACHMENT0 attachment point.
+ mGLContext->fReadBuffer(LOCAL_GL_BACK);
+ }
+
+ RefPtr<DataSourceSurface> source =
+ Factory::CreateDataSourceSurface(rect.Size(), gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!source)) {
+ return;
+ }
+
+ ReadPixelsIntoDataSurface(mGLContext, source);
+
+ // Map from GL space to Cairo space and reverse the world transform.
+ Matrix glToCairoTransform = aTransform;
+ glToCairoTransform.Invert();
+ glToCairoTransform.PreScale(1.0, -1.0);
+ glToCairoTransform.PreTranslate(0.0, -height);
+
+ glToCairoTransform.PostTranslate(-aTopLeft.x, -aTopLeft.y);
+
+ Matrix oldMatrix = aTarget->GetTransform();
+ aTarget->SetTransform(glToCairoTransform);
+ Rect floatRect = Rect(rect.x, rect.y, rect.width, rect.height);
+ aTarget->DrawSurface(source, floatRect, floatRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ aTarget->SetTransform(oldMatrix);
+ aTarget->Flush();
+}
+
+void
+CompositorOGL::Pause()
+{
+#ifdef MOZ_WIDGET_ANDROID
+ if (!gl() || gl()->IsDestroyed())
+ return;
+
+ // ReleaseSurface internally calls MakeCurrent.
+ gl()->ReleaseSurface();
+#endif
+}
+
+bool
+CompositorOGL::Resume()
+{
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
+ if (!gl() || gl()->IsDestroyed())
+ return false;
+
+ // RenewSurface internally calls MakeCurrent.
+ return gl()->RenewSurface(GetWidget()->RealWidget());
+#endif
+ return true;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorOGL::CreateDataTextureSource(TextureFlags aFlags)
+{
+ return MakeAndAddRef<TextureImageTextureSourceOGL>(this, aFlags);
+}
+
+bool
+CompositorOGL::SupportsPartialTextureUpdate()
+{
+ return CanUploadSubTextures(mGLContext);
+}
+
+int32_t
+CompositorOGL::GetMaxTextureSize() const
+{
+ MOZ_ASSERT(mGLContext);
+ GLint texSize = 0;
+ mGLContext->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
+ &texSize);
+ MOZ_ASSERT(texSize != 0);
+ return texSize;
+}
+
+void
+CompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+ mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
+}
+
+GLBlitTextureImageHelper*
+CompositorOGL::BlitTextureImageHelper()
+{
+ if (!mBlitTextureImageHelper) {
+ mBlitTextureImageHelper = MakeUnique<GLBlitTextureImageHelper>(this);
+ }
+
+ return mBlitTextureImageHelper.get();
+}
+
+
+
+GLuint
+CompositorOGL::GetTemporaryTexture(GLenum aTarget, GLenum aUnit)
+{
+ if (!mTexturePool) {
+ mTexturePool = new PerUnitTexturePoolOGL(gl());
+ }
+ return mTexturePool->GetTexture(aTarget, aUnit);
+}
+
+GLuint
+PerUnitTexturePoolOGL::GetTexture(GLenum aTarget, GLenum aTextureUnit)
+{
+ if (mTextureTarget == 0) {
+ mTextureTarget = aTarget;
+ }
+ MOZ_ASSERT(mTextureTarget == aTarget);
+
+ size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
+ // lazily grow the array of temporary textures
+ if (mTextures.Length() <= index) {
+ size_t prevLength = mTextures.Length();
+ mTextures.SetLength(index + 1);
+ for(unsigned int i = prevLength; i <= index; ++i) {
+ mTextures[i] = 0;
+ }
+ }
+ // lazily initialize the temporary textures
+ if (!mTextures[index]) {
+ if (!mGL->MakeCurrent()) {
+ return 0;
+ }
+ mGL->fGenTextures(1, &mTextures[index]);
+ mGL->fBindTexture(aTarget, mTextures[index]);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ }
+ return mTextures[index];
+}
+
+void
+PerUnitTexturePoolOGL::DestroyTextures()
+{
+ if (mGL && mGL->MakeCurrent()) {
+ if (mTextures.Length() > 0) {
+ mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
+ }
+ }
+ mTextures.SetLength(0);
+}
+
+void
+PerFrameTexturePoolOGL::DestroyTextures()
+{
+ if (!mGL->MakeCurrent()) {
+ return;
+ }
+
+ if (mUnusedTextures.Length() > 0) {
+ mGL->fDeleteTextures(mUnusedTextures.Length(), &mUnusedTextures[0]);
+ mUnusedTextures.Clear();
+ }
+
+ if (mCreatedTextures.Length() > 0) {
+ mGL->fDeleteTextures(mCreatedTextures.Length(), &mCreatedTextures[0]);
+ mCreatedTextures.Clear();
+ }
+}
+
+GLuint
+PerFrameTexturePoolOGL::GetTexture(GLenum aTarget, GLenum)
+{
+ if (mTextureTarget == 0) {
+ mTextureTarget = aTarget;
+ }
+
+ // The pool should always use the same texture target because it is illegal
+ // to change the target of an already exisiting gl texture.
+ // If we need to use several targets, a pool with several sub-pools (one per
+ // target) will have to be implemented.
+ // At the moment this pool is only used with tiling on b2g so we always need
+ // the same target.
+ MOZ_ASSERT(mTextureTarget == aTarget);
+
+ GLuint texture = 0;
+
+ if (!mUnusedTextures.IsEmpty()) {
+ // Try to reuse one from the unused pile first
+ texture = mUnusedTextures[0];
+ mUnusedTextures.RemoveElementAt(0);
+ } else if (mGL->MakeCurrent()) {
+ // There isn't one to reuse, create one.
+ mGL->fGenTextures(1, &texture);
+ mGL->fBindTexture(aTarget, texture);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ }
+
+ if (texture) {
+ mCreatedTextures.AppendElement(texture);
+ }
+
+ return texture;
+}
+
+void
+PerFrameTexturePoolOGL::EndFrame()
+{
+ if (!mGL->MakeCurrent()) {
+ // this means the context got destroyed underneith us somehow, and the driver
+ // already has destroyed the textures.
+ mCreatedTextures.Clear();
+ mUnusedTextures.Clear();
+ return;
+ }
+
+ // Some platforms have issues unlocking Gralloc buffers even when they're
+ // rebound.
+ if (gfxPrefs::OverzealousGrallocUnlocking()) {
+ mUnusedTextures.AppendElements(mCreatedTextures);
+ mCreatedTextures.Clear();
+ }
+
+ // Delete unused textures
+ for (size_t i = 0; i < mUnusedTextures.Length(); i++) {
+ GLuint texture = mUnusedTextures[i];
+ mGL->fDeleteTextures(1, &texture);
+ }
+ mUnusedTextures.Clear();
+
+ // Move all created textures into the unused pile
+ mUnusedTextures.AppendElements(mCreatedTextures);
+ mCreatedTextures.Clear();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
new file mode 100644
index 000000000..da204b12e
--- /dev/null
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITOROGL_H
+#define MOZILLA_GFX_COMPOSITOROGL_H
+
+#include "ContextStateTracker.h"
+#include "gfx2DGlue.h"
+#include "GLContextTypes.h" // for GLContext, etc
+#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc
+#include "OGLShaderProgram.h" // for ShaderProgramOGL, etc
+#include "Units.h" // for ScreenPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override, final
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefPtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/Types.h" // for Float, SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, Compositor, etc
+#include "mozilla/layers/CompositorTypes.h" // for MaskType::MaskType::NumMaskTypes, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsThreadUtils.h" // for nsRunnable
+#include "nsXULAppAPI.h" // for XRE_GetProcessType
+#include "nscore.h" // for NS_IMETHOD
+
+class nsIWidget;
+
+namespace mozilla {
+
+namespace layers {
+
+class CompositingRenderTarget;
+class CompositingRenderTargetOGL;
+class DataTextureSource;
+class GLManagerCompositor;
+class TextureSource;
+struct Effect;
+struct EffectChain;
+class GLBlitTextureImageHelper;
+
+/**
+ * Interface for pools of temporary gl textures for the compositor.
+ * The textures are fully owned by the pool, so the latter is responsible
+ * calling fDeleteTextures accordingly.
+ * Users of GetTexture receive a texture that is only valid for the duration
+ * of the current frame.
+ * This is primarily intended for direct texturing APIs that need to attach
+ * shared objects (such as an EGLImage) to a gl texture.
+ */
+class CompositorTexturePoolOGL
+{
+protected:
+ virtual ~CompositorTexturePoolOGL() {}
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(CompositorTexturePoolOGL)
+
+ virtual void Clear() = 0;
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aEnum) = 0;
+
+ virtual void EndFrame() = 0;
+};
+
+/**
+ * Agressively reuses textures. One gl texture per texture unit in total.
+ * So far this hasn't shown the best results on b2g.
+ */
+class PerUnitTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+ explicit PerUnitTexturePoolOGL(gl::GLContext* aGL)
+ : mTextureTarget(0) // zero is never a valid texture target
+ , mGL(aGL)
+ {}
+
+ virtual ~PerUnitTexturePoolOGL()
+ {
+ DestroyTextures();
+ }
+
+ virtual void Clear() override
+ {
+ DestroyTextures();
+ }
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override;
+
+ virtual void EndFrame() override {}
+
+protected:
+ void DestroyTextures();
+
+ GLenum mTextureTarget;
+ nsTArray<GLuint> mTextures;
+ RefPtr<gl::GLContext> mGL;
+};
+
+/**
+ * Reuse gl textures from a pool of textures that haven't yet been
+ * used during the current frame.
+ * All the textures that are not used at the end of a frame are
+ * deleted.
+ * This strategy seems to work well with gralloc textures because destroying
+ * unused textures which are bound to gralloc buffers let drivers know that it
+ * can unlock the gralloc buffers.
+ */
+class PerFrameTexturePoolOGL : public CompositorTexturePoolOGL
+{
+public:
+ explicit PerFrameTexturePoolOGL(gl::GLContext* aGL)
+ : mTextureTarget(0) // zero is never a valid texture target
+ , mGL(aGL)
+ {}
+
+ virtual ~PerFrameTexturePoolOGL()
+ {
+ DestroyTextures();
+ }
+
+ virtual void Clear() override
+ {
+ DestroyTextures();
+ }
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override;
+
+ virtual void EndFrame() override;
+
+protected:
+ void DestroyTextures();
+
+ GLenum mTextureTarget;
+ RefPtr<gl::GLContext> mGL;
+ nsTArray<GLuint> mCreatedTextures;
+ nsTArray<GLuint> mUnusedTextures;
+};
+
+// If you want to make this class not final, first remove calls to virtual
+// methods (Destroy) that are made in the destructor.
+class CompositorOGL final : public Compositor
+{
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class GLManagerCompositor;
+ friend class CompositingRenderTargetOGL;
+
+ std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms;
+public:
+ explicit CompositorOGL(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget,
+ int aSurfaceWidth = -1, int aSurfaceHeight = -1,
+ bool aUseExternalSurfaceSize = false);
+
+protected:
+ virtual ~CompositorOGL();
+
+public:
+ virtual CompositorOGL* AsCompositorOGL() override { return this; }
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ virtual bool Initialize(nsCString* const out_failureReason) override;
+
+ virtual void Destroy() override;
+
+ virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
+ {
+ TextureFactoryIdentifier result =
+ TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL,
+ XRE_GetProcessType(),
+ GetMaxTextureSize(),
+ mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
+ SupportsPartialTextureUpdate());
+ return result;
+ }
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override;
+
+ virtual already_AddRefed<CompositingRenderTarget>
+ CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint) override;
+
+ virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override;
+ virtual CompositingRenderTarget* GetCurrentRenderTarget() const override;
+
+ virtual void DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ virtual void EndFrame() override;
+ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
+
+ virtual bool SupportsPartialTextureUpdate() override;
+
+ virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
+ {
+ if (!mGLContext)
+ return false;
+ int32_t maxSize = GetMaxTextureSize();
+ return aSize <= gfx::IntSize(maxSize, maxSize);
+ }
+
+ virtual int32_t GetMaxTextureSize() const override;
+
+ /**
+ * Set the size of the EGL surface we're rendering to, if we're rendering to
+ * an EGL surface.
+ */
+ virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override;
+
+ virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
+ mRenderOffset = aOffset;
+ }
+
+ virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override;
+
+#ifdef MOZ_DUMP_PAINTING
+ virtual const char* Name() const override { return "OGL"; }
+#endif // MOZ_DUMP_PAINTING
+
+ virtual LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_OPENGL;
+ }
+
+ virtual void Pause() override;
+ virtual bool Resume() override;
+
+ virtual bool HasImageHostOverlays() override
+ {
+ return false;
+ }
+
+ virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) override
+ {
+ }
+
+ virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) override
+ {
+ }
+
+ GLContext* gl() const { return mGLContext; }
+ /**
+ * Clear the program state. This must be called
+ * before operating on the GLContext directly. */
+ void ResetProgram();
+
+ gfx::SurfaceFormat GetFBOFormat() const {
+ return gfx::SurfaceFormat::R8G8B8A8;
+ }
+
+ GLBlitTextureImageHelper* BlitTextureImageHelper();
+
+ /**
+ * The compositor provides with temporary textures for use with direct
+ * textruing like gralloc texture.
+ * Doing so lets us use gralloc the way it has been designed to be used
+ * (see https://wiki.mozilla.org/Platform/GFX/Gralloc)
+ */
+ GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit);
+
+ const gfx::Matrix4x4& GetProjMatrix() const {
+ return mProjMatrix;
+ }
+
+ void SetProjMatrix(const gfx::Matrix4x4& aProjMatrix) {
+ mProjMatrix = aProjMatrix;
+ }
+
+ const gfx::IntSize GetDestinationSurfaceSize() const {
+ return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height);
+ }
+
+ const ScreenPoint& GetScreenRenderOffset() const {
+ return mRenderOffset;
+ }
+
+private:
+ template<typename Geometry>
+ void DrawGeometry(const Geometry& aGeometry,
+ const gfx::IntRect& aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect);
+
+ void PrepareViewport(CompositingRenderTargetOGL *aRenderTarget);
+
+ /** Widget associated with this compositor */
+ LayoutDeviceIntSize mWidgetSize;
+ RefPtr<GLContext> mGLContext;
+ UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
+ gfx::Matrix4x4 mProjMatrix;
+
+ /** The size of the surface we are rendering to */
+ gfx::IntSize mSurfaceSize;
+
+ ScreenPoint mRenderOffset;
+
+ already_AddRefed<mozilla::gl::GLContext> CreateContext();
+
+ /** Texture target to use for FBOs */
+ GLenum mFBOTextureTarget;
+
+ /** Currently bound render target */
+ RefPtr<CompositingRenderTargetOGL> mCurrentRenderTarget;
+#ifdef DEBUG
+ CompositingRenderTargetOGL* mWindowRenderTarget;
+#endif
+
+ /**
+ * VBO that has some basics in it for a textured quad, including vertex
+ * coords and texcoords.
+ */
+ GLuint mQuadVBO;
+
+
+ /**
+ * VBO that stores dynamic triangle geometry.
+ */
+ GLuint mTriangleVBO;
+
+ bool mHasBGRA;
+
+ /**
+ * When rendering to some EGL surfaces (e.g. on Android), we rely on being told
+ * about size changes (via SetSurfaceSize) rather than pulling this information
+ * from the widget.
+ */
+ bool mUseExternalSurfaceSize;
+
+ /**
+ * Have we had DrawQuad calls since the last frame was rendered?
+ */
+ bool mFrameInProgress;
+
+ /*
+ * Clear aRect on current render target.
+ */
+ virtual void ClearRect(const gfx::Rect& aRect) override;
+
+ /* Start a new frame. If aClipRectIn is null and aClipRectOut is non-null,
+ * sets *aClipRectOut to the screen dimensions.
+ */
+ virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+ const gfx::IntRect *aClipRectIn,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ gfx::IntRect *aClipRectOut = nullptr,
+ gfx::IntRect *aRenderBoundsOut = nullptr) override;
+
+ ShaderConfigOGL GetShaderConfigFor(Effect *aEffect,
+ MaskType aMask = MaskType::MaskNone,
+ gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER,
+ bool aColorMatrix = false,
+ bool aDEAAEnabled = false) const;
+
+ ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig);
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+ const gfx::Rect&)
+ {
+ aConfig.SetDynamicGeometry(false);
+ }
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+ const gfx::TexturedTriangle&)
+ {
+ aConfig.SetDynamicGeometry(true);
+ }
+
+ /**
+ * Create a FBO backed by a texture.
+ * Note that the texture target type will be
+ * of the type returned by FBOTextureTarget; different
+ * shaders are required to sample from the different
+ * texture types.
+ */
+ void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ GLuint *aFBO, GLuint *aTexture,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ gfx::Point3D GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2);
+
+ void ActivateProgram(ShaderProgramOGL *aProg);
+
+ void CleanupResources();
+
+ void BindAndDrawQuads(ShaderProgramOGL *aProg,
+ int aQuads,
+ const gfx::Rect* aLayerRect,
+ const gfx::Rect* aTextureRect);
+
+ void BindAndDrawQuad(ShaderProgramOGL *aProg,
+ const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f))
+ {
+ gfx::Rect layerRects[4];
+ gfx::Rect textureRects[4];
+ layerRects[0] = aLayerRect;
+ textureRects[0] = aTextureRect;
+ BindAndDrawQuads(aProg, 1, layerRects, textureRects);
+ }
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTextureRect =
+ gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture);
+
+ void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg,
+ const gfx::TexturedTriangle& aTriangle,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource *aTexture);
+
+ void InitializeVAO(const GLuint aAttribIndex, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset);
+
+ gfx::Rect GetTextureCoordinates(gfx::Rect textureRect,
+ TextureSource* aTexture);
+
+ /**
+ * Bind the texture behind the current render target as the backdrop for a
+ * mix-blend shader.
+ */
+ void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
+
+ /**
+ * Copies the content of our backbuffer to the set transaction target.
+ * Does not restore the target FBO, so only call from EndFrame.
+ */
+ void CopyToTarget(gfx::DrawTarget* aTarget, const nsIntPoint& aTopLeft, const gfx::Matrix& aWorldMatrix);
+
+ /**
+ * Implements the flipping of the y-axis to convert from layers/compositor
+ * coordinates to OpenGL coordinates.
+ *
+ * Indeed, the only coordinate system that OpenGL knows has the y-axis
+ * pointing upwards, but the layers/compositor coordinate system has the
+ * y-axis pointing downwards, for good reason as Web pages are typically
+ * scrolled downwards. So, some flipping has to take place; FlippedY does it.
+ */
+ GLint FlipY(GLint y) const { return mViewportSize.height - y; }
+
+ RefPtr<CompositorTexturePoolOGL> mTexturePool;
+
+ ContextStateTrackerOGL mContextStateTracker;
+
+ bool mDestroyed;
+
+ /**
+ * Size of the OpenGL context's primary framebuffer in pixels. Used by
+ * FlipY for the y-flipping calculation and by the DEAA shader.
+ */
+ gfx::IntSize mViewportSize;
+
+ ShaderProgramOGL *mCurrentProgram;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COMPOSITOROGL_H */
diff --git a/gfx/layers/opengl/EGLImageHelpers.cpp b/gfx/layers/opengl/EGLImageHelpers.cpp
new file mode 100644
index 000000000..4a720302f
--- /dev/null
+++ b/gfx/layers/opengl/EGLImageHelpers.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "EGLImageHelpers.h"
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla
+{
+namespace layers {
+
+using namespace gl;
+
+EGLImage
+EGLImageCreateFromNativeBuffer(GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize)
+{
+ EGLint attrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
+ LOCAL_EGL_NONE, LOCAL_EGL_NONE,
+ };
+
+ EGLint cropAttrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
+ LOCAL_EGL_IMAGE_CROP_LEFT_ANDROID, 0,
+ LOCAL_EGL_IMAGE_CROP_TOP_ANDROID, 0,
+ LOCAL_EGL_IMAGE_CROP_RIGHT_ANDROID, aCropSize.width,
+ LOCAL_EGL_IMAGE_CROP_BOTTOM_ANDROID, aCropSize.height,
+ LOCAL_EGL_NONE, LOCAL_EGL_NONE,
+ };
+
+ bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0);
+ EGLint* usedAttrs = attrs;
+ if (hasCropRect && sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) {
+ usedAttrs = cropAttrs;
+ }
+
+ return sEGLLibrary.fCreateImage(sEGLLibrary.Display(),
+ EGL_NO_CONTEXT,
+ LOCAL_EGL_NATIVE_BUFFER_ANDROID,
+ aBuffer, usedAttrs);
+}
+
+void
+EGLImageDestroy(GLContext* aGL, EGLImage aImage)
+{
+ sEGLLibrary.fDestroyImage(sEGLLibrary.Display(), aImage);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/EGLImageHelpers.h b/gfx/layers/opengl/EGLImageHelpers.h
new file mode 100644
index 000000000..3e03a499c
--- /dev/null
+++ b/gfx/layers/opengl/EGLImageHelpers.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef EGLIMAGEHELPERS_H_
+#define EGLIMAGEHELPERS_H_
+
+#include "mozilla/gfx/Point.h"
+
+typedef void* EGLImage;
+
+namespace mozilla {
+namespace gl {
+ class GLContext;
+}
+
+namespace layers {
+
+EGLImage EGLImageCreateFromNativeBuffer(gl::GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize);
+void EGLImageDestroy(gl::GLContext* aGL, EGLImage aImage);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // EGLIMAGEHELPERS_H_
diff --git a/gfx/layers/opengl/GLBlitTextureImageHelper.cpp b/gfx/layers/opengl/GLBlitTextureImageHelper.cpp
new file mode 100644
index 000000000..8c6ac1703
--- /dev/null
+++ b/gfx/layers/opengl/GLBlitTextureImageHelper.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLBlitTextureImageHelper.h"
+#include "GLUploadHelpers.h"
+#include "DecomposeIntoNoRepeatTriangles.h"
+#include "GLContext.h"
+#include "GLTextureImage.h"
+#include "ScopedGLHelpers.h"
+#include "nsRect.h"
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+#include "CompositorOGL.h"
+#include "mozilla/gfx/Point.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+GLBlitTextureImageHelper::GLBlitTextureImageHelper(CompositorOGL* aCompositor)
+ : mCompositor(aCompositor)
+ , mBlitProgram(0)
+ , mBlitFramebuffer(0)
+
+{
+}
+
+GLBlitTextureImageHelper::~GLBlitTextureImageHelper()
+{
+ GLContext *gl = mCompositor->gl();
+ // Likely used by OGL Layers.
+ gl->fDeleteProgram(mBlitProgram);
+ gl->fDeleteFramebuffers(1, &mBlitFramebuffer);
+}
+
+void
+GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const gfx::IntRect& aSrcRect,
+ TextureImage *aDst, const gfx::IntRect& aDstRect)
+{
+ GLContext *gl = mCompositor->gl();
+
+ if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty())
+ return;
+
+ int savedFb = 0;
+ gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb);
+
+ ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false);
+ ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false);
+
+ // 2.0 means scale up by two
+ float blitScaleX = float(aDstRect.width) / float(aSrcRect.width);
+ float blitScaleY = float(aDstRect.height) / float(aSrcRect.height);
+
+ // We start iterating over all destination tiles
+ aDst->BeginBigImageIteration();
+ do {
+ // calculate portion of the tile that is going to be painted to
+ gfx::IntRect dstSubRect;
+ gfx::IntRect dstTextureRect = aDst->GetTileRect();
+ dstSubRect.IntersectRect(aDstRect, dstTextureRect);
+
+ // this tile is not part of the destination rectangle aDstRect
+ if (dstSubRect.IsEmpty())
+ continue;
+
+ // (*) transform the rect of this tile into the rectangle defined by aSrcRect...
+ gfx::IntRect dstInSrcRect(dstSubRect);
+ dstInSrcRect.MoveBy(-aDstRect.TopLeft());
+ // ...which might be of different size, hence scale accordingly
+ dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY);
+ dstInSrcRect.MoveBy(aSrcRect.TopLeft());
+
+ SetBlitFramebufferForDestTexture(aDst->GetTextureID());
+ UseBlitProgram();
+
+ aSrc->BeginBigImageIteration();
+ // now iterate over all tiles in the source Image...
+ do {
+ // calculate portion of the source tile that is in the source rect
+ gfx::IntRect srcSubRect;
+ gfx::IntRect srcTextureRect = aSrc->GetTileRect();
+ srcSubRect.IntersectRect(aSrcRect, srcTextureRect);
+
+ // this tile is not part of the source rect
+ if (srcSubRect.IsEmpty()) {
+ continue;
+ }
+ // calculate intersection of source rect with destination rect
+ srcSubRect.IntersectRect(srcSubRect, dstInSrcRect);
+ // this tile does not overlap the current destination tile
+ if (srcSubRect.IsEmpty()) {
+ continue;
+ }
+ // We now have the intersection of
+ // the current source tile
+ // and the desired source rectangle
+ // and the destination tile
+ // and the desired destination rectange
+ // in destination space.
+ // We need to transform this back into destination space, inverting the transform from (*)
+ gfx::IntRect srcSubInDstRect(srcSubRect);
+ srcSubInDstRect.MoveBy(-aSrcRect.TopLeft());
+ srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY);
+ srcSubInDstRect.MoveBy(aDstRect.TopLeft());
+
+ // we transform these rectangles to be relative to the current src and dst tiles, respectively
+ gfx::IntSize srcSize = srcTextureRect.Size();
+ gfx::IntSize dstSize = dstTextureRect.Size();
+ srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y);
+ srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y);
+
+ float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f;
+ float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f;
+ float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
+ float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
+ ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height);
+
+ RectTriangles rects;
+
+ gfx::IntSize realTexSize = srcSize;
+ if (!CanUploadNonPowerOfTwo(gl)) {
+ realTexSize = gfx::IntSize(RoundUpPow2(srcSize.width),
+ RoundUpPow2(srcSize.height));
+ }
+
+ if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
+ rects.addRect(/* dest rectangle */
+ dx0, dy0, dx1, dy1,
+ /* tex coords */
+ srcSubRect.x / float(realTexSize.width),
+ srcSubRect.y / float(realTexSize.height),
+ srcSubRect.XMost() / float(realTexSize.width),
+ srcSubRect.YMost() / float(realTexSize.height));
+ } else {
+ DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);
+
+ // now put the coords into the d[xy]0 .. d[xy]1 coordinate space
+ // from the 0..1 that it comes out of decompose
+ InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords();
+
+ for (unsigned int i = 0; i < coords.Length(); ++i) {
+ coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0;
+ coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0;
+ }
+ }
+
+ ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0);
+ ScopedBindTexture autoTex(gl, aSrc->GetTextureID());
+ ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements());
+ ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements());
+
+ gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
+
+ } while (aSrc->NextTile());
+ } while (aDst->NextTile());
+
+ // unbind the previous texture from the framebuffer
+ SetBlitFramebufferForDestTexture(0);
+
+ gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb);
+}
+
+void
+GLBlitTextureImageHelper::SetBlitFramebufferForDestTexture(GLuint aTexture)
+{
+ GLContext *gl = mCompositor->gl();
+ if (!mBlitFramebuffer) {
+ gl->fGenFramebuffers(1, &mBlitFramebuffer);
+ }
+
+ gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer);
+ gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_TEXTURE_2D,
+ aTexture,
+ 0);
+
+ GLenum result = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
+ nsAutoCString msg;
+ msg.AppendLiteral("Framebuffer not complete -- error 0x");
+ msg.AppendInt(result, 16);
+ // Note: if you are hitting this, it is likely that
+ // your texture is not texture complete -- that is, you
+ // allocated a texture name, but didn't actually define its
+ // size via a call to TexImage2D.
+ NS_RUNTIMEABORT(msg.get());
+ }
+}
+
+void
+GLBlitTextureImageHelper::UseBlitProgram()
+{
+ // XXX: GLBlitTextureImageHelper doesn't use ShaderProgramOGL
+ // so we need to Reset the program
+ mCompositor->ResetProgram();
+
+ GLContext *gl = mCompositor->gl();
+ if (mBlitProgram) {
+ gl->fUseProgram(mBlitProgram);
+ return;
+ }
+
+ mBlitProgram = gl->fCreateProgram();
+
+ GLuint shaders[2];
+ shaders[0] = gl->fCreateShader(LOCAL_GL_VERTEX_SHADER);
+ shaders[1] = gl->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
+
+ const char *blitVSSrc =
+ "attribute vec2 aVertex;"
+ "attribute vec2 aTexCoord;"
+ "varying vec2 vTexCoord;"
+ "void main() {"
+ " vTexCoord = aTexCoord;"
+ " gl_Position = vec4(aVertex, 0.0, 1.0);"
+ "}";
+ const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n"
+ "uniform sampler2D uSrcTexture;"
+ "varying vec2 vTexCoord;"
+ "void main() {"
+ " gl_FragColor = texture2D(uSrcTexture, vTexCoord);"
+ "}";
+
+ gl->fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr);
+ gl->fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr);
+
+ for (int i = 0; i < 2; ++i) {
+ GLint success, len = 0;
+
+ gl->fCompileShader(shaders[i]);
+ gl->fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success);
+ NS_ASSERTION(success, "Shader compilation failed!");
+
+ if (!success) {
+ nsAutoCString log;
+ gl->fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ log.SetCapacity(len);
+ gl->fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ printf_stderr("Shader %d compilation failed:\n%s\n", i, log.get());
+ return;
+ }
+
+ gl->fAttachShader(mBlitProgram, shaders[i]);
+ gl->fDeleteShader(shaders[i]);
+ }
+
+ gl->fBindAttribLocation(mBlitProgram, 0, "aVertex");
+ gl->fBindAttribLocation(mBlitProgram, 1, "aTexCoord");
+
+ gl->fLinkProgram(mBlitProgram);
+
+ GLint success, len = 0;
+ gl->fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success);
+ NS_ASSERTION(success, "Shader linking failed!");
+
+ if (!success) {
+ nsAutoCString log;
+ gl->fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ log.SetCapacity(len);
+ gl->fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ printf_stderr("Program linking failed:\n%s\n", log.get());
+ return;
+ }
+
+ gl->fUseProgram(mBlitProgram);
+ gl->fUniform1i(gl->fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/GLBlitTextureImageHelper.h b/gfx/layers/opengl/GLBlitTextureImageHelper.h
new file mode 100644
index 000000000..91cb1bf85
--- /dev/null
+++ b/gfx/layers/opengl/GLBlitTextureImageHelper.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLBLITTEXTUREIMAGEHELPER_H_
+#define GLBLITTEXTUREIMAGEHELPER_H_
+
+#include "mozilla/Attributes.h"
+#include "GLContextTypes.h"
+#include "GLConsts.h"
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace gl {
+ class GLContext;
+ class TextureImage;
+} // namespace gl
+namespace layers {
+
+class CompositorOGL;
+
+class GLBlitTextureImageHelper final
+{
+ // The GLContext is the sole owner of the GLBlitTextureImageHelper.
+ CompositorOGL* mCompositor;
+
+ // lazy-initialized things
+ GLuint mBlitProgram, mBlitFramebuffer;
+ void UseBlitProgram();
+ void SetBlitFramebufferForDestTexture(GLuint aTexture);
+
+public:
+
+ explicit GLBlitTextureImageHelper(CompositorOGL *gl);
+ ~GLBlitTextureImageHelper();
+
+ /**
+ * Copy a rectangle from one TextureImage into another. The
+ * source and destination are given in integer coordinates, and
+ * will be converted to texture coordinates.
+ *
+ * For the source texture, the wrap modes DO apply -- it's valid
+ * to use REPEAT or PAD and expect appropriate behaviour if the source
+ * rectangle extends beyond its bounds.
+ *
+ * For the destination texture, the wrap modes DO NOT apply -- the
+ * destination will be clipped by the bounds of the texture.
+ *
+ * Note: calling this function will cause the following OpenGL state
+ * to be changed:
+ *
+ * - current program
+ * - framebuffer binding
+ * - viewport
+ * - blend state (will be enabled at end)
+ * - scissor state (will be enabled at end)
+ * - vertex attrib 0 and 1 (pointer and enable state [enable state will be disabled at exit])
+ * - array buffer binding (will be 0)
+ * - active texture (will be 0)
+ * - texture 0 binding
+ */
+ void BlitTextureImage(gl::TextureImage *aSrc, const gfx::IntRect& aSrcRect,
+ gl::TextureImage *aDst, const gfx::IntRect& aDstRect);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GLBLITTEXTUREIMAGEHELPER_H_
diff --git a/gfx/layers/opengl/GLManager.cpp b/gfx/layers/opengl/GLManager.cpp
new file mode 100644
index 000000000..e4c0b361f
--- /dev/null
+++ b/gfx/layers/opengl/GLManager.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLManager.h"
+#include "CompositorOGL.h" // for CompositorOGL
+#include "GLContext.h" // for GLContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+class GLManagerCompositor : public GLManager
+{
+public:
+ explicit GLManagerCompositor(CompositorOGL* aCompositor)
+ : mImpl(aCompositor)
+ {}
+
+ virtual GLContext* gl() const override
+ {
+ return mImpl->gl();
+ }
+
+ virtual void ActivateProgram(ShaderProgramOGL *aProg) override
+ {
+ mImpl->ActivateProgram(aProg);
+ }
+
+ virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) override
+ {
+ ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(aTarget, aFormat);
+ return mImpl->GetShaderProgramFor(config);
+ }
+
+ virtual const gfx::Matrix4x4& GetProjMatrix() const override
+ {
+ return mImpl->GetProjMatrix();
+ }
+
+ virtual void BindAndDrawQuad(ShaderProgramOGL *aProg,
+ const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect) override
+ {
+ mImpl->BindAndDrawQuad(aProg, aLayerRect, aTextureRect);
+ }
+
+private:
+ RefPtr<CompositorOGL> mImpl;
+};
+
+/* static */ GLManager*
+GLManager::CreateGLManager(LayerManagerComposite* aManager)
+{
+ if (aManager && aManager->GetCompositor()->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return new GLManagerCompositor(aManager->GetCompositor()->AsCompositorOGL());
+ }
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/GLManager.h b/gfx/layers/opengl/GLManager.h
new file mode 100644
index 000000000..7c872758e
--- /dev/null
+++ b/gfx/layers/opengl/GLManager.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GLMANAGER_H
+#define MOZILLA_GFX_GLMANAGER_H
+
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "OGLShaderProgram.h"
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+} // namespace gl
+
+namespace layers {
+
+class LayerManagerComposite;
+
+/**
+ * Minimal interface to allow widgets to draw using OpenGL. Abstracts
+ * CompositorOGL. Call CreateGLManager with a LayerManagerComposite
+ * backed by a CompositorOGL.
+ */
+class GLManager
+{
+public:
+ static GLManager* CreateGLManager(LayerManagerComposite* aManager);
+
+ virtual ~GLManager() {}
+
+ virtual gl::GLContext* gl() const = 0;
+ virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) = 0;
+ virtual void ActivateProgram(ShaderProgramOGL* aPrg) = 0;
+ virtual const gfx::Matrix4x4& GetProjMatrix() const = 0;
+ virtual void BindAndDrawQuad(ShaderProgramOGL *aProg, const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
new file mode 100644
index 000000000..dd522e650
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurfaceTextureClientOGL.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+MacIOSurfaceTextureData::MacIOSurfaceTextureData(MacIOSurface* aSurface,
+ BackendType aBackend)
+ : mSurface(aSurface)
+ , mBackend(aBackend)
+{
+ MOZ_ASSERT(mSurface);
+}
+
+MacIOSurfaceTextureData::~MacIOSurfaceTextureData()
+{}
+
+// static
+MacIOSurfaceTextureData*
+MacIOSurfaceTextureData::Create(MacIOSurface* aSurface, BackendType aBackend)
+{
+ MOZ_ASSERT(aSurface);
+ if (!aSurface) {
+ return nullptr;
+ }
+ return new MacIOSurfaceTextureData(aSurface, aBackend);
+}
+
+MacIOSurfaceTextureData*
+MacIOSurfaceTextureData::Create(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ BackendType aBackend)
+{
+ if (aFormat != SurfaceFormat::B8G8R8A8 &&
+ aFormat != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ RefPtr<MacIOSurface> surf = MacIOSurface::CreateIOSurface(aSize.width, aSize.height,
+ 1.0,
+ aFormat == SurfaceFormat::B8G8R8A8);
+ if (!surf) {
+ return nullptr;
+ }
+
+ return Create(surf, aBackend);
+}
+
+bool
+MacIOSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorMacIOSurface(mSurface->GetIOSurfaceID(),
+ mSurface->GetContentsScaleFactor(),
+ !mSurface->HasAlpha());
+ return true;
+}
+
+void
+MacIOSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
+ aInfo.format = mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+MacIOSurfaceTextureData::Lock(OpenMode)
+{
+ mSurface->Lock(false);
+ return true;
+}
+
+void
+MacIOSurfaceTextureData::Unlock()
+{
+ mSurface->Unlock(false);
+}
+
+already_AddRefed<DataSourceSurface>
+MacIOSurfaceTextureData::GetAsSurface()
+{
+ RefPtr<SourceSurface> surf = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ return surf->GetDataSurface();
+}
+
+already_AddRefed<DrawTarget>
+MacIOSurfaceTextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mBackend != BackendType::NONE);
+ if (mBackend == BackendType::NONE) {
+ // shouldn't happen, but degrade gracefully
+ return nullptr;
+ }
+ return Factory::CreateDrawTargetForData(
+ mBackend,
+ (unsigned char*)mSurface->GetBaseAddress(),
+ IntSize(mSurface->GetWidth(), mSurface->GetHeight()),
+ mSurface->GetBytesPerRow(),
+ mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8,
+ true);
+}
+
+void
+MacIOSurfaceTextureData::Deallocate(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+void
+MacIOSurfaceTextureData::Forget(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+bool
+MacIOSurfaceTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
new file mode 100644
index 000000000..21e445953
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+
+#include "mozilla/layers/TextureClientOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class MacIOSurfaceTextureData : public TextureData
+{
+public:
+ static MacIOSurfaceTextureData* Create(MacIOSurface* aSurface,
+ gfx::BackendType aBackend);
+
+ static MacIOSurfaceTextureData*
+ Create(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend);
+
+ ~MacIOSurfaceTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual void Forget(LayersIPCChannel*) override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ // For debugging purposes only.
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+protected:
+ MacIOSurfaceTextureData(MacIOSurface* aSurface,
+ gfx::BackendType aBackend);
+
+ RefPtr<MacIOSurface> mSurface;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
new file mode 100644
index 000000000..05f8cf38f
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurfaceTextureHostOGL.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "GLContextCGL.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureHostOGL::MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor)
+ : TextureHost(aFlags)
+{
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureHostOGL);
+ mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(),
+ aDescriptor.scaleFactor(),
+ !aDescriptor.isOpaque());
+}
+
+MacIOSurfaceTextureHostOGL::~MacIOSurfaceTextureHostOGL()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureHostOGL);
+}
+
+GLTextureSource*
+MacIOSurfaceTextureHostOGL::CreateTextureSourceForPlane(size_t aPlane)
+{
+ GLuint textureHandle;
+ gl::GLContext* gl = mCompositor->gl();
+ gl->fGenTextures(1, &textureHandle);
+ gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+
+ mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext(), aPlane);
+
+ return new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ gfx::IntSize(mSurface->GetDevicePixelWidth(aPlane),
+ mSurface->GetDevicePixelHeight(aPlane)),
+ // XXX: This isn't really correct (but isn't used), we should be using the
+ // format of the individual plane, not of the whole buffer.
+ mSurface->GetFormat());
+}
+
+bool
+MacIOSurfaceTextureHostOGL::Lock()
+{
+ if (!gl() || !gl()->MakeCurrent() || !mSurface) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = CreateTextureSourceForPlane(0);
+
+ RefPtr<TextureSource> prev = mTextureSource;
+ for (size_t i = 1; i < mSurface->GetPlaneCount(); i++) {
+ RefPtr<TextureSource> next = CreateTextureSourceForPlane(i);
+ prev->SetNextSibling(next);
+ prev = next;
+ }
+ }
+ return true;
+}
+
+void
+MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mTextureSource = nullptr;
+ mCompositor = nullptr;
+ return;
+ }
+
+ if (mCompositor != glCompositor) {
+ // Cannot share GL texture identifiers across compositors.
+ mTextureSource = nullptr;
+ }
+ mCompositor = glCompositor;
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostOGL::GetFormat() const {
+ return mSurface->GetFormat();
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostOGL::GetReadFormat() const {
+ return mSurface->GetReadFormat();
+}
+
+gfx::IntSize
+MacIOSurfaceTextureHostOGL::GetSize() const {
+ if (!mSurface) {
+ return gfx::IntSize();
+ }
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gl::GLContext*
+MacIOSurfaceTextureHostOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+MacIOSurfaceTextureSourceOGL::MacIOSurfaceTextureSourceOGL(
+ CompositorOGL* aCompositor,
+ MacIOSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+{
+ MOZ_ASSERT(aCompositor);
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceOGL);
+}
+
+MacIOSurfaceTextureSourceOGL::~MacIOSurfaceTextureSourceOGL()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceOGL);
+}
+
+gfx::IntSize
+MacIOSurfaceTextureSourceOGL::GetSize() const
+{
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureSourceOGL::GetFormat() const
+{
+ return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+}
+
+void
+MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ gl::GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("Trying to bind a texture without a working GLContext");
+ return;
+ }
+ GLuint tex = mCompositor->GetTemporaryTexture(GetTextureTarget(), aTextureUnit);
+
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
+ mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+}
+
+void
+MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertGLCompositor(aCompositor);
+ if (mCompositor && mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+gl::GLContext*
+MacIOSurfaceTextureSourceOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
new file mode 100644
index 000000000..55e2f5019
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A texture source meant for use with MacIOSurfaceTextureHostOGL.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ */
+class MacIOSurfaceTextureSourceOGL : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ MacIOSurfaceTextureSourceOGL(CompositorOGL* aCompositor,
+ MacIOSurface* aSurface);
+ virtual ~MacIOSurfaceTextureSourceOGL();
+
+ virtual const char* Name() const override { return "MacIOSurfaceTextureSourceOGL"; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override { return !!gl(); }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_RECTANGLE_ARB; }
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ // MacIOSurfaceTextureSourceOGL doesn't own any gl texture
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ gl::GLContext* gl() const;
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceOGL.
+ */
+class MacIOSurfaceTextureHostOGL : public TextureHost
+{
+public:
+ MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor);
+ virtual ~MacIOSurfaceTextureHostOGL();
+
+ // MacIOSurfaceTextureSourceOGL doesn't own any GL texture
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+ virtual gfx::SurfaceFormat GetReadFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
+#endif
+
+protected:
+ GLTextureSource* CreateTextureSourceForPlane(size_t aPlane);
+
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLTextureSource> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp
new file mode 100644
index 000000000..c06dc52dd
--- /dev/null
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -0,0 +1,974 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OGLShaderProgram.h"
+#include <stdint.h> // for uint32_t
+#include <sstream> // for ostringstream
+#include "gfxEnv.h"
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h"
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/Compositor.h" // for BlendOpIsMixBlendMode
+#include "nsAString.h"
+#include "nsString.h" // for nsAutoCString
+#include "Layers.h"
+#include "GLContext.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace std;
+
+#define GAUSSIAN_KERNEL_HALF_WIDTH 11
+#define GAUSSIAN_KERNEL_STEP 0.2
+
+void
+AddUniforms(ProgramProfileOGL& aProfile)
+{
+ // This needs to be kept in sync with the KnownUniformName enum
+ static const char *sKnownUniformNames[] = {
+ "uLayerTransform",
+ "uLayerTransformInverse",
+ "uMaskTransform",
+ "uBackdropTransform",
+ "uLayerRects",
+ "uMatrixProj",
+ "uTextureTransform",
+ "uTextureRects",
+ "uRenderTargetOffset",
+ "uLayerOpacity",
+ "uTexture",
+ "uYTexture",
+ "uCbTexture",
+ "uCrTexture",
+ "uBlackTexture",
+ "uWhiteTexture",
+ "uMaskTexture",
+ "uBackdropTexture",
+ "uRenderColor",
+ "uTexCoordMultiplier",
+ "uCbCrTexCoordMultiplier",
+ "uTexturePass2",
+ "uColorMatrix",
+ "uColorMatrixVector",
+ "uBlurRadius",
+ "uBlurOffset",
+ "uBlurAlpha",
+ "uBlurGaussianKernel",
+ "uSSEdges",
+ "uViewportSize",
+ "uVisibleCenter",
+ "uYuvColorMatrix",
+ nullptr
+ };
+
+ for (int i = 0; sKnownUniformNames[i] != nullptr; ++i) {
+ aProfile.mUniforms[i].mNameString = sKnownUniformNames[i];
+ aProfile.mUniforms[i].mName = (KnownUniform::KnownUniformName) i;
+ }
+}
+
+void
+ShaderConfigOGL::SetRenderColor(bool aEnabled)
+{
+ SetFeature(ENABLE_RENDER_COLOR, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetTextureTarget(GLenum aTarget)
+{
+ SetFeature(ENABLE_TEXTURE_EXTERNAL | ENABLE_TEXTURE_RECT, false);
+ switch (aTarget) {
+ case LOCAL_GL_TEXTURE_EXTERNAL:
+ SetFeature(ENABLE_TEXTURE_EXTERNAL, true);
+ break;
+ case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+ SetFeature(ENABLE_TEXTURE_RECT, true);
+ break;
+ }
+}
+
+void
+ShaderConfigOGL::SetRBSwap(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_RB_SWAP, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetNoAlpha(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_NO_ALPHA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetOpacity(bool aEnabled)
+{
+ SetFeature(ENABLE_OPACITY, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetYCbCr(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_NV12));
+}
+
+void
+ShaderConfigOGL::SetNV12(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_NV12, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR));
+}
+
+void
+ShaderConfigOGL::SetComponentAlpha(bool aEnabled)
+{
+ SetFeature(ENABLE_TEXTURE_COMPONENT_ALPHA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetColorMatrix(bool aEnabled)
+{
+ SetFeature(ENABLE_COLOR_MATRIX, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetBlur(bool aEnabled)
+{
+ SetFeature(ENABLE_BLUR, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetMask(bool aEnabled)
+{
+ SetFeature(ENABLE_MASK, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetNoPremultipliedAlpha()
+{
+ SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
+}
+
+void
+ShaderConfigOGL::SetDEAA(bool aEnabled)
+{
+ SetFeature(ENABLE_DEAA, aEnabled);
+}
+
+void
+ShaderConfigOGL::SetCompositionOp(gfx::CompositionOp aOp)
+{
+ mCompositionOp = aOp;
+}
+
+void
+ShaderConfigOGL::SetDynamicGeometry(bool aEnabled)
+{
+ SetFeature(ENABLE_DYNAMIC_GEOMETRY, aEnabled);
+}
+
+/* static */ ProgramProfileOGL
+ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
+{
+ ProgramProfileOGL result;
+ ostringstream fs, vs;
+
+ AddUniforms(result);
+
+ gfx::CompositionOp blendOp = aConfig.mCompositionOp;
+
+ vs << "#ifdef GL_ES" << endl;
+ vs << "#define EDGE_PRECISION mediump" << endl;
+ vs << "#else" << endl;
+ vs << "#define EDGE_PRECISION" << endl;
+ vs << "#endif" << endl;
+ vs << "uniform mat4 uMatrixProj;" << endl;
+ vs << "uniform vec4 uLayerRects[4];" << endl;
+ vs << "uniform mat4 uLayerTransform;" << endl;
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ vs << "uniform mat4 uLayerTransformInverse;" << endl;
+ vs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ vs << "uniform vec2 uVisibleCenter;" << endl;
+ vs << "uniform vec2 uViewportSize;" << endl;
+ }
+ vs << "uniform vec2 uRenderTargetOffset;" << endl;
+ vs << "attribute vec4 aCoord;" << endl;
+ result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aCoord", 0});
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << "uniform mat4 uTextureTransform;" << endl;
+ vs << "uniform vec4 uTextureRects[4];" << endl;
+ vs << "varying vec2 vTexCoord;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << "attribute vec2 aTexCoord;" << endl;
+ result.mAttributes.AppendElement(Pair<nsCString, GLuint> {"aTexCoord", 1});
+ }
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ vs << "uniform mat4 uBackdropTransform;" << endl;
+ vs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << "uniform mat4 uMaskTransform;" << endl;
+ vs << "varying vec3 vMaskCoord;" << endl;
+ }
+
+ vs << "void main() {" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vec4 finalPosition = vec4(aCoord.xy, 0.0, 1.0);" << endl;
+ } else {
+ vs << " int vertexID = int(aCoord.w);" << endl;
+ vs << " vec4 layerRect = uLayerRects[vertexID];" << endl;
+ vs << " vec4 finalPosition = vec4(aCoord.xy * layerRect.zw + layerRect.xy, 0.0, 1.0);" << endl;
+ }
+
+ vs << " finalPosition = uLayerTransform * finalPosition;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // XXX kip - The DEAA shader could be made simpler if we switch to
+ // using dynamic vertex buffers instead of sending everything
+ // in through uniforms. This would enable passing information
+ // about how to dilate each vertex explicitly and eliminate the
+ // need to extrapolate this with the sub-pixel coverage
+ // calculation in the vertex shader.
+
+ // Calculate the screen space position of this vertex, in screen pixels
+ vs << " vec4 ssPos = finalPosition;" << endl;
+ vs << " ssPos.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " ssPos = uMatrixProj * ssPos;" << endl;
+ vs << " ssPos.xy = ((ssPos.xy/ssPos.w)*0.5+0.5)*uViewportSize;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_MASK ||
+ !(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << " vec4 coordAdjusted;" << endl;
+ vs << " coordAdjusted.xy = aCoord.xy;" << endl;
+ }
+
+ // It is necessary to dilate edges away from uVisibleCenter to ensure that
+ // fragments with less than 50% sub-pixel coverage will be shaded.
+ // This offset is applied when the sub-pixel coverage of the vertex is
+ // less than 100%. Expanding by 0.5 pixels in screen space is sufficient
+ // to include these pixels.
+ vs << " if (dot(uSSEdges[0], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[1], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[2], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[3], vec3(ssPos.xy, 1.0)) < 1.5) {" << endl;
+ // If the shader reaches this branch, then this vertex is on the edge of
+ // the layer's visible rect and should be dilated away from the center of
+ // the visible rect. We don't want to hit this for inner facing
+ // edges between tiles, as the pixels may be covered twice without clipping
+ // against uSSEdges. If all edges were dilated, it would result in
+ // artifacts visible within semi-transparent layers with multiple tiles.
+ vs << " vec4 visibleCenter = uLayerTransform * vec4(uVisibleCenter, 0.0, 1.0);" << endl;
+ vs << " vec2 dilateDir = finalPosition.xy / finalPosition.w - visibleCenter.xy / visibleCenter.w;" << endl;
+ vs << " vec2 offset = sign(dilateDir) * 0.5;" << endl;
+ vs << " finalPosition.xy += offset * finalPosition.w;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ // We must adjust the texture coordinates to compensate for the dilation
+ vs << " coordAdjusted = uLayerTransformInverse * finalPosition;" << endl;
+ vs << " coordAdjusted /= coordAdjusted.w;" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY)) {
+ vs << " coordAdjusted.xy -= layerRect.xy;" << endl;
+ vs << " coordAdjusted.xy /= layerRect.zw;" << endl;
+ }
+ }
+ vs << " }" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = coordAdjusted.xy * textureRect.zw + textureRect.xy;" << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
+ }
+ }
+ } else if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;" << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = aCoord.xy * textureRect.zw + textureRect.xy;" << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;" << endl;
+ }
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << " vMaskCoord.xy = (uMaskTransform * (finalPosition / finalPosition.w)).xy;" << endl;
+ // correct for perspective correct interpolation, see comment in D3D11 shader
+ vs << " vMaskCoord.z = 1.0;" << endl;
+ vs << " vMaskCoord *= finalPosition.w;" << endl;
+ }
+ vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Translate from clip space (-1, 1) to (0..1), apply the backdrop
+ // transform, then invert the y-axis.
+ vs << " vBackdropCoord.x = (finalPosition.x + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord.y = 1.0 - (finalPosition.y + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord = (uBackdropTransform * vec4(vBackdropCoord.xy, 0.0, 1.0)).xy;" << endl;
+ vs << " vBackdropCoord.y = 1.0 - vBackdropCoord.y;" << endl;
+ }
+ vs << " gl_Position = finalPosition;" << endl;
+ vs << "}" << endl;
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "#extension GL_ARB_texture_rectangle : require" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ fs << "#extension GL_OES_EGL_image_external : require" << endl;
+ }
+ fs << "#ifdef GL_ES" << endl;
+ fs << "precision mediump float;" << endl;
+ fs << "#define COLOR_PRECISION lowp" << endl;
+ fs << "#define EDGE_PRECISION mediump" << endl;
+ fs << "#else" << endl;
+ fs << "#define COLOR_PRECISION" << endl;
+ fs << "#define EDGE_PRECISION" << endl;
+ fs << "#endif" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << "uniform COLOR_PRECISION vec4 uRenderColor;" << endl;
+ } else {
+ // for tiling, texcoord can be greater than the lowfp range
+ fs << "varying vec2 vTexCoord;" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "uniform bool uBlurAlpha;" << endl;
+ fs << "uniform vec2 uBlurRadius;" << endl;
+ fs << "uniform vec2 uBlurOffset;" << endl;
+ fs << "uniform float uBlurGaussianKernel[" << GAUSSIAN_KERNEL_HALF_WIDTH << "];" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << "uniform mat4 uColorMatrix;" << endl;
+ fs << "uniform vec4 uColorMatrixVector;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
+ }
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ const char *sampler2D = "sampler2D";
+ const char *texture2D = "texture2D";
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "uniform vec2 uTexCoordMultiplier;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform vec2 uCbCrTexCoordMultiplier;" << endl;
+ }
+ sampler2D = "sampler2DRect";
+ texture2D = "texture2DRect";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ sampler2D = "samplerExternalOES";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ fs << "uniform sampler2D uYTexture;" << endl;
+ fs << "uniform sampler2D uCbTexture;" << endl;
+ fs << "uniform sampler2D uCrTexture;" << endl;
+ fs << "uniform mat3 uYuvColorMatrix;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform " << sampler2D << " uYTexture;" << endl;
+ fs << "uniform " << sampler2D << " uCbTexture;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ fs << "uniform " << sampler2D << " uBlackTexture;" << endl;
+ fs << "uniform " << sampler2D << " uWhiteTexture;" << endl;
+ fs << "uniform bool uTexturePass2;" << endl;
+ } else {
+ fs << "uniform " << sampler2D << " uTexture;" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Component alpha should be flattened away inside blend containers.
+ MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
+
+ fs << "uniform sampler2D uBackdropTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << "varying vec3 vMaskCoord;" << endl;
+ fs << "uniform sampler2D uMaskTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ BuildMixBlender(aConfig, fs);
+ }
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ fs << "vec4 sample(vec2 coord) {" << endl;
+ fs << " vec4 color;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = texture2D(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = texture2D(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cr = texture2D(uCrTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float y = texture2D(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = texture2D(uCbTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cr = texture2D(uCrTexture, coord).r;" << endl;
+ }
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).a;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord).a;" << endl;
+ }
+ }
+
+ fs << " y = y - 0.06275;" << endl;
+ fs << " cb = cb - 0.50196;" << endl;
+ fs << " cr = cr - 0.50196;" << endl;
+ fs << " vec3 yuv = vec3(y, cb, cr);" << endl;
+ fs << " color.rgb = uYuvColorMatrix * yuv;" << endl;
+ fs << " color.a = 1.0;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D << "(uWhiteTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ } else {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D << "(uWhiteTexture, coord).rgb;" << endl;
+ }
+ fs << " COLOR_PRECISION vec4 alphas = (1.0 - onWhite + onBlack).rgbg;" << endl;
+ fs << " if (uTexturePass2)" << endl;
+ fs << " color = vec4(onBlack, alphas.a);" << endl;
+ fs << " else" << endl;
+ fs << " color = alphas;" << endl;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " color = " << texture2D << "(uTexture, coord * uTexCoordMultiplier);" << endl;
+ } else {
+ fs << " color = " << texture2D << "(uTexture, coord);" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RB_SWAP) {
+ fs << " color = color.bgra;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_NO_ALPHA) {
+ fs << " color = vec4(color.rgb, 1.0);" << endl;
+ }
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "vec4 sampleAtRadius(vec2 coord, float radius) {" << endl;
+ fs << " coord += uBlurOffset;" << endl;
+ fs << " coord += radius * uBlurRadius;" << endl;
+ fs << " if (coord.x < 0. || coord.y < 0. || coord.x > 1. || coord.y > 1.)" << endl;
+ fs << " return vec4(0, 0, 0, 0);" << endl;
+ fs << " return sample(coord);" << endl;
+ fs << "}" << endl;
+ fs << "vec4 blur(vec4 color, vec2 coord) {" << endl;
+ fs << " vec4 total = color * uBlurGaussianKernel[0];" << endl;
+ fs << " for (int i = 1; i < " << GAUSSIAN_KERNEL_HALF_WIDTH << "; ++i) {" << endl;
+ fs << " float r = float(i) * " << GAUSSIAN_KERNEL_STEP << ";" << endl;
+ fs << " float k = uBlurGaussianKernel[i];" << endl;
+ fs << " total += sampleAtRadius(coord, r) * k;" << endl;
+ fs << " total += sampleAtRadius(coord, -r) * k;" << endl;
+ fs << " }" << endl;
+ fs << " if (uBlurAlpha) {" << endl;
+ fs << " color *= total.a;" << endl;
+ fs << " } else {" << endl;
+ fs << " color = total;" << endl;
+ fs << " }" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ }
+ }
+ fs << "void main() {" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << " vec4 color = uRenderColor;" << endl;
+ } else {
+ fs << " vec4 color = sample(vTexCoord);" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << " color = blur(color, vTexCoord);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << " color = uColorMatrix * vec4(color.rgb / color.a, color.a) + uColorMatrixVector;" << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << " color *= uLayerOpacity;" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // Calculate the sub-pixel coverage of the pixel and modulate its opacity
+ // by that amount to perform DEAA.
+ fs << " vec3 ssPos = vec3(gl_FragCoord.xy, 1.0);" << endl;
+ fs << " float deaaCoverage = clamp(dot(uSSEdges[0], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[1], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[2], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
+ fs << " color *= deaaCoverage;" << endl;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);" << endl;
+ fs << " color = mixAndBlend(backdrop, color);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
+ fs << " COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
+ fs << " color *= mask;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float mask = 1.0;" << endl;
+ fs << " color *= mask;" << endl;
+ }
+ fs << " gl_FragColor = color;" << endl;
+ fs << "}" << endl;
+
+ result.mVertexShaderString = vs.str();
+ result.mFragmentShaderString = fs.str();
+
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ result.mTextureCount = 0;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ result.mTextureCount = 3;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ result.mTextureCount = 2;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ result.mTextureCount = 2;
+ } else {
+ result.mTextureCount = 1;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ result.mTextureCount = 1;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ result.mTextureCount += 1;
+ }
+
+ return result;
+}
+
+void
+ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs)
+{
+ // From the "Compositing and Blending Level 1" spec.
+ // Generate helper functions first.
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_OVERLAY:
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ // Note: we substitute (2*src-1) into the screen formula below.
+ fs << "float hardlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest * (2.0 * src);" << endl;
+ fs << " } else {" << endl;
+ fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << "float dodge(float dest, float src) {" << endl;
+ fs << " if (dest == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else if (src == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return min(1.0, dest / (1.0 - src));" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << "float burn(float dest, float src) {" << endl;
+ fs << " if (dest == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else if (src == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << "float darken(float dest) {" << endl;
+ fs << " if (dest <= 0.25) {" << endl;
+ fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
+ fs << " } else {" << endl;
+ fs << " return sqrt(dest);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ fs << "float softlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);" << endl;
+ fs << " } else {" << endl;
+ fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ case gfx::CompositionOp::OP_SATURATION:
+ case gfx::CompositionOp::OP_COLOR:
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << "float Lum(vec3 c) {" << endl;
+ fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
+ fs << "}" << endl;
+ fs << "vec3 ClipColor(vec3 c) {" << endl;
+ fs << " float L = Lum(c);" << endl;
+ fs << " float n = min(min(c.r, c.g), c.b);" << endl;
+ fs << " float x = max(max(c.r, c.g), c.b);" << endl;
+ fs << " if (n < 0.0) {" << endl;
+ fs << " c = L + (((c - L) * L) / (L - n));" << endl;
+ fs << " }" << endl;
+ fs << " if (x > 1.0) {" << endl;
+ fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ fs << "vec3 SetLum(vec3 c, float L) {" << endl;
+ fs << " float d = L - Lum(c);" << endl;
+ fs << " return ClipColor(vec3(" << endl;
+ fs << " c.r + d," << endl;
+ fs << " c.g + d," << endl;
+ fs << " c.b + d));" << endl;
+ fs << "}" << endl;
+ fs << "float Sat(vec3 c) {" << endl;
+ fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);" << endl;
+ fs << "}" << endl;
+
+ // To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+ fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
+ fs << " if (c.b > c.r) {" << endl;
+ fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
+ fs << " c.b = s;" << endl;
+ fs << " } else {" << endl;
+ fs << " c.gb = vec2(0.0, 0.0);" << endl;
+ fs << " }" << endl;
+ fs << " return vec3(0.0, c.gb);" << endl;
+ fs << "}" << endl;
+
+ fs << "vec3 SetSat(vec3 c, float s) {" << endl;
+ fs << " if (c.r <= c.g) {" << endl;
+ fs << " if (c.g <= c.b) {" << endl;
+ fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.brg = SetSatInner(c.brg, s);" << endl;
+ fs << " }" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.grb = SetSatInner(c.grb, s);" << endl;
+ fs << " } else if (c.g <= c.b) {" << endl;
+ fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ break;
+ default:
+ break;
+ }
+
+ // Generate the main blending helper.
+ fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ fs << " return dest * src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_SCREEN:
+ fs << " return dest + src - (dest * src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_OVERLAY:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(src.r, dest.r)," << endl;
+ fs << " hardlight(src.g, dest.g)," << endl;
+ fs << " hardlight(src.b, dest.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DARKEN:
+ fs << " return min(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_LIGHTEN:
+ fs << " return max(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << " return vec3(" << endl;
+ fs << " dodge(dest.r, src.r)," << endl;
+ fs << " dodge(dest.g, src.g)," << endl;
+ fs << " dodge(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << " return vec3(" << endl;
+ fs << " burn(dest.r, src.r)," << endl;
+ fs << " burn(dest.g, src.g)," << endl;
+ fs << " burn(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(dest.r, src.r)," << endl;
+ fs << " hardlight(dest.g, src.g)," << endl;
+ fs << " hardlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " softlight(dest.r, src.r)," << endl;
+ fs << " softlight(dest.g, src.g)," << endl;
+ fs << " softlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ fs << " return abs(dest - src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_EXCLUSION:
+ fs << " return dest + src - 2.0*dest*src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SATURATION:
+ fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR:
+ fs << " return SetLum(src, Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << " return SetLum(dest, Lum(src));" << endl;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown blend mode");
+ }
+ fs << "}" << endl;
+
+ // Generate the mix-blend function the fragment shader will call.
+ fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // Infinity into the blend function and return incorrect results.
+ fs << " if (backdrop.a == 0.0) {" << endl;
+ fs << " return color;" << endl;
+ fs << " }" << endl;
+ fs << " if (color.a == 0.0) {" << endl;
+ fs << " return vec4(0.0, 0.0, 0.0, 0.0);" << endl;
+ fs << " }" << endl;
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ fs << " backdrop.rgb /= backdrop.a;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
+ fs << " color.rgb /= color.a;" << endl;
+ }
+ fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
+ fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * blended.rgb;" << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+}
+
+ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
+ : mGL(aGL)
+ , mProgram(0)
+ , mProfile(aProfile)
+ , mProgramState(STATE_NEW)
+{
+}
+
+ShaderProgramOGL::~ShaderProgramOGL()
+{
+ if (mProgram <= 0) {
+ return;
+ }
+
+ RefPtr<GLContext> ctx = mGL->GetSharedContext();
+ if (!ctx) {
+ ctx = mGL;
+ }
+ ctx->MakeCurrent();
+ ctx->fDeleteProgram(mProgram);
+}
+
+bool
+ShaderProgramOGL::Initialize()
+{
+ NS_ASSERTION(mProgramState == STATE_NEW, "Shader program has already been initialised");
+
+ ostringstream vs, fs;
+ for (uint32_t i = 0; i < mProfile.mDefines.Length(); ++i) {
+ vs << mProfile.mDefines[i] << endl;
+ fs << mProfile.mDefines[i] << endl;
+ }
+ vs << mProfile.mVertexShaderString << endl;
+ fs << mProfile.mFragmentShaderString << endl;
+
+ if (!CreateProgram(vs.str().c_str(), fs.str().c_str())) {
+ mProgramState = STATE_ERROR;
+ return false;
+ }
+
+ mProgramState = STATE_OK;
+
+ for (uint32_t i = 0; i < KnownUniform::KnownUniformCount; ++i) {
+ mProfile.mUniforms[i].mLocation =
+ mGL->fGetUniformLocation(mProgram, mProfile.mUniforms[i].mNameString);
+ }
+
+ return true;
+}
+
+GLint
+ShaderProgramOGL::CreateShader(GLenum aShaderType, const char *aShaderSource)
+{
+ GLint success, len = 0;
+
+ GLint sh = mGL->fCreateShader(aShaderType);
+ mGL->fShaderSource(sh, 1, (const GLchar**)&aShaderSource, nullptr);
+ mGL->fCompileShader(sh);
+ mGL->fGetShaderiv(sh, LOCAL_GL_COMPILE_STATUS, &success);
+ mGL->fGetShaderiv(sh, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ /* Even if compiling is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::DebugShaders())
+#endif
+ )
+ {
+ nsAutoCString log;
+ log.SetCapacity(len);
+ mGL->fGetShaderInfoLog(sh, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ if (!success) {
+ printf_stderr("=== SHADER COMPILATION FAILED ===\n");
+ } else {
+ printf_stderr("=== SHADER COMPILATION WARNINGS ===\n");
+ }
+
+ printf_stderr("=== Source:\n%s\n", aShaderSource);
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+
+ if (!success) {
+ mGL->fDeleteShader(sh);
+ return 0;
+ }
+ }
+
+ return sh;
+}
+
+bool
+ShaderProgramOGL::CreateProgram(const char *aVertexShaderString,
+ const char *aFragmentShaderString)
+{
+ GLuint vertexShader = CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString);
+ GLuint fragmentShader = CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString);
+
+ if (!vertexShader || !fragmentShader)
+ return false;
+
+ GLint result = mGL->fCreateProgram();
+ mGL->fAttachShader(result, vertexShader);
+ mGL->fAttachShader(result, fragmentShader);
+
+ for (Pair<nsCString, GLuint>& attribute : mProfile.mAttributes) {
+ mGL->fBindAttribLocation(result, attribute.second(),
+ attribute.first().get());
+ }
+
+ mGL->fLinkProgram(result);
+
+ GLint success, len;
+ mGL->fGetProgramiv(result, LOCAL_GL_LINK_STATUS, &success);
+ mGL->fGetProgramiv(result, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+ /* Even if linking is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::DebugShaders())
+#endif
+ )
+ {
+ nsAutoCString log;
+ log.SetCapacity(len);
+ mGL->fGetProgramInfoLog(result, len, (GLint*) &len, (char*) log.BeginWriting());
+ log.SetLength(len);
+
+ if (!success) {
+ printf_stderr("=== PROGRAM LINKING FAILED ===\n");
+ } else {
+ printf_stderr("=== PROGRAM LINKING WARNINGS ===\n");
+ }
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+ }
+
+ // We can mark the shaders for deletion; they're attached to the program
+ // and will remain attached.
+ mGL->fDeleteShader(vertexShader);
+ mGL->fDeleteShader(fragmentShader);
+
+ if (!success) {
+ mGL->fDeleteProgram(result);
+ return false;
+ }
+
+ mProgram = result;
+ return true;
+}
+
+GLuint
+ShaderProgramOGL::GetProgram()
+{
+ if (mProgramState == STATE_NEW) {
+ if (!Initialize()) {
+ NS_WARNING("Shader could not be initialised");
+ }
+ }
+ MOZ_ASSERT(HasInitialized(), "Attempting to get a program that's not been initialized!");
+ return mProgram;
+}
+
+void
+ShaderProgramOGL::SetBlurRadius(float aRX, float aRY)
+{
+ float f[] = {aRX, aRY};
+ SetUniform(KnownUniform::BlurRadius, 2, f);
+
+ float gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH];
+ float sum = 0.0f;
+ for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
+ float x = i * GAUSSIAN_KERNEL_STEP;
+ float sigma = 1.0f;
+ gaussianKernel[i] = exp(-x * x / (2 * sigma * sigma)) / sqrt(2 * M_PI * sigma * sigma);
+ sum += gaussianKernel[i] * (i == 0 ? 1 : 2);
+ }
+ for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) {
+ gaussianKernel[i] /= sum;
+ }
+ SetArrayUniform(KnownUniform::BlurGaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel);
+}
+
+void
+ShaderProgramOGL::SetYUVColorSpace(YUVColorSpace aYUVColorSpace)
+{
+ float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(aYUVColorSpace);
+ SetMatrix3fvUniform(KnownUniform::YuvColorMatrix, yuvToRgb);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h
new file mode 100644
index 000000000..ff4fb825f
--- /dev/null
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_OGLSHADERPROGRAM_H
+#define GFX_OGLSHADERPROGRAM_H
+
+#include "GLContext.h" // for fast inlines of glUniform*
+#include "gfxTypes.h"
+#include "ImageTypes.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Pair.h" // for Pair
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/CompositorTypes.h"
+
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+enum ShaderFeatures {
+ ENABLE_RENDER_COLOR=0x01,
+ ENABLE_TEXTURE_RECT=0x02,
+ ENABLE_TEXTURE_EXTERNAL=0x04,
+ ENABLE_TEXTURE_YCBCR=0x08,
+ ENABLE_TEXTURE_NV12=0x10,
+ ENABLE_TEXTURE_COMPONENT_ALPHA=0x20,
+ ENABLE_TEXTURE_NO_ALPHA=0x40,
+ ENABLE_TEXTURE_RB_SWAP=0x80,
+ ENABLE_OPACITY=0x100,
+ ENABLE_BLUR=0x200,
+ ENABLE_COLOR_MATRIX=0x400,
+ ENABLE_MASK=0x800,
+ ENABLE_NO_PREMUL_ALPHA=0x1000,
+ ENABLE_DEAA=0x2000,
+ ENABLE_DYNAMIC_GEOMETRY=0x4000
+};
+
+class KnownUniform {
+public:
+ // this needs to be kept in sync with strings in 'AddUniforms'
+ enum KnownUniformName {
+ NotAKnownUniform = -1,
+
+ LayerTransform = 0,
+ LayerTransformInverse,
+ MaskTransform,
+ BackdropTransform,
+ LayerRects,
+ MatrixProj,
+ TextureTransform,
+ TextureRects,
+ RenderTargetOffset,
+ LayerOpacity,
+ Texture,
+ YTexture,
+ CbTexture,
+ CrTexture,
+ BlackTexture,
+ WhiteTexture,
+ MaskTexture,
+ BackdropTexture,
+ RenderColor,
+ TexCoordMultiplier,
+ CbCrTexCoordMultiplier,
+ TexturePass2,
+ ColorMatrix,
+ ColorMatrixVector,
+ BlurRadius,
+ BlurOffset,
+ BlurAlpha,
+ BlurGaussianKernel,
+ SSEdges,
+ ViewportSize,
+ VisibleCenter,
+ YuvColorMatrix,
+
+ KnownUniformCount
+ };
+
+ KnownUniform()
+ {
+ mName = NotAKnownUniform;
+ mNameString = nullptr;
+ mLocation = -1;
+ memset(&mValue, 0, sizeof(mValue));
+ }
+
+ bool UpdateUniform(int32_t i1) {
+ if (mLocation == -1) return false;
+ if (mValue.i1 != i1) {
+ mValue.i1 = i1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1) {
+ if (mLocation == -1) return false;
+ if (mValue.f1 != f1) {
+ mValue.f1 = f1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 ||
+ mValue.f16v[1] != f2)
+ {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2, float f3, float f4) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 ||
+ mValue.f16v[1] != f2 ||
+ mValue.f16v[2] != f3 ||
+ mValue.f16v[3] != f4)
+ {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ mValue.f16v[2] = f3;
+ mValue.f16v[3] = f4;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(int cnt, const float *fp) {
+ if (mLocation == -1) return false;
+ switch (cnt) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 9:
+ case 16:
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ NS_NOTREACHED("cnt must be 1 2 3 4 9 or 16");
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const float *fp) {
+ if (mLocation == -1) return false;
+ if (cnt > 16) {
+ return false;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const gfx::Point3D* points) {
+ if (mLocation == -1) return false;
+ if (cnt > 4) {
+ return false;
+ }
+
+ float fp[12];
+ float *d = fp;
+ for(int i=0; i < cnt; i++) {
+ // Note: Do not want to make assumptions about .x, .y, .z member packing.
+ // If gfx::Point3D is updated to make this guarantee, SIMD optimizations
+ // may be possible
+ *d++ = points[i].x;
+ *d++ = points[i].y;
+ *d++ = points[i].z;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt * 3) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt * 3);
+ return true;
+ }
+ return false;
+ }
+
+ KnownUniformName mName;
+ const char *mNameString;
+ int32_t mLocation;
+
+ union {
+ int i1;
+ float f1;
+ float f16v[16];
+ } mValue;
+};
+
+class ShaderConfigOGL
+{
+public:
+ ShaderConfigOGL() :
+ mFeatures(0),
+ mCompositionOp(gfx::CompositionOp::OP_OVER)
+ {}
+
+ void SetRenderColor(bool aEnabled);
+ void SetTextureTarget(GLenum aTarget);
+ void SetRBSwap(bool aEnabled);
+ void SetNoAlpha(bool aEnabled);
+ void SetOpacity(bool aEnabled);
+ void SetYCbCr(bool aEnabled);
+ void SetNV12(bool aEnabled);
+ void SetComponentAlpha(bool aEnabled);
+ void SetColorMatrix(bool aEnabled);
+ void SetBlur(bool aEnabled);
+ void SetMask(bool aEnabled);
+ void SetDEAA(bool aEnabled);
+ void SetCompositionOp(gfx::CompositionOp aOp);
+ void SetNoPremultipliedAlpha();
+ void SetDynamicGeometry(bool aEnabled);
+
+ bool operator< (const ShaderConfigOGL& other) const {
+ return mFeatures < other.mFeatures ||
+ (mFeatures == other.mFeatures &&
+ (int)mCompositionOp < (int)other.mCompositionOp);
+ }
+
+public:
+ void SetFeature(int aBitmask, bool aState) {
+ if (aState)
+ mFeatures |= aBitmask;
+ else
+ mFeatures &= (~aBitmask);
+ }
+
+ int mFeatures;
+ gfx::CompositionOp mCompositionOp;
+};
+
+static inline ShaderConfigOGL
+ShaderConfigFromTargetAndFormat(GLenum aTarget,
+ gfx::SurfaceFormat aFormat)
+{
+ ShaderConfigOGL config;
+ config.SetTextureTarget(aTarget);
+ config.SetRBSwap(aFormat == gfx::SurfaceFormat::B8G8R8A8 ||
+ aFormat == gfx::SurfaceFormat::B8G8R8X8);
+ config.SetNoAlpha(aFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+ aFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ aFormat == gfx::SurfaceFormat::R5G6B5_UINT16);
+ return config;
+}
+
+/**
+ * This struct represents the shaders that make up a program and the uniform
+ * and attribute parmeters that those shaders take.
+ * It is used by ShaderProgramOGL.
+ * Use the factory method GetProfileFor to create instances.
+ */
+struct ProgramProfileOGL
+{
+ /**
+ * Factory method; creates an instance of this class for the given
+ * ShaderConfigOGL
+ */
+ static ProgramProfileOGL GetProfileFor(ShaderConfigOGL aConfig);
+
+ // the source code for the program's shaders
+ std::string mVertexShaderString;
+ std::string mFragmentShaderString;
+
+ // the vertex attributes
+ nsTArray<Pair<nsCString, GLuint>> mAttributes;
+
+ KnownUniform mUniforms[KnownUniform::KnownUniformCount];
+ nsTArray<const char *> mDefines;
+ size_t mTextureCount;
+
+ ProgramProfileOGL() :
+ mTextureCount(0)
+ {}
+
+ private:
+ static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs);
+};
+
+
+#if defined(DEBUG)
+#define CHECK_CURRENT_PROGRAM 1
+#define ASSERT_THIS_PROGRAM \
+ do { \
+ GLuint currentProgram; \
+ mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram); \
+ MOZ_ASSERT(currentProgram == mProgram, \
+ "SetUniform with wrong program active!"); \
+ } while (0)
+#else
+#define ASSERT_THIS_PROGRAM \
+ do { } while (0)
+#endif
+
+/**
+ * Represents an OGL shader program. The details of a program are represented
+ * by a ProgramProfileOGL
+ */
+class ShaderProgramOGL
+{
+public:
+ typedef mozilla::gl::GLContext GLContext;
+
+ ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile);
+
+ ~ShaderProgramOGL();
+
+ bool HasInitialized() {
+ NS_ASSERTION(mProgramState != STATE_OK || mProgram > 0, "Inconsistent program state");
+ return mProgramState == STATE_OK;
+ }
+
+ GLuint GetProgram();
+
+ bool Initialize();
+
+ GLint CreateShader(GLenum aShaderType, const char *aShaderSource);
+
+ /**
+ * Creates a program and stores its id.
+ */
+ bool CreateProgram(const char *aVertexShaderString,
+ const char *aFragmentShaderString);
+
+ /**
+ * The following set of methods set a uniform argument to the shader program.
+ * Not all uniforms may be set for all programs, and such uses will throw
+ * an assertion.
+ */
+ void SetLayerTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransform, aMatrix);
+ }
+
+ void SetLayerTransformInverse(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransformInverse, aMatrix);
+ }
+
+ void SetMaskLayerTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::MaskTransform, aMatrix);
+ }
+
+ void SetBackdropTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::BackdropTransform, aMatrix);
+ }
+
+ void SetDEAAEdges(const gfx::Point3D* aEdges) {
+ SetArrayUniform(KnownUniform::SSEdges, 4, aEdges);
+ }
+
+ void SetViewportSize(const gfx::IntSize& aSize) {
+ float vals[2] = { (float)aSize.width, (float)aSize.height };
+ SetUniform(KnownUniform::ViewportSize, 2, vals);
+ }
+
+ void SetVisibleCenter(const gfx::Point& aVisibleCenter) {
+ float vals[2] = { aVisibleCenter.x, aVisibleCenter.y };
+ SetUniform(KnownUniform::VisibleCenter, 2, vals);
+ }
+
+ void SetLayerRects(const gfx::Rect* aRects) {
+ float vals[16] = { aRects[0].x, aRects[0].y, aRects[0].width, aRects[0].height,
+ aRects[1].x, aRects[1].y, aRects[1].width, aRects[1].height,
+ aRects[2].x, aRects[2].y, aRects[2].width, aRects[2].height,
+ aRects[3].x, aRects[3].y, aRects[3].width, aRects[3].height };
+ SetUniform(KnownUniform::LayerRects, 16, vals);
+ }
+
+ void SetProjectionMatrix(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::MatrixProj, aMatrix);
+ }
+
+ // sets this program's texture transform, if it uses one
+ void SetTextureTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::TextureTransform, aMatrix);
+ }
+
+ void SetTextureRects(const gfx::Rect* aRects) {
+ float vals[16] = { aRects[0].x, aRects[0].y, aRects[0].width, aRects[0].height,
+ aRects[1].x, aRects[1].y, aRects[1].width, aRects[1].height,
+ aRects[2].x, aRects[2].y, aRects[2].width, aRects[2].height,
+ aRects[3].x, aRects[3].y, aRects[3].width, aRects[3].height };
+ SetUniform(KnownUniform::TextureRects, 16, vals);
+ }
+
+ void SetRenderOffset(const nsIntPoint& aOffset) {
+ float vals[4] = { float(aOffset.x), float(aOffset.y) };
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetRenderOffset(float aX, float aY) {
+ float vals[2] = { aX, aY };
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetLayerOpacity(float aOpacity) {
+ SetUniform(KnownUniform::LayerOpacity, aOpacity);
+ }
+
+ void SetTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::Texture, aUnit);
+ }
+ void SetYTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::YTexture, aUnit);
+ }
+
+ void SetCbTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CbTexture, aUnit);
+ }
+
+ void SetCrTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CrTexture, aUnit);
+ }
+
+ void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbUnit);
+ SetUniform(KnownUniform::CrTexture, aCrUnit);
+ }
+
+ void SetNV12TextureUnits(GLint aYUnit, GLint aCbCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbCrUnit);
+ }
+
+ void SetBlackTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::BlackTexture, aUnit);
+ }
+
+ void SetWhiteTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::WhiteTexture, aUnit);
+ }
+
+ void SetMaskTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::MaskTexture, aUnit);
+ }
+
+ void SetBackdropTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::BackdropTexture, aUnit);
+ }
+
+ void SetRenderColor(const gfx::Color& aColor) {
+ SetUniform(KnownUniform::RenderColor, aColor);
+ }
+
+ void SetColorMatrix(const gfx::Matrix5x4& aColorMatrix)
+ {
+ SetMatrixUniform(KnownUniform::ColorMatrix, &aColorMatrix._11);
+ SetUniform(KnownUniform::ColorMatrixVector, 4, &aColorMatrix._51);
+ }
+
+ void SetTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::TexCoordMultiplier, 2, f);
+ }
+
+ void SetCbCrTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::CbCrTexCoordMultiplier, 2, f);
+ }
+
+ void SetYUVColorSpace(YUVColorSpace aYUVColorSpace);
+
+ // Set whether we want the component alpha shader to return the color
+ // vector (pass 1, false) or the alpha vector (pass2, true). With support
+ // for multiple render targets we wouldn't need two passes here.
+ void SetTexturePass2(bool aFlag) {
+ SetUniform(KnownUniform::TexturePass2, aFlag ? 1 : 0);
+ }
+
+ void SetBlurRadius(float aRX, float aRY);
+
+ void SetBlurAlpha(float aAlpha) {
+ SetUniform(KnownUniform::BlurAlpha, aAlpha);
+ }
+
+ void SetBlurOffset(float aOffsetX, float aOffsetY) {
+ float f[] = {aOffsetX, aOffsetY};
+ SetUniform(KnownUniform::BlurOffset, 2, f);
+ }
+
+ size_t GetTextureCount() const {
+ return mProfile.mTextureCount;
+ }
+
+protected:
+ RefPtr<GLContext> mGL;
+ // the OpenGL id of the program
+ GLuint mProgram;
+ ProgramProfileOGL mProfile;
+ enum {
+ STATE_NEW,
+ STATE_OK,
+ STATE_ERROR
+ } mProgramState;
+
+#ifdef CHECK_CURRENT_PROGRAM
+ static int sCurrentProgramKey;
+#endif
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, float aFloatValue)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aFloatValue)) {
+ mGL->fUniform1f(ku.mLocation, aFloatValue);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Color& aColor) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) {
+ mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, const float *aFloatValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aLength, aFloatValues)) {
+ switch (aLength) {
+ case 1: mGL->fUniform1fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 2: mGL->fUniform2fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 3: mGL->fUniform3fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 4: mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); break;
+ case 16: mGL->fUniform4fv(ku.mLocation, 4, ku.mValue.f16v); break;
+ default:
+ NS_NOTREACHED("Bogus aLength param");
+ }
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, float *aFloatValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aFloatValues)) {
+ mGL->fUniform1fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, const gfx::Point3D *aPointValues)
+ {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aPointValues)) {
+ mGL->fUniform3fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, GLint aIntValue) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aIntValue)) {
+ mGL->fUniform1i(ku.mLocation, aIntValue);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const float *aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(16, aFloatValues)) {
+ mGL->fUniformMatrix4fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrix3fvUniform(KnownUniform::KnownUniformName aKnownUniform, const float *aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(9, aFloatValues)) {
+ mGL->fUniformMatrix3fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(aKnownUniform, &aMatrix._11);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_OGLSHADERPROGRAM_H */
diff --git a/gfx/layers/opengl/TextureClientOGL.cpp b/gfx/layers/opengl/TextureClientOGL.cpp
new file mode 100644
index 000000000..78d4e6d9c
--- /dev/null
+++ b/gfx/layers/opengl/TextureClientOGL.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GLContext.h" // for GLContext, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "GLLibraryEGL.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+class CompositableForwarder;
+
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+EGLImageTextureData::EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize)
+: mImage(aImage)
+, mSize(aSize)
+{
+ MOZ_ASSERT(aImage);
+}
+
+already_AddRefed<TextureClient>
+EGLImageTextureData::CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "Can't pass an `EGLImage` between processes.");
+
+ if (!aImage || !XRE_IsParentProcess()) {
+ return nullptr;
+ }
+
+ // XXX - This is quite sad and slow.
+ aFlags |= TextureFlags::DEALLOCATE_CLIENT;
+
+ if (aImage->GetOriginPos() == gl::OriginPos::BottomLeft) {
+ aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ return TextureClient::CreateWithData(
+ new EGLImageTextureData(aImage, aSize),
+ aFlags, aAllocator
+ );
+}
+
+void
+EGLImageTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+EGLImageTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ const bool hasAlpha = true;
+ aOutDescriptor =
+ EGLImageDescriptor((uintptr_t)mImage->GetImage(),
+ (uintptr_t)mImage->GetSync(),
+ mImage->GetSize(), hasAlpha);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////
+// AndroidSurface
+
+#ifdef MOZ_WIDGET_ANDROID
+
+already_AddRefed<TextureClient>
+AndroidSurfaceTextureData::CreateTextureClient(AndroidSurfaceTexture* aSurfTex,
+ gfx::IntSize aSize,
+ gl::OriginPos aOriginPos,
+ LayersIPCChannel* aAllocator,
+ TextureFlags aFlags)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "Can't pass an android surfaces between processes.");
+
+ if (!aSurfTex || !XRE_IsParentProcess()) {
+ return nullptr;
+ }
+
+ if (aOriginPos == gl::OriginPos::BottomLeft) {
+ aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ return TextureClient::CreateWithData(
+ new AndroidSurfaceTextureData(aSurfTex, aSize),
+ aFlags, aAllocator
+ );
+}
+
+AndroidSurfaceTextureData::AndroidSurfaceTextureData(AndroidSurfaceTexture* aSurfTex,
+ gfx::IntSize aSize)
+ : mSurfTex(aSurfTex)
+ , mSize(aSize)
+{}
+
+AndroidSurfaceTextureData::~AndroidSurfaceTextureData()
+{}
+
+void
+AndroidSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+AndroidSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceTextureDescriptor((uintptr_t)mSurfTex.get(),
+ mSize);
+ return true;
+}
+
+#endif // MOZ_WIDGET_ANDROID
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/TextureClientOGL.h b/gfx/layers/opengl/TextureClientOGL.h
new file mode 100644
index 000000000..6555f138a
--- /dev/null
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTOGL_H
+#define MOZILLA_GFX_TEXTURECLIENTOGL_H
+
+#include "GLContextTypes.h" // for SharedTextureHandle, etc
+#include "GLImages.h"
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "AndroidSurfaceTexture.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class EGLImageTextureData : public TextureData
+{
+public:
+
+ static already_AddRefed<TextureClient>
+ CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags);
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override { mImage = nullptr; }
+
+ virtual void Forget(LayersIPCChannel*) override { mImage = nullptr; }
+
+ // Unused functions.
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+protected:
+ EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize);
+
+ RefPtr<EGLImageImage> mImage;
+ const gfx::IntSize mSize;
+};
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class AndroidSurfaceTextureData : public TextureData
+{
+public:
+ static already_AddRefed<TextureClient>
+ CreateTextureClient(gl::AndroidSurfaceTexture* aSurfTex,
+ gfx::IntSize aSize,
+ gl::OriginPos aOriginPos,
+ LayersIPCChannel* aAllocator,
+ TextureFlags aFlags);
+
+ ~AndroidSurfaceTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ // Useless functions.
+ virtual bool Lock(OpenMode) override { return true; }
+
+ virtual void Unlock() override {}
+
+ // Our data is always owned externally.
+ virtual void Deallocate(LayersIPCChannel*) override {}
+
+protected:
+ AndroidSurfaceTextureData(gl::AndroidSurfaceTexture* aSurfTex, gfx::IntSize aSize);
+
+ const RefPtr<gl::AndroidSurfaceTexture> mSurfTex;
+ const gfx::IntSize mSize;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp
new file mode 100644
index 000000000..854160bc6
--- /dev/null
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -0,0 +1,771 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureHostOGL.h"
+
+#include "EGLUtils.h"
+#include "GLContext.h" // for GLContext, etc
+#include "GLLibraryEGL.h" // for GLLibraryEGL
+#include "GLUploadHelpers.h"
+#include "GLReadTexImageHelper.h"
+#include "gfx2DGlue.h" // for ContentForFormat, etc
+#include "mozilla/gfx/2D.h" // for DataSourceSurface
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Logging.h" // for gfxCriticalError
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "AndroidSurfaceTexture.h"
+#include "GfxTexturesReporter.h" // for GfxTexturesReporter
+#include "GLBlitTextureImageHelper.h"
+#include "GeckoProfiler.h"
+
+#ifdef XP_MACOSX
+#include "mozilla/layers/MacIOSurfaceTextureHostOGL.h"
+#endif
+
+#ifdef GL_PROVIDER_GLX
+#include "mozilla/layers/X11TextureHost.h"
+#endif
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+already_AddRefed<TextureHost>
+CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+{
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ result = CreateBackendIndependentTextureHost(aDesc,
+ aDeallocator, aFlags);
+ break;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ case SurfaceDescriptor::TSurfaceTextureDescriptor: {
+ const SurfaceTextureDescriptor& desc = aDesc.get_SurfaceTextureDescriptor();
+ result = new SurfaceTextureHost(aFlags,
+ (AndroidSurfaceTexture*)desc.surfTex(),
+ desc.size());
+ break;
+ }
+#endif
+
+ case SurfaceDescriptor::TEGLImageDescriptor: {
+ const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor();
+ result = new EGLImageTextureHost(aFlags,
+ (EGLImage)desc.image(),
+ (EGLSync)desc.fence(),
+ desc.size(),
+ desc.hasAlpha());
+ break;
+ }
+
+#ifdef XP_MACOSX
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
+ const SurfaceDescriptorMacIOSurface& desc =
+ aDesc.get_SurfaceDescriptorMacIOSurface();
+ result = new MacIOSurfaceTextureHostOGL(aFlags, desc);
+ break;
+ }
+#endif
+
+#ifdef GL_PROVIDER_GLX
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ const auto& desc = aDesc.get_SurfaceDescriptorX11();
+ result = new X11TextureHost(aFlags, desc);
+ break;
+ }
+#endif
+
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
+ const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
+ result = new GLTextureHost(aFlags, desc.texture(),
+ desc.target(),
+ (GLsync)desc.fence(),
+ desc.size(),
+ desc.hasAlpha());
+ break;
+ }
+ default: return nullptr;
+ }
+ return result.forget();
+}
+
+static gl::TextureImage::Flags
+FlagsToGLFlags(TextureFlags aFlags)
+{
+ uint32_t result = TextureImage::NoFlags;
+
+ if (aFlags & TextureFlags::USE_NEAREST_FILTER)
+ result |= TextureImage::UseNearestFilter;
+ if (aFlags & TextureFlags::ORIGIN_BOTTOM_LEFT)
+ result |= TextureImage::OriginBottomLeft;
+ if (aFlags & TextureFlags::DISALLOW_BIGIMAGE)
+ result |= TextureImage::DisallowBigImage;
+
+ return static_cast<gl::TextureImage::Flags>(result);
+}
+
+bool
+TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset)
+{
+ GLContext *gl = mCompositor->gl();
+ MOZ_ASSERT(gl);
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("trying to update TextureImageTextureSourceOGL without a GLContext");
+ return false;
+ }
+ if (!aSurface) {
+ gfxCriticalError() << "Invalid surface for OGL update";
+ return false;
+ }
+ MOZ_ASSERT(aSurface);
+
+ IntSize size = aSurface->GetSize();
+ if (!mTexImage ||
+ (mTexImage->GetSize() != size && !aSrcOffset) ||
+ mTexImage->GetContentType() != gfx::ContentForFormat(aSurface->GetFormat())) {
+ if (mFlags & TextureFlags::DISALLOW_BIGIMAGE) {
+ GLint maxTextureSize;
+ gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ if (size.width > maxTextureSize || size.height > maxTextureSize) {
+ NS_WARNING("Texture exceeds maximum texture size, refusing upload");
+ return false;
+ }
+ // Explicitly use CreateBasicTextureImage instead of CreateTextureImage,
+ // because CreateTextureImage might still choose to create a tiled
+ // texture image.
+ mTexImage = CreateBasicTextureImage(gl, size,
+ gfx::ContentForFormat(aSurface->GetFormat()),
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags));
+ } else {
+ // XXX - clarify which size we want to use. IncrementalContentHost will
+ // require the size of the destination surface to be different from
+ // the size of aSurface.
+ // See bug 893300 (tracks the implementation of ContentHost for new textures).
+ mTexImage = CreateTextureImage(gl,
+ size,
+ gfx::ContentForFormat(aSurface->GetFormat()),
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags),
+ SurfaceFormatToImageFormat(aSurface->GetFormat()));
+ }
+ ClearCachedFilter();
+
+ if (aDestRegion &&
+ !aSrcOffset &&
+ !aDestRegion->IsEqual(gfx::IntRect(0, 0, size.width, size.height))) {
+ // UpdateFromDataSource will ignore our specified aDestRegion since the texture
+ // hasn't been allocated with glTexImage2D yet. Call Resize() to force the
+ // allocation (full size, but no upload), and then we'll only upload the pixels
+ // we care about below.
+ mTexImage->Resize(size);
+ }
+ }
+
+ mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset);
+
+ return true;
+}
+
+void
+TextureImageTextureSourceOGL::EnsureBuffer(const IntSize& aSize,
+ gfxContentType aContentType)
+{
+ if (!mTexImage ||
+ mTexImage->GetSize() != aSize ||
+ mTexImage->GetContentType() != aContentType) {
+ mTexImage = CreateTextureImage(mCompositor->gl(),
+ aSize,
+ aContentType,
+ LOCAL_GL_CLAMP_TO_EDGE,
+ FlagsToGLFlags(mFlags));
+ }
+ mTexImage->Resize(aSize);
+}
+
+void
+TextureImageTextureSourceOGL::CopyTo(const gfx::IntRect& aSourceRect,
+ DataTextureSource *aDest,
+ const gfx::IntRect& aDestRect)
+{
+ MOZ_ASSERT(aDest->AsSourceOGL(), "Incompatible destination type!");
+ TextureImageTextureSourceOGL *dest =
+ aDest->AsSourceOGL()->AsTextureImageTextureSource();
+ MOZ_ASSERT(dest, "Incompatible destination type!");
+
+ mCompositor->BlitTextureImageHelper()->BlitTextureImage(mTexImage, aSourceRect,
+ dest->mTexImage, aDestRect);
+ dest->mTexImage->MarkValid();
+}
+
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* compositor = aCompositor ? aCompositor->AsCompositorOGL()
+ : nullptr;
+ MOZ_ASSERT(!!compositor);
+ return compositor;
+}
+
+void
+TextureImageTextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ DeallocateDeviceData();
+ return;
+ }
+ if (mCompositor != glCompositor) {
+ DeallocateDeviceData();
+ mCompositor = glCompositor;
+ }
+}
+
+gfx::IntSize
+TextureImageTextureSourceOGL::GetSize() const
+{
+ if (mTexImage) {
+ if (mIterating) {
+ return mTexImage->GetTileRect().Size();
+ }
+ return mTexImage->GetSize();
+ }
+ NS_WARNING("Trying to query the size of an empty TextureSource.");
+ return gfx::IntSize(0, 0);
+}
+
+gfx::SurfaceFormat
+TextureImageTextureSourceOGL::GetFormat() const
+{
+ if (mTexImage) {
+ return mTexImage->GetTextureFormat();
+ }
+ NS_WARNING("Trying to query the format of an empty TextureSource.");
+ return gfx::SurfaceFormat::UNKNOWN;
+}
+
+gfx::IntRect TextureImageTextureSourceOGL::GetTileRect()
+{
+ return mTexImage->GetTileRect();
+}
+
+void
+TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ MOZ_ASSERT(mTexImage,
+ "Trying to bind a TextureSource that does not have an underlying GL texture.");
+ mTexImage->BindTexture(aTextureUnit);
+ SetSamplingFilter(mCompositor->gl(), aSamplingFilter);
+}
+
+////////////////////////////////////////////////////////////////////////
+// GLTextureSource
+
+GLTextureSource::GLTextureSource(CompositorOGL* aCompositor,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ bool aExternallyOwned)
+ : mCompositor(aCompositor)
+ , mTextureHandle(aTextureHandle)
+ , mTextureTarget(aTarget)
+ , mSize(aSize)
+ , mFormat(aFormat)
+ , mExternallyOwned(aExternallyOwned)
+{
+ MOZ_COUNT_CTOR(GLTextureSource);
+}
+
+GLTextureSource::~GLTextureSource()
+{
+ MOZ_COUNT_DTOR(GLTextureSource);
+ if (!mExternallyOwned) {
+ DeleteTextureHandle();
+ }
+}
+
+void
+GLTextureSource::DeallocateDeviceData()
+{
+ if (!mExternallyOwned) {
+ DeleteTextureHandle();
+ }
+}
+
+void
+GLTextureSource::DeleteTextureHandle()
+{
+ GLContext* gl = this->gl();
+ if (mTextureHandle != 0 && gl && gl->MakeCurrent()) {
+ gl->fDeleteTextures(1, &mTextureHandle);
+ }
+ mTextureHandle = 0;
+}
+
+void
+GLTextureSource::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ MOZ_ASSERT(mTextureHandle != 0);
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return;
+ }
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(mTextureTarget, mTextureHandle);
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
+}
+
+void
+GLTextureSource::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ return;
+ }
+
+ if (mCompositor && mCompositor != glCompositor) {
+ gfxCriticalError() << "GLTextureSource does not support changing compositors";
+ }
+ mCompositor = glCompositor;
+
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+bool
+GLTextureSource::IsValid() const
+{
+ return !!gl() && mTextureHandle != 0;
+}
+
+gl::GLContext*
+GLTextureSource::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+// SurfaceTextureHost
+
+#ifdef MOZ_WIDGET_ANDROID
+
+SurfaceTextureSource::SurfaceTextureSource(CompositorOGL* aCompositor,
+ AndroidSurfaceTexture* aSurfTex,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize)
+ : mCompositor(aCompositor)
+ , mSurfTex(aSurfTex)
+ , mFormat(aFormat)
+ , mTextureTarget(aTarget)
+ , mWrapMode(aWrapMode)
+ , mSize(aSize)
+{
+}
+
+void
+SurfaceTextureSource::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ MOZ_ASSERT(mSurfTex);
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("Trying to bind a texture without a GLContext");
+ return;
+ }
+
+ gl->fActiveTexture(aTextureUnit);
+
+ // SurfaceTexture spams us if there are any existing GL errors, so
+ // we'll clear them here in order to avoid that.
+ gl->FlushErrors();
+
+ mSurfTex->UpdateTexImage();
+
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
+}
+
+void
+SurfaceTextureSource::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ DeallocateDeviceData();
+ return;
+ }
+ if (mCompositor != glCompositor) {
+ DeallocateDeviceData();
+ }
+
+ mCompositor = glCompositor;
+}
+
+bool
+SurfaceTextureSource::IsValid() const
+{
+ return !!gl();
+}
+
+gl::GLContext*
+SurfaceTextureSource::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+gfx::Matrix4x4
+SurfaceTextureSource::GetTextureTransform()
+{
+ MOZ_ASSERT(mSurfTex);
+
+ gfx::Matrix4x4 ret;
+ mSurfTex->GetTransformMatrix(ret);
+
+ return ret;
+}
+
+void
+SurfaceTextureSource::DeallocateDeviceData()
+{
+ mSurfTex = nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+SurfaceTextureHost::SurfaceTextureHost(TextureFlags aFlags,
+ AndroidSurfaceTexture* aSurfTex,
+ gfx::IntSize aSize)
+ : TextureHost(aFlags)
+ , mSurfTex(aSurfTex)
+ , mSize(aSize)
+ , mCompositor(nullptr)
+{
+}
+
+SurfaceTextureHost::~SurfaceTextureHost()
+{
+}
+
+gl::GLContext*
+SurfaceTextureHost::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+SurfaceTextureHost::Lock()
+{
+ MOZ_ASSERT(mSurfTex);
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::R8G8B8A8;
+ GLenum target = LOCAL_GL_TEXTURE_EXTERNAL;
+ GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
+ mTextureSource = new SurfaceTextureSource(mCompositor,
+ mSurfTex,
+ format,
+ target,
+ wrapMode,
+ mSize);
+ }
+
+ return NS_SUCCEEDED(mSurfTex->Attach(gl));
+}
+
+void
+SurfaceTextureHost::Unlock()
+{
+ MOZ_ASSERT(mSurfTex);
+ mSurfTex->Detach();
+}
+
+void
+SurfaceTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ DeallocateDeviceData();
+ return;
+ }
+ mCompositor = glCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(glCompositor);
+ }
+}
+
+gfx::SurfaceFormat
+SurfaceTextureHost::GetFormat() const
+{
+ return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+}
+
+void
+SurfaceTextureHost::DeallocateDeviceData()
+{
+ if (mTextureSource) {
+ mTextureSource->DeallocateDeviceData();
+ }
+ mSurfTex = nullptr;
+}
+
+#endif // MOZ_WIDGET_ANDROID
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+EGLImageTextureSource::EGLImageTextureSource(CompositorOGL* aCompositor,
+ EGLImage aImage,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize)
+ : mCompositor(aCompositor)
+ , mImage(aImage)
+ , mFormat(aFormat)
+ , mTextureTarget(aTarget)
+ , mWrapMode(aWrapMode)
+ , mSize(aSize)
+{
+ MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
+ mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
+}
+
+void
+EGLImageTextureSource::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("Trying to bind a texture without a GLContext");
+ return;
+ }
+
+ MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl),
+ "EGLImage not supported or disabled in runtime");
+
+ GLuint tex = mCompositor->GetTemporaryTexture(mTextureTarget, aTextureUnit);
+
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(mTextureTarget, tex);
+
+ gl->fEGLImageTargetTexture2D(mTextureTarget, mImage);
+
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
+}
+
+void
+EGLImageTextureSource::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertGLCompositor(aCompositor);
+}
+
+bool
+EGLImageTextureSource::IsValid() const
+{
+ return !!gl();
+}
+
+gl::GLContext*
+EGLImageTextureSource::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+gfx::Matrix4x4
+EGLImageTextureSource::GetTextureTransform()
+{
+ gfx::Matrix4x4 ret;
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+EGLImageTextureHost::EGLImageTextureHost(TextureFlags aFlags,
+ EGLImage aImage,
+ EGLSync aSync,
+ gfx::IntSize aSize,
+ bool hasAlpha)
+ : TextureHost(aFlags)
+ , mImage(aImage)
+ , mSync(aSync)
+ , mSize(aSize)
+ , mHasAlpha(hasAlpha)
+ , mCompositor(nullptr)
+{}
+
+EGLImageTextureHost::~EGLImageTextureHost()
+{}
+
+gl::GLContext*
+EGLImageTextureHost::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+EGLImageTextureHost::Lock()
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return false;
+ }
+
+ EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+
+ if (mSync) {
+ MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
+ status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), mSync, 0, LOCAL_EGL_FOREVER);
+ }
+
+ if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+ MOZ_ASSERT(status != 0,
+ "ClientWaitSync generated an error. Has mSync already been destroyed?");
+ return false;
+ }
+
+ if (!mTextureSource) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ GLenum target = gl->GetPreferredEGLImageTextureTarget();
+ GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
+ mTextureSource = new EGLImageTextureSource(mCompositor,
+ mImage,
+ format,
+ target,
+ wrapMode,
+ mSize);
+ }
+
+ return true;
+}
+
+void
+EGLImageTextureHost::Unlock()
+{
+}
+
+void
+EGLImageTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = glCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(glCompositor);
+ }
+}
+
+gfx::SurfaceFormat
+EGLImageTextureHost::GetFormat() const
+{
+ MOZ_ASSERT(mTextureSource);
+ return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+}
+
+//
+
+GLTextureHost::GLTextureHost(TextureFlags aFlags,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ GLsync aSync,
+ gfx::IntSize aSize,
+ bool aHasAlpha)
+ : TextureHost(aFlags)
+ , mTexture(aTextureHandle)
+ , mTarget(aTarget)
+ , mSync(aSync)
+ , mSize(aSize)
+ , mHasAlpha(aHasAlpha)
+ , mCompositor(nullptr)
+{}
+
+GLTextureHost::~GLTextureHost()
+{}
+
+gl::GLContext*
+GLTextureHost::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+GLTextureHost::Lock()
+{
+ GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ return false;
+ }
+
+ if (mSync) {
+ if (!gl->MakeCurrent()) {
+ return false;
+ }
+ gl->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED);
+ gl->fDeleteSync(mSync);
+ mSync = 0;
+ }
+
+ if (!mTextureSource) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ mTextureSource = new GLTextureSource(mCompositor,
+ mTexture,
+ mTarget,
+ mSize,
+ format,
+ false /* owned by the client */);
+ }
+
+ return true;
+}
+void
+GLTextureHost::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mCompositor = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = glCompositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(glCompositor);
+ }
+}
+
+gfx::SurfaceFormat
+GLTextureHost::GetFormat() const
+{
+ MOZ_ASSERT(mTextureSource);
+ return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h
new file mode 100644
index 000000000..dd425e768
--- /dev/null
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -0,0 +1,539 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_TEXTUREOGL_H
+#define MOZILLA_GFX_TEXTUREOGL_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "CompositableHost.h"
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_CLAMP_TO_EDGE, etc
+#include "GLTextureImage.h" // for TextureImage
+#include "gfxTypes.h"
+#include "mozilla/GfxMessageUtils.h" // for gfxContentType
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for TextureImage::Release, etc
+#include "nsRegionFwd.h" // for nsIntRegion
+#include "OGLShaderProgram.h" // for ShaderProgramType, etc
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace gl {
+class AndroidSurfaceTexture;
+} // namespace gl
+
+namespace layers {
+
+class Compositor;
+class CompositorOGL;
+class TextureImageTextureSourceOGL;
+class GLTextureSource;
+
+inline void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL,
+ gfx::SamplingFilter aSamplingFilter,
+ GLuint aTarget = LOCAL_GL_TEXTURE_2D)
+{
+ GLenum filter =
+ (aSamplingFilter == gfx::SamplingFilter::POINT ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR);
+
+ aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, filter);
+ aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, filter);
+}
+
+/*
+ * TextureHost implementations for the OpenGL backend.
+ *
+ * Note that it is important to be careful about the ownership model with
+ * the OpenGL backend, due to some widget limitation on Linux: before
+ * the nsBaseWidget associated with our OpenGL context has been completely
+ * deleted, every resource belonging to the OpenGL context MUST have been
+ * released. At the moment the teardown sequence happens in the middle of
+ * the nsBaseWidget's destructor, meaning that at a given moment we must be
+ * able to easily find and release all the GL resources.
+ * The point is: be careful about the ownership model and limit the number
+ * of objects sharing references to GL resources to make the tear down
+ * sequence as simple as possible.
+ */
+
+/**
+ * TextureSourceOGL provides the necessary API for CompositorOGL to composite
+ * a TextureSource.
+ */
+class TextureSourceOGL
+{
+public:
+ TextureSourceOGL()
+ : mHasCachedSamplingFilter(false)
+ {}
+
+ virtual bool IsValid() const = 0;
+
+ virtual void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) = 0;
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; }
+
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+
+ virtual GLenum GetWrapMode() const = 0;
+
+ virtual gfx::Matrix4x4 GetTextureTransform() { return gfx::Matrix4x4(); }
+
+ virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; }
+
+ virtual GLTextureSource* AsGLTextureSource() { return nullptr; }
+
+ void SetSamplingFilter(gl::GLContext* aGL, gfx::SamplingFilter aSamplingFilter)
+ {
+ if (mHasCachedSamplingFilter &&
+ mCachedSamplingFilter == aSamplingFilter) {
+ return;
+ }
+ mHasCachedSamplingFilter = true;
+ mCachedSamplingFilter = aSamplingFilter;
+ ApplySamplingFilterToBoundTexture(aGL, aSamplingFilter, GetTextureTarget());
+ }
+
+ void ClearCachedFilter() { mHasCachedSamplingFilter = false; }
+
+private:
+ gfx::SamplingFilter mCachedSamplingFilter;
+ bool mHasCachedSamplingFilter;
+};
+
+/**
+ * A TextureSource backed by a TextureImage.
+ *
+ * Depending on the underlying TextureImage, may support texture tiling, so
+ * make sure to check AsBigImageIterator() and use the texture accordingly.
+ *
+ * This TextureSource can be used without a TextureHost and manage it's own
+ * GL texture(s).
+ */
+class TextureImageTextureSourceOGL final : public DataTextureSource
+ , public TextureSourceOGL
+ , public BigImageIterator
+{
+public:
+ explicit TextureImageTextureSourceOGL(CompositorOGL *aCompositor,
+ TextureFlags aFlags = TextureFlags::DEFAULT)
+ : mCompositor(aCompositor)
+ , mFlags(aFlags)
+ , mIterating(false)
+ {}
+
+ virtual const char* Name() const override { return "TextureImageTextureSourceOGL"; }
+ // DataTextureSource
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ void EnsureBuffer(const gfx::IntSize& aSize,
+ gfxContentType aContentType);
+
+ void CopyTo(const gfx::IntRect& aSourceRect,
+ DataTextureSource* aDest,
+ const gfx::IntRect& aDestRect);
+
+ virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() override { return this; }
+
+ // TextureSource
+
+ virtual void DeallocateDeviceData() override
+ {
+ mTexImage = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool IsValid() const override { return !!mTexImage; }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual GLenum GetWrapMode() const override
+ {
+ return mTexImage->GetWrapMode();
+ }
+
+ // BigImageIterator
+
+ virtual BigImageIterator* AsBigImageIterator() override { return this; }
+
+ virtual void BeginBigImageIteration() override
+ {
+ mTexImage->BeginBigImageIteration();
+ mIterating = true;
+ }
+
+ virtual void EndBigImageIteration() override
+ {
+ mIterating = false;
+ }
+
+ virtual gfx::IntRect GetTileRect() override;
+
+ virtual size_t GetTileCount() override
+ {
+ return mTexImage->GetTileCount();
+ }
+
+ virtual bool NextTile() override
+ {
+ return mTexImage->NextTile();
+ }
+
+protected:
+ RefPtr<gl::TextureImage> mTexImage;
+ RefPtr<CompositorOGL> mCompositor;
+ TextureFlags mFlags;
+ bool mIterating;
+};
+
+/**
+ * A texture source for GL textures.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ *
+ * The shared texture handle is owned by the TextureHost.
+ */
+class GLTextureSource : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ GLTextureSource(CompositorOGL* aCompositor,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ bool aExternallyOwned = false);
+
+ ~GLTextureSource();
+
+ virtual const char* Name() const override { return "GLTextureSource"; }
+
+ virtual GLTextureSource* AsGLTextureSource() override { return this; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ void SetSize(gfx::IntSize aSize) { mSize = aSize; }
+
+ void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; }
+
+ GLuint GetTextureHandle() const { return mTextureHandle; }
+
+ gl::GLContext* gl() const;
+
+protected:
+ void DeleteTextureHandle();
+
+ RefPtr<CompositorOGL> mCompositor;
+ GLuint mTextureHandle;
+ GLenum mTextureTarget;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ // If the texture is externally owned, the gl handle will not be deleted
+ // in the destructor.
+ bool mExternallyOwned;
+};
+
+class GLTextureHost : public TextureHost
+{
+public:
+ GLTextureHost(TextureFlags aFlags,
+ GLuint aTextureHandle,
+ GLenum aTarget,
+ GLsync aSync,
+ gfx::IntSize aSize,
+ bool aHasAlpha);
+
+ virtual ~GLTextureHost();
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override {}
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual const char* Name() override { return "GLTextureHost"; }
+
+protected:
+ const GLuint mTexture;
+ const GLenum mTarget;
+ GLsync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLTextureSource> mTextureSource;
+};
+
+////////////////////////////////////////////////////////////////////////
+// SurfaceTexture
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class SurfaceTextureSource : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ SurfaceTextureSource(CompositorOGL* aCompositor,
+ mozilla::gl::AndroidSurfaceTexture* aSurfTex,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize);
+
+ virtual const char* Name() const override { return "SurfaceTextureSource"; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::Matrix4x4 GetTextureTransform() override;
+
+ virtual GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ virtual GLenum GetWrapMode() const override { return mWrapMode; }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ gl::GLContext* gl() const;
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<gl::AndroidSurfaceTexture> mSurfTex;
+ const gfx::SurfaceFormat mFormat;
+ const GLenum mTextureTarget;
+ const GLenum mWrapMode;
+ const gfx::IntSize mSize;
+};
+
+class SurfaceTextureHost : public TextureHost
+{
+public:
+ SurfaceTextureHost(TextureFlags aFlags,
+ mozilla::gl::AndroidSurfaceTexture* aSurfTex,
+ gfx::IntSize aSize);
+
+ virtual ~SurfaceTextureHost();
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual const char* Name() override { return "SurfaceTextureHost"; }
+
+protected:
+ RefPtr<gl::AndroidSurfaceTexture> mSurfTex;
+ const gfx::IntSize mSize;
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<SurfaceTextureSource> mTextureSource;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+class EGLImageTextureSource : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ EGLImageTextureSource(CompositorOGL* aCompositor,
+ EGLImage aImage,
+ gfx::SurfaceFormat aFormat,
+ GLenum aTarget,
+ GLenum aWrapMode,
+ gfx::IntSize aSize);
+
+ virtual const char* Name() const override { return "EGLImageTextureSource"; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+ virtual gfx::Matrix4x4 GetTextureTransform() override;
+
+ virtual GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ virtual GLenum GetWrapMode() const override { return mWrapMode; }
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ gl::GLContext* gl() const;
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ const EGLImage mImage;
+ const gfx::SurfaceFormat mFormat;
+ const GLenum mTextureTarget;
+ const GLenum mWrapMode;
+ const gfx::IntSize mSize;
+};
+
+class EGLImageTextureHost : public TextureHost
+{
+public:
+ EGLImageTextureHost(TextureFlags aFlags,
+ EGLImage aImage,
+ EGLSync aSync,
+ gfx::IntSize aSize,
+ bool hasAlpha);
+
+ virtual ~EGLImageTextureHost();
+
+ // We don't own anything.
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override { return mSize; }
+
+ virtual const char* Name() override { return "EGLImageTextureHost"; }
+
+protected:
+ const EGLImage mImage;
+ const EGLSync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<EGLImageTextureSource> mTextureSource;
+};
+
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTUREOGL_H */
diff --git a/gfx/layers/opengl/TexturePoolOGL.cpp b/gfx/layers/opengl/TexturePoolOGL.cpp
new file mode 100644
index 000000000..8ee3b4cbb
--- /dev/null
+++ b/gfx/layers/opengl/TexturePoolOGL.cpp
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TexturePoolOGL.h"
+#include <stdlib.h> // for malloc
+#include "GLContext.h" // for GLContext
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_ASSERTION, NS_ERROR, etc
+#include "nsDeque.h" // for nsDeque
+
+#define TEXTURE_POOL_SIZE 10
+
+namespace mozilla {
+namespace gl {
+
+static GLContext* sActiveContext = nullptr;
+
+static Monitor* sMonitor = nullptr;
+static nsDeque* sTextures = nullptr;
+
+GLuint TexturePoolOGL::AcquireTexture()
+{
+ NS_ASSERTION(sMonitor, "not initialized");
+
+ MonitorAutoLock lock(*sMonitor);
+
+ if (!sActiveContext) {
+ // Wait for a context
+ sMonitor->Wait();
+
+ if (!sActiveContext)
+ return 0;
+ }
+
+ GLuint texture = 0;
+ if (sActiveContext->IsOwningThreadCurrent()) {
+ sActiveContext->MakeCurrent();
+
+ sActiveContext->fGenTextures(1, &texture);
+ } else {
+ while (sTextures->GetSize() == 0) {
+ NS_WARNING("Waiting for texture");
+ sMonitor->Wait();
+ }
+
+ GLuint* popped = (GLuint*) sTextures->Pop();
+ if (!popped) {
+ NS_ERROR("Failed to pop texture pool item");
+ return 0;
+ }
+
+ texture = *popped;
+ delete popped;
+
+ NS_ASSERTION(texture, "Failed to retrieve texture from pool");
+ }
+
+ return texture;
+}
+
+static void Clear()
+{
+ if (!sActiveContext)
+ return;
+
+ sActiveContext->MakeCurrent();
+
+ GLuint* item;
+ while (sTextures->GetSize()) {
+ item = (GLuint*)sTextures->Pop();
+ sActiveContext->fDeleteTextures(1, item);
+ delete item;
+ }
+}
+
+void TexturePoolOGL::Fill(GLContext* aContext)
+{
+ NS_ASSERTION(aContext, "NULL GLContext");
+ NS_ASSERTION(sMonitor, "not initialized");
+
+ MonitorAutoLock lock(*sMonitor);
+
+ if (sActiveContext != aContext) {
+ Clear();
+ sActiveContext = aContext;
+ }
+
+ if (sTextures->GetSize() == TEXTURE_POOL_SIZE)
+ return;
+
+ sActiveContext->MakeCurrent();
+
+ GLuint* texture = nullptr;
+ while (sTextures->GetSize() < TEXTURE_POOL_SIZE) {
+ texture = (GLuint*)malloc(sizeof(GLuint));
+ sActiveContext->fGenTextures(1, texture);
+ sTextures->Push((void*) texture);
+ }
+
+ sMonitor->NotifyAll();
+}
+
+GLContext* TexturePoolOGL::GetGLContext()
+{
+ return sActiveContext;
+}
+
+void TexturePoolOGL::Init()
+{
+ sMonitor = new Monitor("TexturePoolOGL.sMonitor");
+ sTextures = new nsDeque();
+}
+
+void TexturePoolOGL::Shutdown()
+{
+ delete sMonitor;
+ delete sTextures;
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/gfx/layers/opengl/TexturePoolOGL.h b/gfx/layers/opengl/TexturePoolOGL.h
new file mode 100644
index 000000000..136364e8c
--- /dev/null
+++ b/gfx/layers/opengl/TexturePoolOGL.h
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEXTUREPOOLOGL_H
+#define GFX_TEXTUREPOOLOGL_H
+
+#include "GLContextTypes.h" // for GLContext, GLuint
+
+namespace mozilla {
+namespace gl {
+
+// A texture pool for for the on-screen GLContext. The main purpose of this class
+// is to provide the ability to easily allocate an on-screen texture from the
+// content thread. The unfortunate nature of the SurfaceTexture API (see AndroidSurfaceTexture)
+// necessitates this.
+class TexturePoolOGL
+{
+public:
+ // Get a new texture from the pool. Will block
+ // and wait for one to be created if necessary
+ static GLuint AcquireTexture();
+
+ // Called by the active LayerManagerOGL to fill
+ // the pool
+ static void Fill(GLContext* aContext);
+
+ static GLContext* GetGLContext();
+
+ // Initializes the pool, but does not fill it. Called by gfxPlatform init.
+ static void Init();
+
+ // Clears all internal data structures in preparation for shutdown
+ static void Shutdown();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GFX_TEXTUREPOOLOGL_H
diff --git a/gfx/layers/opengl/X11TextureSourceOGL.cpp b/gfx/layers/opengl/X11TextureSourceOGL.cpp
new file mode 100644
index 000000000..dbed66b61
--- /dev/null
+++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef GL_PROVIDER_GLX
+
+#include "X11TextureSourceOGL.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceOGL::X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+ , mTexture(0)
+ , mUpdated(false)
+{
+}
+
+X11TextureSourceOGL::~X11TextureSourceOGL()
+{
+ DeallocateDeviceData();
+}
+
+void
+X11TextureSourceOGL::DeallocateDeviceData()
+{
+ if (mTexture) {
+ if (gl() && gl()->MakeCurrent()) {
+ gl::sGLXLibrary.ReleaseTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ gl()->fDeleteTextures(1, &mTexture);
+ mTexture = 0;
+ }
+ }
+}
+
+void
+X11TextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ gl()->fActiveTexture(aTextureUnit);
+
+ if (!mTexture) {
+ gl()->fGenTextures(1, &mTexture);
+
+ gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+
+ gl::sGLXLibrary.BindTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ } else {
+ gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+ if (mUpdated) {
+ gl::sGLXLibrary.UpdateTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
+ mUpdated = false;
+ }
+ }
+
+ ApplySamplingFilterToBoundTexture(gl(), aSamplingFilter, LOCAL_GL_TEXTURE_2D);
+}
+
+IntSize
+X11TextureSourceOGL::GetSize() const
+{
+ return mSurface->GetSize();
+}
+
+SurfaceFormat
+X11TextureSourceOGL::GetFormat() const {
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
+}
+
+void
+X11TextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (mCompositor == glCompositor) {
+ return;
+ }
+ DeallocateDeviceData();
+ if (glCompositor) {
+ mCompositor = glCompositor;
+ }
+}
+
+gl::GLContext*
+X11TextureSourceOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+SurfaceFormat
+X11TextureSourceOGL::ContentTypeToSurfaceFormat(gfxContentType aType)
+{
+ // X11 uses a switched format and the OGL compositor
+ // doesn't support ALPHA / A8.
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::R8G8B8X8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::R8G8B8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+}
+}
+
+#endif
diff --git a/gfx/layers/opengl/X11TextureSourceOGL.h b/gfx/layers/opengl/X11TextureSourceOGL.h
new file mode 100644
index 000000000..505847403
--- /dev/null
+++ b/gfx/layers/opengl/X11TextureSourceOGL.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEOGL__H
+#define MOZILLA_GFX_X11TEXTURESOURCEOGL__H
+
+#ifdef GL_PROVIDER_GLX
+
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceOGL
+ : public TextureSourceOGL
+ , public X11TextureSource
+{
+public:
+ X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface);
+ ~X11TextureSourceOGL();
+
+ virtual X11TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual bool IsValid() const override { return !!gl(); } ;
+
+ virtual void BindTexture(GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_2D; }
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual void Updated() override { mUpdated = true; }
+
+ gl::GLContext* gl() const;
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+ GLuint mTexture;
+ bool mUpdated;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEOGL__H
diff --git a/gfx/layers/protobuf/LayerScopePacket.pb.cc b/gfx/layers/protobuf/LayerScopePacket.pb.cc
new file mode 100644
index 000000000..c9a1bd94e
--- /dev/null
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.cc
@@ -0,0 +1,6802 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: LayerScopePacket.proto
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "LayerScopePacket.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+
+namespace mozilla {
+namespace layers {
+namespace layerscope {
+
+void protobuf_ShutdownFile_LayerScopePacket_2eproto() {
+ delete FramePacket::default_instance_;
+ delete ColorPacket::default_instance_;
+ delete TexturePacket::default_instance_;
+ delete TexturePacket_Rect::default_instance_;
+ delete TexturePacket_Size::default_instance_;
+ delete TexturePacket_Matrix::default_instance_;
+ delete TexturePacket_EffectMask::default_instance_;
+ delete LayersPacket::default_instance_;
+ delete LayersPacket_Layer::default_instance_;
+ delete LayersPacket_Layer_Size::default_instance_;
+ delete LayersPacket_Layer_Rect::default_instance_;
+ delete LayersPacket_Layer_Region::default_instance_;
+ delete LayersPacket_Layer_Matrix::default_instance_;
+ delete LayersPacket_Layer_Shadow::default_instance_;
+ delete MetaPacket::default_instance_;
+ delete DrawPacket::default_instance_;
+ delete DrawPacket_Rect::default_instance_;
+ delete Packet::default_instance_;
+ delete CommandPacket::default_instance_;
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+void protobuf_AddDesc_LayerScopePacket_2eproto_impl() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#else
+void protobuf_AddDesc_LayerScopePacket_2eproto() {
+ static bool already_here = false;
+ if (already_here) return;
+ already_here = true;
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#endif
+ FramePacket::default_instance_ = new FramePacket();
+ ColorPacket::default_instance_ = new ColorPacket();
+ TexturePacket::default_instance_ = new TexturePacket();
+ TexturePacket_Rect::default_instance_ = new TexturePacket_Rect();
+ TexturePacket_Size::default_instance_ = new TexturePacket_Size();
+ TexturePacket_Matrix::default_instance_ = new TexturePacket_Matrix();
+ TexturePacket_EffectMask::default_instance_ = new TexturePacket_EffectMask();
+ LayersPacket::default_instance_ = new LayersPacket();
+ LayersPacket_Layer::default_instance_ = new LayersPacket_Layer();
+ LayersPacket_Layer_Size::default_instance_ = new LayersPacket_Layer_Size();
+ LayersPacket_Layer_Rect::default_instance_ = new LayersPacket_Layer_Rect();
+ LayersPacket_Layer_Region::default_instance_ = new LayersPacket_Layer_Region();
+ LayersPacket_Layer_Matrix::default_instance_ = new LayersPacket_Layer_Matrix();
+ LayersPacket_Layer_Shadow::default_instance_ = new LayersPacket_Layer_Shadow();
+ MetaPacket::default_instance_ = new MetaPacket();
+ DrawPacket::default_instance_ = new DrawPacket();
+ DrawPacket_Rect::default_instance_ = new DrawPacket_Rect();
+ Packet::default_instance_ = new Packet();
+ CommandPacket::default_instance_ = new CommandPacket();
+ FramePacket::default_instance_->InitAsDefaultInstance();
+ ColorPacket::default_instance_->InitAsDefaultInstance();
+ TexturePacket::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Rect::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Size::default_instance_->InitAsDefaultInstance();
+ TexturePacket_Matrix::default_instance_->InitAsDefaultInstance();
+ TexturePacket_EffectMask::default_instance_->InitAsDefaultInstance();
+ LayersPacket::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Size::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Rect::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Region::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Matrix::default_instance_->InitAsDefaultInstance();
+ LayersPacket_Layer_Shadow::default_instance_->InitAsDefaultInstance();
+ MetaPacket::default_instance_->InitAsDefaultInstance();
+ DrawPacket::default_instance_->InitAsDefaultInstance();
+ DrawPacket_Rect::default_instance_->InitAsDefaultInstance();
+ Packet::default_instance_->InitAsDefaultInstance();
+ CommandPacket::default_instance_->InitAsDefaultInstance();
+ ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_LayerScopePacket_2eproto);
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_LayerScopePacket_2eproto_once_);
+void protobuf_AddDesc_LayerScopePacket_2eproto() {
+ ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_LayerScopePacket_2eproto_once_,
+ &protobuf_AddDesc_LayerScopePacket_2eproto_impl);
+}
+#else
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_LayerScopePacket_2eproto {
+ StaticDescriptorInitializer_LayerScopePacket_2eproto() {
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+ }
+} static_descriptor_initializer_LayerScopePacket_2eproto_;
+#endif
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int FramePacket::kValueFieldNumber;
+const int FramePacket::kScaleFieldNumber;
+#endif // !_MSC_VER
+
+FramePacket::FramePacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.FramePacket)
+}
+
+void FramePacket::InitAsDefaultInstance() {
+}
+
+FramePacket::FramePacket(const FramePacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.FramePacket)
+}
+
+void FramePacket::SharedCtor() {
+ _cached_size_ = 0;
+ value_ = GOOGLE_ULONGLONG(0);
+ scale_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+FramePacket::~FramePacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.FramePacket)
+ SharedDtor();
+}
+
+void FramePacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void FramePacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const FramePacket& FramePacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+FramePacket* FramePacket::default_instance_ = NULL;
+
+FramePacket* FramePacket::New() const {
+ return new FramePacket;
+}
+
+void FramePacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<FramePacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(value_, scale_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool FramePacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.FramePacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional uint64 value = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &value_)));
+ set_has_value();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_scale;
+ break;
+ }
+
+ // optional float scale = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_scale:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &scale_)));
+ set_has_scale();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.FramePacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.FramePacket)
+ return false;
+#undef DO_
+}
+
+void FramePacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.FramePacket)
+ // optional uint64 value = 1;
+ if (has_value()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->value(), output);
+ }
+
+ // optional float scale = 2;
+ if (has_scale()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->scale(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.FramePacket)
+}
+
+int FramePacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional uint64 value = 1;
+ if (has_value()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->value());
+ }
+
+ // optional float scale = 2;
+ if (has_scale()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void FramePacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const FramePacket*>(&from));
+}
+
+void FramePacket::MergeFrom(const FramePacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_value()) {
+ set_value(from.value());
+ }
+ if (from.has_scale()) {
+ set_scale(from.scale());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void FramePacket::CopyFrom(const FramePacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FramePacket::IsInitialized() const {
+
+ return true;
+}
+
+void FramePacket::Swap(FramePacket* other) {
+ if (other != this) {
+ std::swap(value_, other->value_);
+ std::swap(scale_, other->scale_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string FramePacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.FramePacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int ColorPacket::kLayerrefFieldNumber;
+const int ColorPacket::kWidthFieldNumber;
+const int ColorPacket::kHeightFieldNumber;
+const int ColorPacket::kColorFieldNumber;
+#endif // !_MSC_VER
+
+ColorPacket::ColorPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.ColorPacket)
+}
+
+void ColorPacket::InitAsDefaultInstance() {
+}
+
+ColorPacket::ColorPacket(const ColorPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.ColorPacket)
+}
+
+void ColorPacket::SharedCtor() {
+ _cached_size_ = 0;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ width_ = 0u;
+ height_ = 0u;
+ color_ = 0u;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+ColorPacket::~ColorPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.ColorPacket)
+ SharedDtor();
+}
+
+void ColorPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void ColorPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ColorPacket& ColorPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+ColorPacket* ColorPacket::default_instance_ = NULL;
+
+ColorPacket* ColorPacket::New() const {
+ return new ColorPacket;
+}
+
+void ColorPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<ColorPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(layerref_, color_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool ColorPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.ColorPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required uint64 layerref = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_width;
+ break;
+ }
+
+ // optional uint32 width = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_width:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &width_)));
+ set_has_width();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_height;
+ break;
+ }
+
+ // optional uint32 height = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_height:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &height_)));
+ set_has_height();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_color;
+ break;
+ }
+
+ // optional uint32 color = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_color:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &color_)));
+ set_has_color();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.ColorPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.ColorPacket)
+ return false;
+#undef DO_
+}
+
+void ColorPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.ColorPacket)
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output);
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output);
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output);
+ }
+
+ // optional uint32 color = 4;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->color(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.ColorPacket)
+}
+
+int ColorPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->width());
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->height());
+ }
+
+ // optional uint32 color = 4;
+ if (has_color()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->color());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void ColorPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const ColorPacket*>(&from));
+}
+
+void ColorPacket::MergeFrom(const ColorPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ if (from.has_width()) {
+ set_width(from.width());
+ }
+ if (from.has_height()) {
+ set_height(from.height());
+ }
+ if (from.has_color()) {
+ set_color(from.color());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void ColorPacket::CopyFrom(const ColorPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ColorPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void ColorPacket::Swap(ColorPacket* other) {
+ if (other != this) {
+ std::swap(layerref_, other->layerref_);
+ std::swap(width_, other->width_);
+ std::swap(height_, other->height_);
+ std::swap(color_, other->color_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string ColorPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.ColorPacket";
+}
+
+
+// ===================================================================
+
+bool TexturePacket_Filter_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const TexturePacket_Filter TexturePacket::GOOD;
+const TexturePacket_Filter TexturePacket::LINEAR;
+const TexturePacket_Filter TexturePacket::POINT;
+const TexturePacket_Filter TexturePacket::Filter_MIN;
+const TexturePacket_Filter TexturePacket::Filter_MAX;
+const int TexturePacket::Filter_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int TexturePacket_Rect::kXFieldNumber;
+const int TexturePacket_Rect::kYFieldNumber;
+const int TexturePacket_Rect::kWFieldNumber;
+const int TexturePacket_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Rect::TexturePacket_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+void TexturePacket_Rect::InitAsDefaultInstance() {
+}
+
+TexturePacket_Rect::TexturePacket_Rect(const TexturePacket_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+void TexturePacket_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Rect::~TexturePacket_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Rect)
+ SharedDtor();
+}
+
+void TexturePacket_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Rect& TexturePacket_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Rect* TexturePacket_Rect::default_instance_ = NULL;
+
+TexturePacket_Rect* TexturePacket_Rect::New() const {
+ return new TexturePacket_Rect;
+}
+
+void TexturePacket_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional float x = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_y;
+ break;
+ }
+
+ // optional float y = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_w;
+ break;
+ }
+
+ // optional float w = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(37)) goto parse_h;
+ break;
+ }
+
+ // optional float h = 4;
+ case 4: {
+ if (tag == 37) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Rect)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Rect)
+ // optional float x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->x(), output);
+ }
+
+ // optional float y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->y(), output);
+ }
+
+ // optional float w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->w(), output);
+ }
+
+ // optional float h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Rect)
+}
+
+int TexturePacket_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional float x = 1;
+ if (has_x()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float y = 2;
+ if (has_y()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float w = 3;
+ if (has_w()) {
+ total_size += 1 + 4;
+ }
+
+ // optional float h = 4;
+ if (has_h()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Rect*>(&from));
+}
+
+void TexturePacket_Rect::MergeFrom(const TexturePacket_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Rect::CopyFrom(const TexturePacket_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Rect::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Rect::Swap(TexturePacket_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_Size::kWFieldNumber;
+const int TexturePacket_Size::kHFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Size::TexturePacket_Size()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+void TexturePacket_Size::InitAsDefaultInstance() {
+}
+
+TexturePacket_Size::TexturePacket_Size(const TexturePacket_Size& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+void TexturePacket_Size::SharedCtor() {
+ _cached_size_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Size::~TexturePacket_Size() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Size)
+ SharedDtor();
+}
+
+void TexturePacket_Size::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Size::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Size& TexturePacket_Size::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Size* TexturePacket_Size::default_instance_ = NULL;
+
+TexturePacket_Size* TexturePacket_Size::New() const {
+ return new TexturePacket_Size;
+}
+
+void TexturePacket_Size::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Size*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(w_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Size::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Size)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 w = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Size)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Size)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Size::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Size)
+ // optional int32 w = 1;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->w(), output);
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Size)
+}
+
+int TexturePacket_Size::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 w = 1;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Size::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Size*>(&from));
+}
+
+void TexturePacket_Size::MergeFrom(const TexturePacket_Size& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Size::CopyFrom(const TexturePacket_Size& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Size::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Size::Swap(TexturePacket_Size* other) {
+ if (other != this) {
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Size::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Size";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_Matrix::kIs2DFieldNumber;
+const int TexturePacket_Matrix::kIsIdFieldNumber;
+const int TexturePacket_Matrix::kMFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_Matrix::TexturePacket_Matrix()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+void TexturePacket_Matrix::InitAsDefaultInstance() {
+}
+
+TexturePacket_Matrix::TexturePacket_Matrix(const TexturePacket_Matrix& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+void TexturePacket_Matrix::SharedCtor() {
+ _cached_size_ = 0;
+ is2d_ = false;
+ isid_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_Matrix::~TexturePacket_Matrix() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.Matrix)
+ SharedDtor();
+}
+
+void TexturePacket_Matrix::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void TexturePacket_Matrix::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_Matrix& TexturePacket_Matrix::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_Matrix* TexturePacket_Matrix::default_instance_ = NULL;
+
+TexturePacket_Matrix* TexturePacket_Matrix::New() const {
+ return new TexturePacket_Matrix;
+}
+
+void TexturePacket_Matrix::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket_Matrix*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(is2d_, isid_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ m_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_Matrix::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.Matrix)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool is2D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &is2d_)));
+ set_has_is2d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_isId;
+ break;
+ }
+
+ // optional bool isId = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_isId:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &isid_)));
+ set_has_isid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ break;
+ }
+
+ // repeated float m = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_m:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_m())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_m())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.Matrix)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.Matrix)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_Matrix::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.Matrix)
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->is2d(), output);
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->isid(), output);
+ }
+
+ // repeated float m = 3;
+ for (int i = 0; i < this->m_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->m(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.Matrix)
+}
+
+int TexturePacket_Matrix::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ // repeated float m = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->m_size();
+ total_size += 1 * this->m_size() + data_size;
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_Matrix::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_Matrix*>(&from));
+}
+
+void TexturePacket_Matrix::MergeFrom(const TexturePacket_Matrix& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ m_.MergeFrom(from.m_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_is2d()) {
+ set_is2d(from.is2d());
+ }
+ if (from.has_isid()) {
+ set_isid(from.isid());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_Matrix::CopyFrom(const TexturePacket_Matrix& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_Matrix::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_Matrix::Swap(TexturePacket_Matrix* other) {
+ if (other != this) {
+ std::swap(is2d_, other->is2d_);
+ std::swap(isid_, other->isid_);
+ m_.Swap(&other->m_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_Matrix::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.Matrix";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket_EffectMask::kMIs3DFieldNumber;
+const int TexturePacket_EffectMask::kMSizeFieldNumber;
+const int TexturePacket_EffectMask::kMMaskTransformFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket_EffectMask::TexturePacket_EffectMask()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+void TexturePacket_EffectMask::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ msize_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Size*>(
+ ::mozilla::layers::layerscope::TexturePacket_Size::internal_default_instance());
+#else
+ msize_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Size*>(&::mozilla::layers::layerscope::TexturePacket_Size::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mmasktransform_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Matrix*>(
+ ::mozilla::layers::layerscope::TexturePacket_Matrix::internal_default_instance());
+#else
+ mmasktransform_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Matrix*>(&::mozilla::layers::layerscope::TexturePacket_Matrix::default_instance());
+#endif
+}
+
+TexturePacket_EffectMask::TexturePacket_EffectMask(const TexturePacket_EffectMask& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+void TexturePacket_EffectMask::SharedCtor() {
+ _cached_size_ = 0;
+ mis3d_ = false;
+ msize_ = NULL;
+ mmasktransform_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket_EffectMask::~TexturePacket_EffectMask() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ SharedDtor();
+}
+
+void TexturePacket_EffectMask::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete msize_;
+ delete mmasktransform_;
+ }
+}
+
+void TexturePacket_EffectMask::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket_EffectMask& TexturePacket_EffectMask::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket_EffectMask* TexturePacket_EffectMask::default_instance_ = NULL;
+
+TexturePacket_EffectMask* TexturePacket_EffectMask::New() const {
+ return new TexturePacket_EffectMask;
+}
+
+void TexturePacket_EffectMask::Clear() {
+ if (_has_bits_[0 / 32] & 7) {
+ mis3d_ = false;
+ if (has_msize()) {
+ if (msize_ != NULL) msize_->::mozilla::layers::layerscope::TexturePacket_Size::Clear();
+ }
+ if (has_mmasktransform()) {
+ if (mmasktransform_ != NULL) mmasktransform_->::mozilla::layers::layerscope::TexturePacket_Matrix::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket_EffectMask::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool mIs3D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &mis3d_)));
+ set_has_mis3d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_mSize;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_mSize:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_msize()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_mMaskTransform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_mMaskTransform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mmasktransform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ return false;
+#undef DO_
+}
+
+void TexturePacket_EffectMask::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ // optional bool mIs3D = 1;
+ if (has_mis3d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->mis3d(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ if (has_msize()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->msize(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ if (has_mmasktransform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->mmasktransform(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket.EffectMask)
+}
+
+int TexturePacket_EffectMask::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool mIs3D = 1;
+ if (has_mis3d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ if (has_msize()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->msize());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ if (has_mmasktransform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mmasktransform());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket_EffectMask::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket_EffectMask*>(&from));
+}
+
+void TexturePacket_EffectMask::MergeFrom(const TexturePacket_EffectMask& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_mis3d()) {
+ set_mis3d(from.mis3d());
+ }
+ if (from.has_msize()) {
+ mutable_msize()->::mozilla::layers::layerscope::TexturePacket_Size::MergeFrom(from.msize());
+ }
+ if (from.has_mmasktransform()) {
+ mutable_mmasktransform()->::mozilla::layers::layerscope::TexturePacket_Matrix::MergeFrom(from.mmasktransform());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket_EffectMask::CopyFrom(const TexturePacket_EffectMask& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket_EffectMask::IsInitialized() const {
+
+ return true;
+}
+
+void TexturePacket_EffectMask::Swap(TexturePacket_EffectMask* other) {
+ if (other != this) {
+ std::swap(mis3d_, other->mis3d_);
+ std::swap(msize_, other->msize_);
+ std::swap(mmasktransform_, other->mmasktransform_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket_EffectMask::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket.EffectMask";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int TexturePacket::kLayerrefFieldNumber;
+const int TexturePacket::kWidthFieldNumber;
+const int TexturePacket::kHeightFieldNumber;
+const int TexturePacket::kStrideFieldNumber;
+const int TexturePacket::kNameFieldNumber;
+const int TexturePacket::kTargetFieldNumber;
+const int TexturePacket::kDataformatFieldNumber;
+const int TexturePacket::kGlcontextFieldNumber;
+const int TexturePacket::kDataFieldNumber;
+const int TexturePacket::kMTextureCoordsFieldNumber;
+const int TexturePacket::kMPremultipliedFieldNumber;
+const int TexturePacket::kMFilterFieldNumber;
+const int TexturePacket::kIsMaskFieldNumber;
+const int TexturePacket::kMaskFieldNumber;
+#endif // !_MSC_VER
+
+TexturePacket::TexturePacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.TexturePacket)
+}
+
+void TexturePacket::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mtexturecoords_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Rect*>(
+ ::mozilla::layers::layerscope::TexturePacket_Rect::internal_default_instance());
+#else
+ mtexturecoords_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_Rect*>(&::mozilla::layers::layerscope::TexturePacket_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ mask_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_EffectMask*>(
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask::internal_default_instance());
+#else
+ mask_ = const_cast< ::mozilla::layers::layerscope::TexturePacket_EffectMask*>(&::mozilla::layers::layerscope::TexturePacket_EffectMask::default_instance());
+#endif
+}
+
+TexturePacket::TexturePacket(const TexturePacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.TexturePacket)
+}
+
+void TexturePacket::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ width_ = 0u;
+ height_ = 0u;
+ stride_ = 0u;
+ name_ = 0u;
+ target_ = 0u;
+ dataformat_ = 0u;
+ glcontext_ = GOOGLE_ULONGLONG(0);
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ mtexturecoords_ = NULL;
+ mpremultiplied_ = false;
+ mfilter_ = 0;
+ ismask_ = false;
+ mask_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+TexturePacket::~TexturePacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.TexturePacket)
+ SharedDtor();
+}
+
+void TexturePacket::SharedDtor() {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete data_;
+ }
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete mtexturecoords_;
+ delete mask_;
+ }
+}
+
+void TexturePacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const TexturePacket& TexturePacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+TexturePacket* TexturePacket::default_instance_ = NULL;
+
+TexturePacket* TexturePacket::New() const {
+ return new TexturePacket;
+}
+
+void TexturePacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<TexturePacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 255) {
+ ZR_(layerref_, glcontext_);
+ }
+ if (_has_bits_[8 / 32] & 16128) {
+ ZR_(mfilter_, ismask_);
+ if (has_data()) {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_->clear();
+ }
+ }
+ if (has_mtexturecoords()) {
+ if (mtexturecoords_ != NULL) mtexturecoords_->::mozilla::layers::layerscope::TexturePacket_Rect::Clear();
+ }
+ if (has_mask()) {
+ if (mask_ != NULL) mask_->::mozilla::layers::layerscope::TexturePacket_EffectMask::Clear();
+ }
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool TexturePacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.TexturePacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(16383);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required uint64 layerref = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_width;
+ break;
+ }
+
+ // optional uint32 width = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_width:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &width_)));
+ set_has_width();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_height;
+ break;
+ }
+
+ // optional uint32 height = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_height:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &height_)));
+ set_has_height();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_stride;
+ break;
+ }
+
+ // optional uint32 stride = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_stride:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &stride_)));
+ set_has_stride();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(40)) goto parse_name;
+ break;
+ }
+
+ // optional uint32 name = 5;
+ case 5: {
+ if (tag == 40) {
+ parse_name:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &name_)));
+ set_has_name();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(48)) goto parse_target;
+ break;
+ }
+
+ // optional uint32 target = 6;
+ case 6: {
+ if (tag == 48) {
+ parse_target:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &target_)));
+ set_has_target();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_dataformat;
+ break;
+ }
+
+ // optional uint32 dataformat = 7;
+ case 7: {
+ if (tag == 56) {
+ parse_dataformat:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &dataformat_)));
+ set_has_dataformat();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(64)) goto parse_glcontext;
+ break;
+ }
+
+ // optional uint64 glcontext = 8;
+ case 8: {
+ if (tag == 64) {
+ parse_glcontext:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &glcontext_)));
+ set_has_glcontext();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(74)) goto parse_data;
+ break;
+ }
+
+ // optional bytes data = 9;
+ case 9: {
+ if (tag == 74) {
+ parse_data:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+ input, this->mutable_data()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(82)) goto parse_mTextureCoords;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ case 10: {
+ if (tag == 82) {
+ parse_mTextureCoords:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mtexturecoords()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(88)) goto parse_mPremultiplied;
+ break;
+ }
+
+ // optional bool mPremultiplied = 11;
+ case 11: {
+ if (tag == 88) {
+ parse_mPremultiplied:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &mpremultiplied_)));
+ set_has_mpremultiplied();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(96)) goto parse_mFilter;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ case 12: {
+ if (tag == 96) {
+ parse_mFilter:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::TexturePacket_Filter_IsValid(value)) {
+ set_mfilter(static_cast< ::mozilla::layers::layerscope::TexturePacket_Filter >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(160)) goto parse_isMask;
+ break;
+ }
+
+ // optional bool isMask = 20;
+ case 20: {
+ if (tag == 160) {
+ parse_isMask:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &ismask_)));
+ set_has_ismask();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(170)) goto parse_mask;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ case 21: {
+ if (tag == 170) {
+ parse_mask:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_mask()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.TexturePacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.TexturePacket)
+ return false;
+#undef DO_
+}
+
+void TexturePacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.TexturePacket)
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output);
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output);
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output);
+ }
+
+ // optional uint32 stride = 4;
+ if (has_stride()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->stride(), output);
+ }
+
+ // optional uint32 name = 5;
+ if (has_name()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(5, this->name(), output);
+ }
+
+ // optional uint32 target = 6;
+ if (has_target()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(6, this->target(), output);
+ }
+
+ // optional uint32 dataformat = 7;
+ if (has_dataformat()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(7, this->dataformat(), output);
+ }
+
+ // optional uint64 glcontext = 8;
+ if (has_glcontext()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(8, this->glcontext(), output);
+ }
+
+ // optional bytes data = 9;
+ if (has_data()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
+ 9, this->data(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ if (has_mtexturecoords()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 10, this->mtexturecoords(), output);
+ }
+
+ // optional bool mPremultiplied = 11;
+ if (has_mpremultiplied()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(11, this->mpremultiplied(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ if (has_mfilter()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 12, this->mfilter(), output);
+ }
+
+ // optional bool isMask = 20;
+ if (has_ismask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(20, this->ismask(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ if (has_mask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 21, this->mask(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.TexturePacket)
+}
+
+int TexturePacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required uint64 layerref = 1;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ // optional uint32 width = 2;
+ if (has_width()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->width());
+ }
+
+ // optional uint32 height = 3;
+ if (has_height()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->height());
+ }
+
+ // optional uint32 stride = 4;
+ if (has_stride()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->stride());
+ }
+
+ // optional uint32 name = 5;
+ if (has_name()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->name());
+ }
+
+ // optional uint32 target = 6;
+ if (has_target()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->target());
+ }
+
+ // optional uint32 dataformat = 7;
+ if (has_dataformat()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->dataformat());
+ }
+
+ // optional uint64 glcontext = 8;
+ if (has_glcontext()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->glcontext());
+ }
+
+ }
+ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ // optional bytes data = 9;
+ if (has_data()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::BytesSize(
+ this->data());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ if (has_mtexturecoords()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mtexturecoords());
+ }
+
+ // optional bool mPremultiplied = 11;
+ if (has_mpremultiplied()) {
+ total_size += 1 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ if (has_mfilter()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->mfilter());
+ }
+
+ // optional bool isMask = 20;
+ if (has_ismask()) {
+ total_size += 2 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ if (has_mask()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->mask());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void TexturePacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const TexturePacket*>(&from));
+}
+
+void TexturePacket::MergeFrom(const TexturePacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ if (from.has_width()) {
+ set_width(from.width());
+ }
+ if (from.has_height()) {
+ set_height(from.height());
+ }
+ if (from.has_stride()) {
+ set_stride(from.stride());
+ }
+ if (from.has_name()) {
+ set_name(from.name());
+ }
+ if (from.has_target()) {
+ set_target(from.target());
+ }
+ if (from.has_dataformat()) {
+ set_dataformat(from.dataformat());
+ }
+ if (from.has_glcontext()) {
+ set_glcontext(from.glcontext());
+ }
+ }
+ if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ if (from.has_data()) {
+ set_data(from.data());
+ }
+ if (from.has_mtexturecoords()) {
+ mutable_mtexturecoords()->::mozilla::layers::layerscope::TexturePacket_Rect::MergeFrom(from.mtexturecoords());
+ }
+ if (from.has_mpremultiplied()) {
+ set_mpremultiplied(from.mpremultiplied());
+ }
+ if (from.has_mfilter()) {
+ set_mfilter(from.mfilter());
+ }
+ if (from.has_ismask()) {
+ set_ismask(from.ismask());
+ }
+ if (from.has_mask()) {
+ mutable_mask()->::mozilla::layers::layerscope::TexturePacket_EffectMask::MergeFrom(from.mask());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void TexturePacket::CopyFrom(const TexturePacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool TexturePacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void TexturePacket::Swap(TexturePacket* other) {
+ if (other != this) {
+ std::swap(layerref_, other->layerref_);
+ std::swap(width_, other->width_);
+ std::swap(height_, other->height_);
+ std::swap(stride_, other->stride_);
+ std::swap(name_, other->name_);
+ std::swap(target_, other->target_);
+ std::swap(dataformat_, other->dataformat_);
+ std::swap(glcontext_, other->glcontext_);
+ std::swap(data_, other->data_);
+ std::swap(mtexturecoords_, other->mtexturecoords_);
+ std::swap(mpremultiplied_, other->mpremultiplied_);
+ std::swap(mfilter_, other->mfilter_);
+ std::swap(ismask_, other->ismask_);
+ std::swap(mask_, other->mask_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string TexturePacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.TexturePacket";
+}
+
+
+// ===================================================================
+
+bool LayersPacket_Layer_LayerType_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_LayerType LayersPacket_Layer::UnknownLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerManager;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ContainerLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::PaintedLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::CanvasLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ImageLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ColorLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::RefLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::ReadbackLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerType_MIN;
+const LayersPacket_Layer_LayerType LayersPacket_Layer::LayerType_MAX;
+const int LayersPacket_Layer::LayerType_ARRAYSIZE;
+#endif // _MSC_VER
+bool LayersPacket_Layer_ScrollingDirect_IsValid(int value) {
+ switch(value) {
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::VERTICAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::HORIZONTAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::ScrollingDirect_MIN;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::ScrollingDirect_MAX;
+const int LayersPacket_Layer::ScrollingDirect_ARRAYSIZE;
+#endif // _MSC_VER
+bool LayersPacket_Layer_Filter_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_FAST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GOOD;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BEST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_NEAREST;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BILINEAR;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GAUSSIAN;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_SENTINEL;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_LINEAR;
+const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_POINT;
+const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MIN;
+const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MAX;
+const int LayersPacket_Layer::Filter_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Size::kWFieldNumber;
+const int LayersPacket_Layer_Size::kHFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Size::LayersPacket_Layer_Size()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+void LayersPacket_Layer_Size::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Size::LayersPacket_Layer_Size(const LayersPacket_Layer_Size& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+void LayersPacket_Layer_Size::SharedCtor() {
+ _cached_size_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Size::~LayersPacket_Layer_Size() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Size::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Size::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Size& LayersPacket_Layer_Size::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Size* LayersPacket_Layer_Size::default_instance_ = NULL;
+
+LayersPacket_Layer_Size* LayersPacket_Layer_Size::New() const {
+ return new LayersPacket_Layer_Size;
+}
+
+void LayersPacket_Layer_Size::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Size*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(w_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Size::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 w = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Size::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ // optional int32 w = 1;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->w(), output);
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+}
+
+int LayersPacket_Layer_Size::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 w = 1;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 2;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Size::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Size*>(&from));
+}
+
+void LayersPacket_Layer_Size::MergeFrom(const LayersPacket_Layer_Size& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Size::CopyFrom(const LayersPacket_Layer_Size& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Size::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Size::Swap(LayersPacket_Layer_Size* other) {
+ if (other != this) {
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Size::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Size";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Rect::kXFieldNumber;
+const int LayersPacket_Layer_Rect::kYFieldNumber;
+const int LayersPacket_Layer_Rect::kWFieldNumber;
+const int LayersPacket_Layer_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Rect::LayersPacket_Layer_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+void LayersPacket_Layer_Rect::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Rect::LayersPacket_Layer_Rect(const LayersPacket_Layer_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+void LayersPacket_Layer_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Rect::~LayersPacket_Layer_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Rect& LayersPacket_Layer_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Rect* LayersPacket_Layer_Rect::default_instance_ = NULL;
+
+LayersPacket_Layer_Rect* LayersPacket_Layer_Rect::New() const {
+ return new LayersPacket_Layer_Rect;
+}
+
+void LayersPacket_Layer_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional int32 x = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_y;
+ break;
+ }
+
+ // optional int32 y = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_w;
+ break;
+ }
+
+ // optional int32 w = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_h;
+ break;
+ }
+
+ // optional int32 h = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ // optional int32 x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->x(), output);
+ }
+
+ // optional int32 y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->y(), output);
+ }
+
+ // optional int32 w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->w(), output);
+ }
+
+ // optional int32 h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+}
+
+int LayersPacket_Layer_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional int32 x = 1;
+ if (has_x()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->x());
+ }
+
+ // optional int32 y = 2;
+ if (has_y()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->y());
+ }
+
+ // optional int32 w = 3;
+ if (has_w()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->w());
+ }
+
+ // optional int32 h = 4;
+ if (has_h()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->h());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Rect*>(&from));
+}
+
+void LayersPacket_Layer_Rect::MergeFrom(const LayersPacket_Layer_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Rect::CopyFrom(const LayersPacket_Layer_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Rect::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Rect::Swap(LayersPacket_Layer_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Region::kRFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Region::LayersPacket_Layer_Region()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+void LayersPacket_Layer_Region::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Region::LayersPacket_Layer_Region(const LayersPacket_Layer_Region& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+void LayersPacket_Layer_Region::SharedCtor() {
+ _cached_size_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Region::~LayersPacket_Layer_Region() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Region::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Region::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Region& LayersPacket_Layer_Region::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Region* LayersPacket_Layer_Region::default_instance_ = NULL;
+
+LayersPacket_Layer_Region* LayersPacket_Layer_Region::New() const {
+ return new LayersPacket_Layer_Region;
+}
+
+void LayersPacket_Layer_Region::Clear() {
+ r_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Region::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ case 1: {
+ if (tag == 10) {
+ parse_r:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_r()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_r;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Region::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ for (int i = 0; i < this->r_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->r(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+}
+
+int LayersPacket_Layer_Region::ByteSize() const {
+ int total_size = 0;
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ total_size += 1 * this->r_size();
+ for (int i = 0; i < this->r_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->r(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Region::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Region*>(&from));
+}
+
+void LayersPacket_Layer_Region::MergeFrom(const LayersPacket_Layer_Region& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ r_.MergeFrom(from.r_);
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Region::CopyFrom(const LayersPacket_Layer_Region& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Region::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Region::Swap(LayersPacket_Layer_Region* other) {
+ if (other != this) {
+ r_.Swap(&other->r_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Region::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Region";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Matrix::kIs2DFieldNumber;
+const int LayersPacket_Layer_Matrix::kIsIdFieldNumber;
+const int LayersPacket_Layer_Matrix::kMFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Matrix::LayersPacket_Layer_Matrix()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+void LayersPacket_Layer_Matrix::InitAsDefaultInstance() {
+}
+
+LayersPacket_Layer_Matrix::LayersPacket_Layer_Matrix(const LayersPacket_Layer_Matrix& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+void LayersPacket_Layer_Matrix::SharedCtor() {
+ _cached_size_ = 0;
+ is2d_ = false;
+ isid_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Matrix::~LayersPacket_Layer_Matrix() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Matrix::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket_Layer_Matrix::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Matrix& LayersPacket_Layer_Matrix::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Matrix* LayersPacket_Layer_Matrix::default_instance_ = NULL;
+
+LayersPacket_Layer_Matrix* LayersPacket_Layer_Matrix::New() const {
+ return new LayersPacket_Layer_Matrix;
+}
+
+void LayersPacket_Layer_Matrix::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer_Matrix*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(is2d_, isid_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ m_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Matrix::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool is2D = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &is2d_)));
+ set_has_is2d();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_isId;
+ break;
+ }
+
+ // optional bool isId = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_isId:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &isid_)));
+ set_has_isid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ break;
+ }
+
+ // repeated float m = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_m:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_m())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_m())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_m;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Matrix::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->is2d(), output);
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->isid(), output);
+ }
+
+ // repeated float m = 3;
+ for (int i = 0; i < this->m_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->m(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+}
+
+int LayersPacket_Layer_Matrix::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool is2D = 1;
+ if (has_is2d()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool isId = 2;
+ if (has_isid()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ // repeated float m = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->m_size();
+ total_size += 1 * this->m_size() + data_size;
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Matrix::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Matrix*>(&from));
+}
+
+void LayersPacket_Layer_Matrix::MergeFrom(const LayersPacket_Layer_Matrix& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ m_.MergeFrom(from.m_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_is2d()) {
+ set_is2d(from.is2d());
+ }
+ if (from.has_isid()) {
+ set_isid(from.isid());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Matrix::CopyFrom(const LayersPacket_Layer_Matrix& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Matrix::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Matrix::Swap(LayersPacket_Layer_Matrix* other) {
+ if (other != this) {
+ std::swap(is2d_, other->is2d_);
+ std::swap(isid_, other->isid_);
+ m_.Swap(&other->m_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Matrix::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Matrix";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer_Shadow::kClipFieldNumber;
+const int LayersPacket_Layer_Shadow::kTransformFieldNumber;
+const int LayersPacket_Layer_Shadow::kVRegionFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer_Shadow::LayersPacket_Layer_Shadow()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+void LayersPacket_Layer_Shadow::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect::internal_default_instance());
+#else
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::internal_default_instance());
+#else
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+}
+
+LayersPacket_Layer_Shadow::LayersPacket_Layer_Shadow(const LayersPacket_Layer_Shadow& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+void LayersPacket_Layer_Shadow::SharedCtor() {
+ _cached_size_ = 0;
+ clip_ = NULL;
+ transform_ = NULL;
+ vregion_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer_Shadow::~LayersPacket_Layer_Shadow() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ SharedDtor();
+}
+
+void LayersPacket_Layer_Shadow::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete clip_;
+ delete transform_;
+ delete vregion_;
+ }
+}
+
+void LayersPacket_Layer_Shadow::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer_Shadow& LayersPacket_Layer_Shadow::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer_Shadow* LayersPacket_Layer_Shadow::default_instance_ = NULL;
+
+LayersPacket_Layer_Shadow* LayersPacket_Layer_Shadow::New() const {
+ return new LayersPacket_Layer_Shadow;
+}
+
+void LayersPacket_Layer_Shadow::Clear() {
+ if (_has_bits_[0 / 32] & 7) {
+ if (has_clip()) {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ }
+ if (has_transform()) {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ }
+ if (has_vregion()) {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer_Shadow::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_clip()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_transform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_transform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_transform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_vRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_vRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer_Shadow::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ if (has_clip()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->clip(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ if (has_transform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->transform(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ if (has_vregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->vregion(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+}
+
+int LayersPacket_Layer_Shadow::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ if (has_clip()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->clip());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ if (has_transform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->transform());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ if (has_vregion()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vregion());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer_Shadow::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer_Shadow*>(&from));
+}
+
+void LayersPacket_Layer_Shadow::MergeFrom(const LayersPacket_Layer_Shadow& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_clip()) {
+ mutable_clip()->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::MergeFrom(from.clip());
+ }
+ if (from.has_transform()) {
+ mutable_transform()->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::MergeFrom(from.transform());
+ }
+ if (from.has_vregion()) {
+ mutable_vregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vregion());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer_Shadow::CopyFrom(const LayersPacket_Layer_Shadow& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer_Shadow::IsInitialized() const {
+
+ return true;
+}
+
+void LayersPacket_Layer_Shadow::Swap(LayersPacket_Layer_Shadow* other) {
+ if (other != this) {
+ std::swap(clip_, other->clip_);
+ std::swap(transform_, other->transform_);
+ std::swap(vregion_, other->vregion_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer_Shadow::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer.Shadow";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket_Layer::kTypeFieldNumber;
+const int LayersPacket_Layer::kPtrFieldNumber;
+const int LayersPacket_Layer::kParentPtrFieldNumber;
+const int LayersPacket_Layer::kClipFieldNumber;
+const int LayersPacket_Layer::kTransformFieldNumber;
+const int LayersPacket_Layer::kVRegionFieldNumber;
+const int LayersPacket_Layer::kShadowFieldNumber;
+const int LayersPacket_Layer::kOpacityFieldNumber;
+const int LayersPacket_Layer::kCOpaqueFieldNumber;
+const int LayersPacket_Layer::kCAlphaFieldNumber;
+const int LayersPacket_Layer::kDirectFieldNumber;
+const int LayersPacket_Layer::kBarIDFieldNumber;
+const int LayersPacket_Layer::kMaskFieldNumber;
+const int LayersPacket_Layer::kHitRegionFieldNumber;
+const int LayersPacket_Layer::kDispatchRegionFieldNumber;
+const int LayersPacket_Layer::kNoActionRegionFieldNumber;
+const int LayersPacket_Layer::kHPanRegionFieldNumber;
+const int LayersPacket_Layer::kVPanRegionFieldNumber;
+const int LayersPacket_Layer::kValidFieldNumber;
+const int LayersPacket_Layer::kColorFieldNumber;
+const int LayersPacket_Layer::kFilterFieldNumber;
+const int LayersPacket_Layer::kRefIDFieldNumber;
+const int LayersPacket_Layer::kSizeFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogLengthFieldNumber;
+const int LayersPacket_Layer::kDisplayListLogFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket_Layer::LayersPacket_Layer()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+void LayersPacket_Layer::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect::internal_default_instance());
+#else
+ clip_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Rect::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::internal_default_instance());
+#else
+ transform_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ shadow_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::internal_default_instance());
+#else
+ shadow_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ hitregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ hitregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ dispatchregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ dispatchregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ noactionregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ noactionregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ hpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ hpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ vpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ vpanregion_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ valid_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region::internal_default_instance());
+#else
+ valid_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Region*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Region::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ size_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Size*>(
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size::internal_default_instance());
+#else
+ size_ = const_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Size*>(&::mozilla::layers::layerscope::LayersPacket_Layer_Size::default_instance());
+#endif
+}
+
+LayersPacket_Layer::LayersPacket_Layer(const LayersPacket_Layer& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+void LayersPacket_Layer::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ type_ = 0;
+ ptr_ = GOOGLE_ULONGLONG(0);
+ parentptr_ = GOOGLE_ULONGLONG(0);
+ clip_ = NULL;
+ transform_ = NULL;
+ vregion_ = NULL;
+ shadow_ = NULL;
+ opacity_ = 0;
+ copaque_ = false;
+ calpha_ = false;
+ direct_ = 1;
+ barid_ = GOOGLE_ULONGLONG(0);
+ mask_ = GOOGLE_ULONGLONG(0);
+ hitregion_ = NULL;
+ dispatchregion_ = NULL;
+ noactionregion_ = NULL;
+ hpanregion_ = NULL;
+ vpanregion_ = NULL;
+ valid_ = NULL;
+ color_ = 0u;
+ filter_ = 0;
+ refid_ = GOOGLE_ULONGLONG(0);
+ size_ = NULL;
+ displaylistloglength_ = 0u;
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket_Layer::~LayersPacket_Layer() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket.Layer)
+ SharedDtor();
+}
+
+void LayersPacket_Layer::SharedDtor() {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete displaylistlog_;
+ }
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete clip_;
+ delete transform_;
+ delete vregion_;
+ delete shadow_;
+ delete hitregion_;
+ delete dispatchregion_;
+ delete noactionregion_;
+ delete hpanregion_;
+ delete vpanregion_;
+ delete valid_;
+ delete size_;
+ }
+}
+
+void LayersPacket_Layer::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket_Layer& LayersPacket_Layer::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket_Layer* LayersPacket_Layer::default_instance_ = NULL;
+
+LayersPacket_Layer* LayersPacket_Layer::New() const {
+ return new LayersPacket_Layer;
+}
+
+void LayersPacket_Layer::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<LayersPacket_Layer*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 255) {
+ ZR_(ptr_, parentptr_);
+ ZR_(type_, opacity_);
+ if (has_clip()) {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ }
+ if (has_transform()) {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ }
+ if (has_vregion()) {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_shadow()) {
+ if (shadow_ != NULL) shadow_->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::Clear();
+ }
+ }
+ if (_has_bits_[8 / 32] & 65280) {
+ ZR_(copaque_, calpha_);
+ ZR_(barid_, mask_);
+ direct_ = 1;
+ if (has_hitregion()) {
+ if (hitregion_ != NULL) hitregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_dispatchregion()) {
+ if (dispatchregion_ != NULL) dispatchregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_noactionregion()) {
+ if (noactionregion_ != NULL) noactionregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ }
+ if (_has_bits_[16 / 32] & 16711680) {
+ ZR_(color_, refid_);
+ if (has_hpanregion()) {
+ if (hpanregion_ != NULL) hpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_vpanregion()) {
+ if (vpanregion_ != NULL) vpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_valid()) {
+ if (valid_ != NULL) valid_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ }
+ if (has_size()) {
+ if (size_ != NULL) size_->::mozilla::layers::layerscope::LayersPacket_Layer_Size::Clear();
+ }
+ displaylistloglength_ = 0u;
+ }
+ if (has_displaylistlog()) {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_->clear();
+ }
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket_Layer::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket.Layer)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(16383);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_LayerType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_ptr;
+ break;
+ }
+
+ // required uint64 ptr = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_ptr:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &ptr_)));
+ set_has_ptr();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_parentPtr;
+ break;
+ }
+
+ // required uint64 parentPtr = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_parentPtr:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &parentptr_)));
+ set_has_parentptr();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(82)) goto parse_clip;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ case 10: {
+ if (tag == 82) {
+ parse_clip:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_clip()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(90)) goto parse_transform;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ case 11: {
+ if (tag == 90) {
+ parse_transform:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_transform()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(98)) goto parse_vRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ case 12: {
+ if (tag == 98) {
+ parse_vRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(106)) goto parse_shadow;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ case 13: {
+ if (tag == 106) {
+ parse_shadow:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_shadow()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(117)) goto parse_opacity;
+ break;
+ }
+
+ // optional float opacity = 14;
+ case 14: {
+ if (tag == 117) {
+ parse_opacity:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &opacity_)));
+ set_has_opacity();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(120)) goto parse_cOpaque;
+ break;
+ }
+
+ // optional bool cOpaque = 15;
+ case 15: {
+ if (tag == 120) {
+ parse_cOpaque:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &copaque_)));
+ set_has_copaque();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(128)) goto parse_cAlpha;
+ break;
+ }
+
+ // optional bool cAlpha = 16;
+ case 16: {
+ if (tag == 128) {
+ parse_cAlpha:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &calpha_)));
+ set_has_calpha();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(136)) goto parse_direct;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ case 17: {
+ if (tag == 136) {
+ parse_direct:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect_IsValid(value)) {
+ set_direct(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(144)) goto parse_barID;
+ break;
+ }
+
+ // optional uint64 barID = 18;
+ case 18: {
+ if (tag == 144) {
+ parse_barID:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &barid_)));
+ set_has_barid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(152)) goto parse_mask;
+ break;
+ }
+
+ // optional uint64 mask = 19;
+ case 19: {
+ if (tag == 152) {
+ parse_mask:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &mask_)));
+ set_has_mask();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(162)) goto parse_hitRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ case 20: {
+ if (tag == 162) {
+ parse_hitRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_hitregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(170)) goto parse_dispatchRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ case 21: {
+ if (tag == 170) {
+ parse_dispatchRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_dispatchregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(178)) goto parse_noActionRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ case 22: {
+ if (tag == 178) {
+ parse_noActionRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_noactionregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(186)) goto parse_hPanRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ case 23: {
+ if (tag == 186) {
+ parse_hPanRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_hpanregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(194)) goto parse_vPanRegion;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ case 24: {
+ if (tag == 194) {
+ parse_vPanRegion:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_vpanregion()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(802)) goto parse_valid;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ case 100: {
+ if (tag == 802) {
+ parse_valid:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_valid()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(808)) goto parse_color;
+ break;
+ }
+
+ // optional uint32 color = 101;
+ case 101: {
+ if (tag == 808) {
+ parse_color:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &color_)));
+ set_has_color();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(816)) goto parse_filter;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ case 102: {
+ if (tag == 816) {
+ parse_filter:
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::LayersPacket_Layer_Filter_IsValid(value)) {
+ set_filter(static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Filter >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(824)) goto parse_refID;
+ break;
+ }
+
+ // optional uint64 refID = 103;
+ case 103: {
+ if (tag == 824) {
+ parse_refID:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &refid_)));
+ set_has_refid();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(834)) goto parse_size;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ case 104: {
+ if (tag == 834) {
+ parse_size:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_size()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(840)) goto parse_displayListLogLength;
+ break;
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ case 105: {
+ if (tag == 840) {
+ parse_displayListLogLength:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &displaylistloglength_)));
+ set_has_displaylistloglength();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(850)) goto parse_displayListLog;
+ break;
+ }
+
+ // optional bytes displayListLog = 106;
+ case 106: {
+ if (tag == 850) {
+ parse_displayListLog:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+ input, this->mutable_displaylistlog()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket.Layer)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket.Layer)
+ return false;
+#undef DO_
+}
+
+void LayersPacket_Layer::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket.Layer)
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // required uint64 ptr = 2;
+ if (has_ptr()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(2, this->ptr(), output);
+ }
+
+ // required uint64 parentPtr = 3;
+ if (has_parentptr()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(3, this->parentptr(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ if (has_clip()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 10, this->clip(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ if (has_transform()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 11, this->transform(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ if (has_vregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 12, this->vregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ if (has_shadow()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 13, this->shadow(), output);
+ }
+
+ // optional float opacity = 14;
+ if (has_opacity()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(14, this->opacity(), output);
+ }
+
+ // optional bool cOpaque = 15;
+ if (has_copaque()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(15, this->copaque(), output);
+ }
+
+ // optional bool cAlpha = 16;
+ if (has_calpha()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(16, this->calpha(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ if (has_direct()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 17, this->direct(), output);
+ }
+
+ // optional uint64 barID = 18;
+ if (has_barid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(18, this->barid(), output);
+ }
+
+ // optional uint64 mask = 19;
+ if (has_mask()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(19, this->mask(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ if (has_hitregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 20, this->hitregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ if (has_dispatchregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 21, this->dispatchregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ if (has_noactionregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 22, this->noactionregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ if (has_hpanregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 23, this->hpanregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ if (has_vpanregion()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 24, this->vpanregion(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ if (has_valid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 100, this->valid(), output);
+ }
+
+ // optional uint32 color = 101;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(101, this->color(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ if (has_filter()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 102, this->filter(), output);
+ }
+
+ // optional uint64 refID = 103;
+ if (has_refid()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(103, this->refid(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ if (has_size()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 104, this->size(), output);
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ if (has_displaylistloglength()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(105, this->displaylistloglength(), output);
+ }
+
+ // optional bytes displayListLog = 106;
+ if (has_displaylistlog()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
+ 106, this->displaylistlog(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket.Layer)
+}
+
+int LayersPacket_Layer::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // required uint64 ptr = 2;
+ if (has_ptr()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->ptr());
+ }
+
+ // required uint64 parentPtr = 3;
+ if (has_parentptr()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->parentptr());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ if (has_clip()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->clip());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ if (has_transform()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->transform());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ if (has_vregion()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ if (has_shadow()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->shadow());
+ }
+
+ // optional float opacity = 14;
+ if (has_opacity()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ // optional bool cOpaque = 15;
+ if (has_copaque()) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool cAlpha = 16;
+ if (has_calpha()) {
+ total_size += 2 + 1;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ if (has_direct()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->direct());
+ }
+
+ // optional uint64 barID = 18;
+ if (has_barid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->barid());
+ }
+
+ // optional uint64 mask = 19;
+ if (has_mask()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->mask());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ if (has_hitregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->hitregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ if (has_dispatchregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->dispatchregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ if (has_noactionregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->noactionregion());
+ }
+
+ }
+ if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ if (has_hpanregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->hpanregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ if (has_vpanregion()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->vpanregion());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ if (has_valid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->valid());
+ }
+
+ // optional uint32 color = 101;
+ if (has_color()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->color());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ if (has_filter()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->filter());
+ }
+
+ // optional uint64 refID = 103;
+ if (has_refid()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->refid());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ if (has_size()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->size());
+ }
+
+ // optional uint32 displayListLogLength = 105;
+ if (has_displaylistloglength()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->displaylistloglength());
+ }
+
+ }
+ if (_has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+ // optional bytes displayListLog = 106;
+ if (has_displaylistlog()) {
+ total_size += 2 +
+ ::google::protobuf::internal::WireFormatLite::BytesSize(
+ this->displaylistlog());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket_Layer::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket_Layer*>(&from));
+}
+
+void LayersPacket_Layer::MergeFrom(const LayersPacket_Layer& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_ptr()) {
+ set_ptr(from.ptr());
+ }
+ if (from.has_parentptr()) {
+ set_parentptr(from.parentptr());
+ }
+ if (from.has_clip()) {
+ mutable_clip()->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::MergeFrom(from.clip());
+ }
+ if (from.has_transform()) {
+ mutable_transform()->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::MergeFrom(from.transform());
+ }
+ if (from.has_vregion()) {
+ mutable_vregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vregion());
+ }
+ if (from.has_shadow()) {
+ mutable_shadow()->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::MergeFrom(from.shadow());
+ }
+ if (from.has_opacity()) {
+ set_opacity(from.opacity());
+ }
+ }
+ if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+ if (from.has_copaque()) {
+ set_copaque(from.copaque());
+ }
+ if (from.has_calpha()) {
+ set_calpha(from.calpha());
+ }
+ if (from.has_direct()) {
+ set_direct(from.direct());
+ }
+ if (from.has_barid()) {
+ set_barid(from.barid());
+ }
+ if (from.has_mask()) {
+ set_mask(from.mask());
+ }
+ if (from.has_hitregion()) {
+ mutable_hitregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.hitregion());
+ }
+ if (from.has_dispatchregion()) {
+ mutable_dispatchregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.dispatchregion());
+ }
+ if (from.has_noactionregion()) {
+ mutable_noactionregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.noactionregion());
+ }
+ }
+ if (from._has_bits_[16 / 32] & (0xffu << (16 % 32))) {
+ if (from.has_hpanregion()) {
+ mutable_hpanregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.hpanregion());
+ }
+ if (from.has_vpanregion()) {
+ mutable_vpanregion()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.vpanregion());
+ }
+ if (from.has_valid()) {
+ mutable_valid()->::mozilla::layers::layerscope::LayersPacket_Layer_Region::MergeFrom(from.valid());
+ }
+ if (from.has_color()) {
+ set_color(from.color());
+ }
+ if (from.has_filter()) {
+ set_filter(from.filter());
+ }
+ if (from.has_refid()) {
+ set_refid(from.refid());
+ }
+ if (from.has_size()) {
+ mutable_size()->::mozilla::layers::layerscope::LayersPacket_Layer_Size::MergeFrom(from.size());
+ }
+ if (from.has_displaylistloglength()) {
+ set_displaylistloglength(from.displaylistloglength());
+ }
+ }
+ if (from._has_bits_[24 / 32] & (0xffu << (24 % 32))) {
+ if (from.has_displaylistlog()) {
+ set_displaylistlog(from.displaylistlog());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket_Layer::CopyFrom(const LayersPacket_Layer& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket_Layer::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000007) != 0x00000007) return false;
+
+ return true;
+}
+
+void LayersPacket_Layer::Swap(LayersPacket_Layer* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(ptr_, other->ptr_);
+ std::swap(parentptr_, other->parentptr_);
+ std::swap(clip_, other->clip_);
+ std::swap(transform_, other->transform_);
+ std::swap(vregion_, other->vregion_);
+ std::swap(shadow_, other->shadow_);
+ std::swap(opacity_, other->opacity_);
+ std::swap(copaque_, other->copaque_);
+ std::swap(calpha_, other->calpha_);
+ std::swap(direct_, other->direct_);
+ std::swap(barid_, other->barid_);
+ std::swap(mask_, other->mask_);
+ std::swap(hitregion_, other->hitregion_);
+ std::swap(dispatchregion_, other->dispatchregion_);
+ std::swap(noactionregion_, other->noactionregion_);
+ std::swap(hpanregion_, other->hpanregion_);
+ std::swap(vpanregion_, other->vpanregion_);
+ std::swap(valid_, other->valid_);
+ std::swap(color_, other->color_);
+ std::swap(filter_, other->filter_);
+ std::swap(refid_, other->refid_);
+ std::swap(size_, other->size_);
+ std::swap(displaylistloglength_, other->displaylistloglength_);
+ std::swap(displaylistlog_, other->displaylistlog_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket_Layer::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket.Layer";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int LayersPacket::kLayerFieldNumber;
+#endif // !_MSC_VER
+
+LayersPacket::LayersPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.LayersPacket)
+}
+
+void LayersPacket::InitAsDefaultInstance() {
+}
+
+LayersPacket::LayersPacket(const LayersPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.LayersPacket)
+}
+
+void LayersPacket::SharedCtor() {
+ _cached_size_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+LayersPacket::~LayersPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.LayersPacket)
+ SharedDtor();
+}
+
+void LayersPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LayersPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LayersPacket& LayersPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LayersPacket* LayersPacket::default_instance_ = NULL;
+
+LayersPacket* LayersPacket::New() const {
+ return new LayersPacket;
+}
+
+void LayersPacket::Clear() {
+ layer_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool LayersPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.LayersPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ case 1: {
+ if (tag == 10) {
+ parse_layer:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_layer()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_layer;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.LayersPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.LayersPacket)
+ return false;
+#undef DO_
+}
+
+void LayersPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.LayersPacket)
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ for (int i = 0; i < this->layer_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->layer(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.LayersPacket)
+}
+
+int LayersPacket::ByteSize() const {
+ int total_size = 0;
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ total_size += 1 * this->layer_size();
+ for (int i = 0; i < this->layer_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layer(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LayersPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LayersPacket*>(&from));
+}
+
+void LayersPacket::MergeFrom(const LayersPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ layer_.MergeFrom(from.layer_);
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void LayersPacket::CopyFrom(const LayersPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LayersPacket::IsInitialized() const {
+
+ if (!::google::protobuf::internal::AllAreInitialized(this->layer())) return false;
+ return true;
+}
+
+void LayersPacket::Swap(LayersPacket* other) {
+ if (other != this) {
+ layer_.Swap(&other->layer_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string LayersPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.LayersPacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int MetaPacket::kComposedByHwcFieldNumber;
+#endif // !_MSC_VER
+
+MetaPacket::MetaPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.MetaPacket)
+}
+
+void MetaPacket::InitAsDefaultInstance() {
+}
+
+MetaPacket::MetaPacket(const MetaPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.MetaPacket)
+}
+
+void MetaPacket::SharedCtor() {
+ _cached_size_ = 0;
+ composedbyhwc_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+MetaPacket::~MetaPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.MetaPacket)
+ SharedDtor();
+}
+
+void MetaPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void MetaPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const MetaPacket& MetaPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+MetaPacket* MetaPacket::default_instance_ = NULL;
+
+MetaPacket* MetaPacket::New() const {
+ return new MetaPacket;
+}
+
+void MetaPacket::Clear() {
+ composedbyhwc_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool MetaPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.MetaPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional bool composedByHwc = 1;
+ case 1: {
+ if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &composedbyhwc_)));
+ set_has_composedbyhwc();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.MetaPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.MetaPacket)
+ return false;
+#undef DO_
+}
+
+void MetaPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.MetaPacket)
+ // optional bool composedByHwc = 1;
+ if (has_composedbyhwc()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(1, this->composedbyhwc(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.MetaPacket)
+}
+
+int MetaPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // optional bool composedByHwc = 1;
+ if (has_composedbyhwc()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void MetaPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const MetaPacket*>(&from));
+}
+
+void MetaPacket::MergeFrom(const MetaPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_composedbyhwc()) {
+ set_composedbyhwc(from.composedbyhwc());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void MetaPacket::CopyFrom(const MetaPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool MetaPacket::IsInitialized() const {
+
+ return true;
+}
+
+void MetaPacket::Swap(MetaPacket* other) {
+ if (other != this) {
+ std::swap(composedbyhwc_, other->composedbyhwc_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string MetaPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.MetaPacket";
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int DrawPacket_Rect::kXFieldNumber;
+const int DrawPacket_Rect::kYFieldNumber;
+const int DrawPacket_Rect::kWFieldNumber;
+const int DrawPacket_Rect::kHFieldNumber;
+#endif // !_MSC_VER
+
+DrawPacket_Rect::DrawPacket_Rect()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+void DrawPacket_Rect::InitAsDefaultInstance() {
+}
+
+DrawPacket_Rect::DrawPacket_Rect(const DrawPacket_Rect& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+void DrawPacket_Rect::SharedCtor() {
+ _cached_size_ = 0;
+ x_ = 0;
+ y_ = 0;
+ w_ = 0;
+ h_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+DrawPacket_Rect::~DrawPacket_Rect() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.DrawPacket.Rect)
+ SharedDtor();
+}
+
+void DrawPacket_Rect::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void DrawPacket_Rect::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const DrawPacket_Rect& DrawPacket_Rect::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+DrawPacket_Rect* DrawPacket_Rect::default_instance_ = NULL;
+
+DrawPacket_Rect* DrawPacket_Rect::New() const {
+ return new DrawPacket_Rect;
+}
+
+void DrawPacket_Rect::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<DrawPacket_Rect*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(x_, h_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool DrawPacket_Rect::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.DrawPacket.Rect)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required float x = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &x_)));
+ set_has_x();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_y;
+ break;
+ }
+
+ // required float y = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_y:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &y_)));
+ set_has_y();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_w;
+ break;
+ }
+
+ // required float w = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_w:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &w_)));
+ set_has_w();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(37)) goto parse_h;
+ break;
+ }
+
+ // required float h = 4;
+ case 4: {
+ if (tag == 37) {
+ parse_h:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &h_)));
+ set_has_h();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.DrawPacket.Rect)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.DrawPacket.Rect)
+ return false;
+#undef DO_
+}
+
+void DrawPacket_Rect::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.DrawPacket.Rect)
+ // required float x = 1;
+ if (has_x()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->x(), output);
+ }
+
+ // required float y = 2;
+ if (has_y()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->y(), output);
+ }
+
+ // required float w = 3;
+ if (has_w()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->w(), output);
+ }
+
+ // required float h = 4;
+ if (has_h()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->h(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.DrawPacket.Rect)
+}
+
+int DrawPacket_Rect::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required float x = 1;
+ if (has_x()) {
+ total_size += 1 + 4;
+ }
+
+ // required float y = 2;
+ if (has_y()) {
+ total_size += 1 + 4;
+ }
+
+ // required float w = 3;
+ if (has_w()) {
+ total_size += 1 + 4;
+ }
+
+ // required float h = 4;
+ if (has_h()) {
+ total_size += 1 + 4;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void DrawPacket_Rect::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const DrawPacket_Rect*>(&from));
+}
+
+void DrawPacket_Rect::MergeFrom(const DrawPacket_Rect& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_x()) {
+ set_x(from.x());
+ }
+ if (from.has_y()) {
+ set_y(from.y());
+ }
+ if (from.has_w()) {
+ set_w(from.w());
+ }
+ if (from.has_h()) {
+ set_h(from.h());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void DrawPacket_Rect::CopyFrom(const DrawPacket_Rect& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool DrawPacket_Rect::IsInitialized() const {
+ if ((_has_bits_[0] & 0x0000000f) != 0x0000000f) return false;
+
+ return true;
+}
+
+void DrawPacket_Rect::Swap(DrawPacket_Rect* other) {
+ if (other != this) {
+ std::swap(x_, other->x_);
+ std::swap(y_, other->y_);
+ std::swap(w_, other->w_);
+ std::swap(h_, other->h_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string DrawPacket_Rect::GetTypeName() const {
+ return "mozilla.layers.layerscope.DrawPacket.Rect";
+}
+
+
+// -------------------------------------------------------------------
+
+#ifndef _MSC_VER
+const int DrawPacket::kOffsetXFieldNumber;
+const int DrawPacket::kOffsetYFieldNumber;
+const int DrawPacket::kMvMatrixFieldNumber;
+const int DrawPacket::kTotalRectsFieldNumber;
+const int DrawPacket::kLayerRectFieldNumber;
+const int DrawPacket::kLayerrefFieldNumber;
+const int DrawPacket::kTexIDsFieldNumber;
+const int DrawPacket::kTextureRectFieldNumber;
+#endif // !_MSC_VER
+
+DrawPacket::DrawPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.DrawPacket)
+}
+
+void DrawPacket::InitAsDefaultInstance() {
+}
+
+DrawPacket::DrawPacket(const DrawPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.DrawPacket)
+}
+
+void DrawPacket::SharedCtor() {
+ _cached_size_ = 0;
+ offsetx_ = 0;
+ offsety_ = 0;
+ totalrects_ = 0u;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+DrawPacket::~DrawPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.DrawPacket)
+ SharedDtor();
+}
+
+void DrawPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void DrawPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const DrawPacket& DrawPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+DrawPacket* DrawPacket::default_instance_ = NULL;
+
+DrawPacket* DrawPacket::New() const {
+ return new DrawPacket;
+}
+
+void DrawPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<DrawPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ if (_has_bits_[0 / 32] & 43) {
+ ZR_(offsetx_, offsety_);
+ totalrects_ = 0u;
+ layerref_ = GOOGLE_ULONGLONG(0);
+ }
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ mvmatrix_.Clear();
+ layerrect_.Clear();
+ texids_.Clear();
+ texturerect_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool DrawPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.DrawPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required float offsetX = 1;
+ case 1: {
+ if (tag == 13) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &offsetx_)));
+ set_has_offsetx();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(21)) goto parse_offsetY;
+ break;
+ }
+
+ // required float offsetY = 2;
+ case 2: {
+ if (tag == 21) {
+ parse_offsetY:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, &offsety_)));
+ set_has_offsety();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_mvMatrix;
+ break;
+ }
+
+ // repeated float mvMatrix = 3;
+ case 3: {
+ if (tag == 29) {
+ parse_mvMatrix:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ 1, 29, input, this->mutable_mvmatrix())));
+ } else if (tag == 26) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+ input, this->mutable_mvmatrix())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(29)) goto parse_mvMatrix;
+ if (input->ExpectTag(32)) goto parse_totalRects;
+ break;
+ }
+
+ // required uint32 totalRects = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_totalRects:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, &totalrects_)));
+ set_has_totalrects();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layerRect;
+ break;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ case 5: {
+ if (tag == 42) {
+ parse_layerRect:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_layerrect()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layerRect;
+ if (input->ExpectTag(48)) goto parse_layerref;
+ break;
+ }
+
+ // required uint64 layerref = 6;
+ case 6: {
+ if (tag == 48) {
+ parse_layerref:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+ input, &layerref_)));
+ set_has_layerref();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_texIDs;
+ break;
+ }
+
+ // repeated uint32 texIDs = 7;
+ case 7: {
+ if (tag == 56) {
+ parse_texIDs:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ 1, 56, input, this->mutable_texids())));
+ } else if (tag == 58) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+ input, this->mutable_texids())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_texIDs;
+ if (input->ExpectTag(66)) goto parse_textureRect;
+ break;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ case 8: {
+ if (tag == 66) {
+ parse_textureRect:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, add_texturerect()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(66)) goto parse_textureRect;
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.DrawPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.DrawPacket)
+ return false;
+#undef DO_
+}
+
+void DrawPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.DrawPacket)
+ // required float offsetX = 1;
+ if (has_offsetx()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(1, this->offsetx(), output);
+ }
+
+ // required float offsetY = 2;
+ if (has_offsety()) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->offsety(), output);
+ }
+
+ // repeated float mvMatrix = 3;
+ for (int i = 0; i < this->mvmatrix_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteFloat(
+ 3, this->mvmatrix(i), output);
+ }
+
+ // required uint32 totalRects = 4;
+ if (has_totalrects()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->totalrects(), output);
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ for (int i = 0; i < this->layerrect_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 5, this->layerrect(i), output);
+ }
+
+ // required uint64 layerref = 6;
+ if (has_layerref()) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt64(6, this->layerref(), output);
+ }
+
+ // repeated uint32 texIDs = 7;
+ for (int i = 0; i < this->texids_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteUInt32(
+ 7, this->texids(i), output);
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ for (int i = 0; i < this->texturerect_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 8, this->texturerect(i), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.DrawPacket)
+}
+
+int DrawPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required float offsetX = 1;
+ if (has_offsetx()) {
+ total_size += 1 + 4;
+ }
+
+ // required float offsetY = 2;
+ if (has_offsety()) {
+ total_size += 1 + 4;
+ }
+
+ // required uint32 totalRects = 4;
+ if (has_totalrects()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt32Size(
+ this->totalrects());
+ }
+
+ // required uint64 layerref = 6;
+ if (has_layerref()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::UInt64Size(
+ this->layerref());
+ }
+
+ }
+ // repeated float mvMatrix = 3;
+ {
+ int data_size = 0;
+ data_size = 4 * this->mvmatrix_size();
+ total_size += 1 * this->mvmatrix_size() + data_size;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ total_size += 1 * this->layerrect_size();
+ for (int i = 0; i < this->layerrect_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layerrect(i));
+ }
+
+ // repeated uint32 texIDs = 7;
+ {
+ int data_size = 0;
+ for (int i = 0; i < this->texids_size(); i++) {
+ data_size += ::google::protobuf::internal::WireFormatLite::
+ UInt32Size(this->texids(i));
+ }
+ total_size += 1 * this->texids_size() + data_size;
+ }
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ total_size += 1 * this->texturerect_size();
+ for (int i = 0; i < this->texturerect_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->texturerect(i));
+ }
+
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void DrawPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const DrawPacket*>(&from));
+}
+
+void DrawPacket::MergeFrom(const DrawPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ mvmatrix_.MergeFrom(from.mvmatrix_);
+ layerrect_.MergeFrom(from.layerrect_);
+ texids_.MergeFrom(from.texids_);
+ texturerect_.MergeFrom(from.texturerect_);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_offsetx()) {
+ set_offsetx(from.offsetx());
+ }
+ if (from.has_offsety()) {
+ set_offsety(from.offsety());
+ }
+ if (from.has_totalrects()) {
+ set_totalrects(from.totalrects());
+ }
+ if (from.has_layerref()) {
+ set_layerref(from.layerref());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void DrawPacket::CopyFrom(const DrawPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool DrawPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x0000002b) != 0x0000002b) return false;
+
+ if (!::google::protobuf::internal::AllAreInitialized(this->layerrect())) return false;
+ if (!::google::protobuf::internal::AllAreInitialized(this->texturerect())) return false;
+ return true;
+}
+
+void DrawPacket::Swap(DrawPacket* other) {
+ if (other != this) {
+ std::swap(offsetx_, other->offsetx_);
+ std::swap(offsety_, other->offsety_);
+ mvmatrix_.Swap(&other->mvmatrix_);
+ std::swap(totalrects_, other->totalrects_);
+ layerrect_.Swap(&other->layerrect_);
+ std::swap(layerref_, other->layerref_);
+ texids_.Swap(&other->texids_);
+ texturerect_.Swap(&other->texturerect_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string DrawPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.DrawPacket";
+}
+
+
+// ===================================================================
+
+bool Packet_DataType_IsValid(int value) {
+ switch(value) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const Packet_DataType Packet::FRAMESTART;
+const Packet_DataType Packet::FRAMEEND;
+const Packet_DataType Packet::COLOR;
+const Packet_DataType Packet::TEXTURE;
+const Packet_DataType Packet::LAYERS;
+const Packet_DataType Packet::META;
+const Packet_DataType Packet::DRAW;
+const Packet_DataType Packet::DataType_MIN;
+const Packet_DataType Packet::DataType_MAX;
+const int Packet::DataType_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int Packet::kTypeFieldNumber;
+const int Packet::kFrameFieldNumber;
+const int Packet::kColorFieldNumber;
+const int Packet::kTextureFieldNumber;
+const int Packet::kLayersFieldNumber;
+const int Packet::kMetaFieldNumber;
+const int Packet::kDrawFieldNumber;
+#endif // !_MSC_VER
+
+Packet::Packet()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.Packet)
+}
+
+void Packet::InitAsDefaultInstance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ frame_ = const_cast< ::mozilla::layers::layerscope::FramePacket*>(
+ ::mozilla::layers::layerscope::FramePacket::internal_default_instance());
+#else
+ frame_ = const_cast< ::mozilla::layers::layerscope::FramePacket*>(&::mozilla::layers::layerscope::FramePacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ color_ = const_cast< ::mozilla::layers::layerscope::ColorPacket*>(
+ ::mozilla::layers::layerscope::ColorPacket::internal_default_instance());
+#else
+ color_ = const_cast< ::mozilla::layers::layerscope::ColorPacket*>(&::mozilla::layers::layerscope::ColorPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ texture_ = const_cast< ::mozilla::layers::layerscope::TexturePacket*>(
+ ::mozilla::layers::layerscope::TexturePacket::internal_default_instance());
+#else
+ texture_ = const_cast< ::mozilla::layers::layerscope::TexturePacket*>(&::mozilla::layers::layerscope::TexturePacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ layers_ = const_cast< ::mozilla::layers::layerscope::LayersPacket*>(
+ ::mozilla::layers::layerscope::LayersPacket::internal_default_instance());
+#else
+ layers_ = const_cast< ::mozilla::layers::layerscope::LayersPacket*>(&::mozilla::layers::layerscope::LayersPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ meta_ = const_cast< ::mozilla::layers::layerscope::MetaPacket*>(
+ ::mozilla::layers::layerscope::MetaPacket::internal_default_instance());
+#else
+ meta_ = const_cast< ::mozilla::layers::layerscope::MetaPacket*>(&::mozilla::layers::layerscope::MetaPacket::default_instance());
+#endif
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ draw_ = const_cast< ::mozilla::layers::layerscope::DrawPacket*>(
+ ::mozilla::layers::layerscope::DrawPacket::internal_default_instance());
+#else
+ draw_ = const_cast< ::mozilla::layers::layerscope::DrawPacket*>(&::mozilla::layers::layerscope::DrawPacket::default_instance());
+#endif
+}
+
+Packet::Packet(const Packet& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.Packet)
+}
+
+void Packet::SharedCtor() {
+ _cached_size_ = 0;
+ type_ = 1;
+ frame_ = NULL;
+ color_ = NULL;
+ texture_ = NULL;
+ layers_ = NULL;
+ meta_ = NULL;
+ draw_ = NULL;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+Packet::~Packet() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.Packet)
+ SharedDtor();
+}
+
+void Packet::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ delete frame_;
+ delete color_;
+ delete texture_;
+ delete layers_;
+ delete meta_;
+ delete draw_;
+ }
+}
+
+void Packet::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const Packet& Packet::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+Packet* Packet::default_instance_ = NULL;
+
+Packet* Packet::New() const {
+ return new Packet;
+}
+
+void Packet::Clear() {
+ if (_has_bits_[0 / 32] & 127) {
+ type_ = 1;
+ if (has_frame()) {
+ if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear();
+ }
+ if (has_color()) {
+ if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear();
+ }
+ if (has_texture()) {
+ if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear();
+ }
+ if (has_layers()) {
+ if (layers_ != NULL) layers_->::mozilla::layers::layerscope::LayersPacket::Clear();
+ }
+ if (has_meta()) {
+ if (meta_ != NULL) meta_->::mozilla::layers::layerscope::MetaPacket::Clear();
+ }
+ if (has_draw()) {
+ if (draw_ != NULL) draw_->::mozilla::layers::layerscope::DrawPacket::Clear();
+ }
+ }
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool Packet::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.Packet)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::Packet_DataType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::Packet_DataType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_frame;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_frame:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_frame()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_color;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_color:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_color()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(34)) goto parse_texture;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ case 4: {
+ if (tag == 34) {
+ parse_texture:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_texture()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(42)) goto parse_layers;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ case 5: {
+ if (tag == 42) {
+ parse_layers:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_layers()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(50)) goto parse_meta;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ case 6: {
+ if (tag == 50) {
+ parse_meta:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_meta()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(58)) goto parse_draw;
+ break;
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ case 7: {
+ if (tag == 58) {
+ parse_draw:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+ input, mutable_draw()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.Packet)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.Packet)
+ return false;
+#undef DO_
+}
+
+void Packet::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.Packet)
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ if (has_frame()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->frame(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ if (has_color()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 3, this->color(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ if (has_texture()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 4, this->texture(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ if (has_layers()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 5, this->layers(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ if (has_meta()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 6, this->meta(), output);
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ if (has_draw()) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 7, this->draw(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.Packet)
+}
+
+int Packet::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ if (has_frame()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->frame());
+ }
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ if (has_color()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->color());
+ }
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ if (has_texture()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->texture());
+ }
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ if (has_layers()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->layers());
+ }
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ if (has_meta()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->meta());
+ }
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ if (has_draw()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->draw());
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void Packet::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const Packet*>(&from));
+}
+
+void Packet::MergeFrom(const Packet& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_frame()) {
+ mutable_frame()->::mozilla::layers::layerscope::FramePacket::MergeFrom(from.frame());
+ }
+ if (from.has_color()) {
+ mutable_color()->::mozilla::layers::layerscope::ColorPacket::MergeFrom(from.color());
+ }
+ if (from.has_texture()) {
+ mutable_texture()->::mozilla::layers::layerscope::TexturePacket::MergeFrom(from.texture());
+ }
+ if (from.has_layers()) {
+ mutable_layers()->::mozilla::layers::layerscope::LayersPacket::MergeFrom(from.layers());
+ }
+ if (from.has_meta()) {
+ mutable_meta()->::mozilla::layers::layerscope::MetaPacket::MergeFrom(from.meta());
+ }
+ if (from.has_draw()) {
+ mutable_draw()->::mozilla::layers::layerscope::DrawPacket::MergeFrom(from.draw());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void Packet::CopyFrom(const Packet& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Packet::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ if (has_color()) {
+ if (!this->color().IsInitialized()) return false;
+ }
+ if (has_texture()) {
+ if (!this->texture().IsInitialized()) return false;
+ }
+ if (has_layers()) {
+ if (!this->layers().IsInitialized()) return false;
+ }
+ if (has_draw()) {
+ if (!this->draw().IsInitialized()) return false;
+ }
+ return true;
+}
+
+void Packet::Swap(Packet* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(frame_, other->frame_);
+ std::swap(color_, other->color_);
+ std::swap(texture_, other->texture_);
+ std::swap(layers_, other->layers_);
+ std::swap(meta_, other->meta_);
+ std::swap(draw_, other->draw_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string Packet::GetTypeName() const {
+ return "mozilla.layers.layerscope.Packet";
+}
+
+
+// ===================================================================
+
+bool CommandPacket_CmdType_IsValid(int value) {
+ switch(value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#ifndef _MSC_VER
+const CommandPacket_CmdType CommandPacket::NO_OP;
+const CommandPacket_CmdType CommandPacket::LAYERS_TREE;
+const CommandPacket_CmdType CommandPacket::LAYERS_BUFFER;
+const CommandPacket_CmdType CommandPacket::CmdType_MIN;
+const CommandPacket_CmdType CommandPacket::CmdType_MAX;
+const int CommandPacket::CmdType_ARRAYSIZE;
+#endif // _MSC_VER
+#ifndef _MSC_VER
+const int CommandPacket::kTypeFieldNumber;
+const int CommandPacket::kValueFieldNumber;
+#endif // !_MSC_VER
+
+CommandPacket::CommandPacket()
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:mozilla.layers.layerscope.CommandPacket)
+}
+
+void CommandPacket::InitAsDefaultInstance() {
+}
+
+CommandPacket::CommandPacket(const CommandPacket& from)
+ : ::google::protobuf::MessageLite() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:mozilla.layers.layerscope.CommandPacket)
+}
+
+void CommandPacket::SharedCtor() {
+ _cached_size_ = 0;
+ type_ = 0;
+ value_ = false;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+CommandPacket::~CommandPacket() {
+ // @@protoc_insertion_point(destructor:mozilla.layers.layerscope.CommandPacket)
+ SharedDtor();
+}
+
+void CommandPacket::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void CommandPacket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const CommandPacket& CommandPacket::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_LayerScopePacket_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto();
+#endif
+ return *default_instance_;
+}
+
+CommandPacket* CommandPacket::default_instance_ = NULL;
+
+CommandPacket* CommandPacket::New() const {
+ return new CommandPacket;
+}
+
+void CommandPacket::Clear() {
+#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \
+ &reinterpret_cast<CommandPacket*>(16)->f) - \
+ reinterpret_cast<char*>(16))
+
+#define ZR_(first, last) do { \
+ size_t f = OFFSET_OF_FIELD_(first); \
+ size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \
+ ::memset(&first, 0, n); \
+ } while (0)
+
+ ZR_(type_, value_);
+
+#undef OFFSET_OF_FIELD_
+#undef ZR_
+
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ mutable_unknown_fields()->clear();
+}
+
+bool CommandPacket::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ ::google::protobuf::io::StringOutputStream unknown_fields_string(
+ mutable_unknown_fields());
+ ::google::protobuf::io::CodedOutputStream unknown_fields_stream(
+ &unknown_fields_string);
+ // @@protoc_insertion_point(parse_start:mozilla.layers.layerscope.CommandPacket)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ case 1: {
+ if (tag == 8) {
+ int value;
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+ input, &value)));
+ if (::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value)) {
+ set_type(static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(value));
+ } else {
+ unknown_fields_stream.WriteVarint32(tag);
+ unknown_fields_stream.WriteVarint32(value);
+ }
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(16)) goto parse_value;
+ break;
+ }
+
+ // optional bool value = 2;
+ case 2: {
+ if (tag == 16) {
+ parse_value:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &value_)));
+ set_has_value();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(
+ input, tag, &unknown_fields_stream));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:mozilla.layers.layerscope.CommandPacket)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:mozilla.layers.layerscope.CommandPacket)
+ return false;
+#undef DO_
+}
+
+void CommandPacket::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:mozilla.layers.layerscope.CommandPacket)
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ if (has_type()) {
+ ::google::protobuf::internal::WireFormatLite::WriteEnum(
+ 1, this->type(), output);
+ }
+
+ // optional bool value = 2;
+ if (has_value()) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(2, this->value(), output);
+ }
+
+ output->WriteRaw(unknown_fields().data(),
+ unknown_fields().size());
+ // @@protoc_insertion_point(serialize_end:mozilla.layers.layerscope.CommandPacket)
+}
+
+int CommandPacket::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ if (has_type()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::EnumSize(this->type());
+ }
+
+ // optional bool value = 2;
+ if (has_value()) {
+ total_size += 1 + 1;
+ }
+
+ }
+ total_size += unknown_fields().size();
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void CommandPacket::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const CommandPacket*>(&from));
+}
+
+void CommandPacket::MergeFrom(const CommandPacket& from) {
+ GOOGLE_CHECK_NE(&from, this);
+ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+ if (from.has_type()) {
+ set_type(from.type());
+ }
+ if (from.has_value()) {
+ set_value(from.value());
+ }
+ }
+ mutable_unknown_fields()->append(from.unknown_fields());
+}
+
+void CommandPacket::CopyFrom(const CommandPacket& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool CommandPacket::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false;
+
+ return true;
+}
+
+void CommandPacket::Swap(CommandPacket* other) {
+ if (other != this) {
+ std::swap(type_, other->type_);
+ std::swap(value_, other->value_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _unknown_fields_.swap(other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+ }
+}
+
+::std::string CommandPacket::GetTypeName() const {
+ return "mozilla.layers.layerscope.CommandPacket";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace layerscope
+} // namespace layers
+} // namespace mozilla
+
+// @@protoc_insertion_point(global_scope)
diff --git a/gfx/layers/protobuf/LayerScopePacket.pb.h b/gfx/layers/protobuf/LayerScopePacket.pb.h
new file mode 100644
index 000000000..1a850c03d
--- /dev/null
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.h
@@ -0,0 +1,5779 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: LayerScopePacket.proto
+
+#ifndef PROTOBUF_LayerScopePacket_2eproto__INCLUDED
+#define PROTOBUF_LayerScopePacket_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 2006000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+// @@protoc_insertion_point(includes)
+
+namespace mozilla {
+namespace layers {
+namespace layerscope {
+
+// Internal implementation detail -- do not call these.
+void protobuf_AddDesc_LayerScopePacket_2eproto();
+void protobuf_AssignDesc_LayerScopePacket_2eproto();
+void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+class FramePacket;
+class ColorPacket;
+class TexturePacket;
+class TexturePacket_Rect;
+class TexturePacket_Size;
+class TexturePacket_Matrix;
+class TexturePacket_EffectMask;
+class LayersPacket;
+class LayersPacket_Layer;
+class LayersPacket_Layer_Size;
+class LayersPacket_Layer_Rect;
+class LayersPacket_Layer_Region;
+class LayersPacket_Layer_Matrix;
+class LayersPacket_Layer_Shadow;
+class MetaPacket;
+class DrawPacket;
+class DrawPacket_Rect;
+class Packet;
+class CommandPacket;
+
+enum TexturePacket_Filter {
+ TexturePacket_Filter_GOOD = 0,
+ TexturePacket_Filter_LINEAR = 1,
+ TexturePacket_Filter_POINT = 2
+};
+bool TexturePacket_Filter_IsValid(int value);
+const TexturePacket_Filter TexturePacket_Filter_Filter_MIN = TexturePacket_Filter_GOOD;
+const TexturePacket_Filter TexturePacket_Filter_Filter_MAX = TexturePacket_Filter_POINT;
+const int TexturePacket_Filter_Filter_ARRAYSIZE = TexturePacket_Filter_Filter_MAX + 1;
+
+enum LayersPacket_Layer_LayerType {
+ LayersPacket_Layer_LayerType_UnknownLayer = 0,
+ LayersPacket_Layer_LayerType_LayerManager = 1,
+ LayersPacket_Layer_LayerType_ContainerLayer = 2,
+ LayersPacket_Layer_LayerType_PaintedLayer = 3,
+ LayersPacket_Layer_LayerType_CanvasLayer = 4,
+ LayersPacket_Layer_LayerType_ImageLayer = 5,
+ LayersPacket_Layer_LayerType_ColorLayer = 6,
+ LayersPacket_Layer_LayerType_RefLayer = 7,
+ LayersPacket_Layer_LayerType_ReadbackLayer = 8
+};
+bool LayersPacket_Layer_LayerType_IsValid(int value);
+const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MIN = LayersPacket_Layer_LayerType_UnknownLayer;
+const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MAX = LayersPacket_Layer_LayerType_ReadbackLayer;
+const int LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE = LayersPacket_Layer_LayerType_LayerType_MAX + 1;
+
+enum LayersPacket_Layer_ScrollingDirect {
+ LayersPacket_Layer_ScrollingDirect_VERTICAL = 1,
+ LayersPacket_Layer_ScrollingDirect_HORIZONTAL = 2
+};
+bool LayersPacket_Layer_ScrollingDirect_IsValid(int value);
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MIN = LayersPacket_Layer_ScrollingDirect_VERTICAL;
+const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX = LayersPacket_Layer_ScrollingDirect_HORIZONTAL;
+const int LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE = LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX + 1;
+
+enum LayersPacket_Layer_Filter {
+ LayersPacket_Layer_Filter_FILTER_FAST = 0,
+ LayersPacket_Layer_Filter_FILTER_GOOD = 1,
+ LayersPacket_Layer_Filter_FILTER_BEST = 2,
+ LayersPacket_Layer_Filter_FILTER_NEAREST = 3,
+ LayersPacket_Layer_Filter_FILTER_BILINEAR = 4,
+ LayersPacket_Layer_Filter_FILTER_GAUSSIAN = 5,
+ LayersPacket_Layer_Filter_FILTER_SENTINEL = 6,
+ LayersPacket_Layer_Filter_FILTER_LINEAR = 7,
+ LayersPacket_Layer_Filter_FILTER_POINT = 8
+};
+bool LayersPacket_Layer_Filter_IsValid(int value);
+const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MIN = LayersPacket_Layer_Filter_FILTER_FAST;
+const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MAX = LayersPacket_Layer_Filter_FILTER_POINT;
+const int LayersPacket_Layer_Filter_Filter_ARRAYSIZE = LayersPacket_Layer_Filter_Filter_MAX + 1;
+
+enum Packet_DataType {
+ Packet_DataType_FRAMESTART = 1,
+ Packet_DataType_FRAMEEND = 2,
+ Packet_DataType_COLOR = 3,
+ Packet_DataType_TEXTURE = 4,
+ Packet_DataType_LAYERS = 5,
+ Packet_DataType_META = 6,
+ Packet_DataType_DRAW = 7
+};
+bool Packet_DataType_IsValid(int value);
+const Packet_DataType Packet_DataType_DataType_MIN = Packet_DataType_FRAMESTART;
+const Packet_DataType Packet_DataType_DataType_MAX = Packet_DataType_DRAW;
+const int Packet_DataType_DataType_ARRAYSIZE = Packet_DataType_DataType_MAX + 1;
+
+enum CommandPacket_CmdType {
+ CommandPacket_CmdType_NO_OP = 0,
+ CommandPacket_CmdType_LAYERS_TREE = 1,
+ CommandPacket_CmdType_LAYERS_BUFFER = 2
+};
+bool CommandPacket_CmdType_IsValid(int value);
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MIN = CommandPacket_CmdType_NO_OP;
+const CommandPacket_CmdType CommandPacket_CmdType_CmdType_MAX = CommandPacket_CmdType_LAYERS_BUFFER;
+const int CommandPacket_CmdType_CmdType_ARRAYSIZE = CommandPacket_CmdType_CmdType_MAX + 1;
+
+// ===================================================================
+
+class FramePacket : public ::google::protobuf::MessageLite {
+ public:
+ FramePacket();
+ virtual ~FramePacket();
+
+ FramePacket(const FramePacket& from);
+
+ inline FramePacket& operator=(const FramePacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const FramePacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const FramePacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(FramePacket* other);
+
+ // implements Message ----------------------------------------------
+
+ FramePacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const FramePacket& from);
+ void MergeFrom(const FramePacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional uint64 value = 1;
+ inline bool has_value() const;
+ inline void clear_value();
+ static const int kValueFieldNumber = 1;
+ inline ::google::protobuf::uint64 value() const;
+ inline void set_value(::google::protobuf::uint64 value);
+
+ // optional float scale = 2;
+ inline bool has_scale() const;
+ inline void clear_scale();
+ static const int kScaleFieldNumber = 2;
+ inline float scale() const;
+ inline void set_scale(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.FramePacket)
+ private:
+ inline void set_has_value();
+ inline void clear_has_value();
+ inline void set_has_scale();
+ inline void clear_has_scale();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 value_;
+ float scale_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static FramePacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class ColorPacket : public ::google::protobuf::MessageLite {
+ public:
+ ColorPacket();
+ virtual ~ColorPacket();
+
+ ColorPacket(const ColorPacket& from);
+
+ inline ColorPacket& operator=(const ColorPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const ColorPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const ColorPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(ColorPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ ColorPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const ColorPacket& from);
+ void MergeFrom(const ColorPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // required uint64 layerref = 1;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 1;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // optional uint32 width = 2;
+ inline bool has_width() const;
+ inline void clear_width();
+ static const int kWidthFieldNumber = 2;
+ inline ::google::protobuf::uint32 width() const;
+ inline void set_width(::google::protobuf::uint32 value);
+
+ // optional uint32 height = 3;
+ inline bool has_height() const;
+ inline void clear_height();
+ static const int kHeightFieldNumber = 3;
+ inline ::google::protobuf::uint32 height() const;
+ inline void set_height(::google::protobuf::uint32 value);
+
+ // optional uint32 color = 4;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 4;
+ inline ::google::protobuf::uint32 color() const;
+ inline void set_color(::google::protobuf::uint32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.ColorPacket)
+ private:
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+ inline void set_has_width();
+ inline void clear_has_width();
+ inline void set_has_height();
+ inline void clear_has_height();
+ inline void set_has_color();
+ inline void clear_has_color();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::uint32 width_;
+ ::google::protobuf::uint32 height_;
+ ::google::protobuf::uint32 color_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static ColorPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Rect : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Rect();
+ virtual ~TexturePacket_Rect();
+
+ TexturePacket_Rect(const TexturePacket_Rect& from);
+
+ inline TexturePacket_Rect& operator=(const TexturePacket_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Rect& from);
+ void MergeFrom(const TexturePacket_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional float x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline float x() const;
+ inline void set_x(float value);
+
+ // optional float y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline float y() const;
+ inline void set_y(float value);
+
+ // optional float w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline float w() const;
+ inline void set_w(float value);
+
+ // optional float h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline float h() const;
+ inline void set_h(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float x_;
+ float y_;
+ float w_;
+ float h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Size : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Size();
+ virtual ~TexturePacket_Size();
+
+ TexturePacket_Size(const TexturePacket_Size& from);
+
+ inline TexturePacket_Size& operator=(const TexturePacket_Size& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Size& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Size* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Size* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Size* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Size& from);
+ void MergeFrom(const TexturePacket_Size& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 w = 1;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 1;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 2;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 2;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Size)
+ private:
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Size* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_Matrix : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_Matrix();
+ virtual ~TexturePacket_Matrix();
+
+ TexturePacket_Matrix(const TexturePacket_Matrix& from);
+
+ inline TexturePacket_Matrix& operator=(const TexturePacket_Matrix& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_Matrix& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_Matrix* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_Matrix* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_Matrix* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_Matrix& from);
+ void MergeFrom(const TexturePacket_Matrix& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool is2D = 1;
+ inline bool has_is2d() const;
+ inline void clear_is2d();
+ static const int kIs2DFieldNumber = 1;
+ inline bool is2d() const;
+ inline void set_is2d(bool value);
+
+ // optional bool isId = 2;
+ inline bool has_isid() const;
+ inline void clear_isid();
+ static const int kIsIdFieldNumber = 2;
+ inline bool isid() const;
+ inline void set_isid(bool value);
+
+ // repeated float m = 3;
+ inline int m_size() const;
+ inline void clear_m();
+ static const int kMFieldNumber = 3;
+ inline float m(int index) const;
+ inline void set_m(int index, float value);
+ inline void add_m(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ m() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_m();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.Matrix)
+ private:
+ inline void set_has_is2d();
+ inline void clear_has_is2d();
+ inline void set_has_isid();
+ inline void clear_has_isid();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedField< float > m_;
+ bool is2d_;
+ bool isid_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_Matrix* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket_EffectMask : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket_EffectMask();
+ virtual ~TexturePacket_EffectMask();
+
+ TexturePacket_EffectMask(const TexturePacket_EffectMask& from);
+
+ inline TexturePacket_EffectMask& operator=(const TexturePacket_EffectMask& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket_EffectMask& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket_EffectMask* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket_EffectMask* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket_EffectMask* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket_EffectMask& from);
+ void MergeFrom(const TexturePacket_EffectMask& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool mIs3D = 1;
+ inline bool has_mis3d() const;
+ inline void clear_mis3d();
+ static const int kMIs3DFieldNumber = 1;
+ inline bool mis3d() const;
+ inline void set_mis3d(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+ inline bool has_msize() const;
+ inline void clear_msize();
+ static const int kMSizeFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Size& msize() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Size* mutable_msize();
+ inline ::mozilla::layers::layerscope::TexturePacket_Size* release_msize();
+ inline void set_allocated_msize(::mozilla::layers::layerscope::TexturePacket_Size* msize);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+ inline bool has_mmasktransform() const;
+ inline void clear_mmasktransform();
+ static const int kMMaskTransformFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Matrix& mmasktransform() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Matrix* mutable_mmasktransform();
+ inline ::mozilla::layers::layerscope::TexturePacket_Matrix* release_mmasktransform();
+ inline void set_allocated_mmasktransform(::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket.EffectMask)
+ private:
+ inline void set_has_mis3d();
+ inline void clear_has_mis3d();
+ inline void set_has_msize();
+ inline void clear_has_msize();
+ inline void set_has_mmasktransform();
+ inline void clear_has_mmasktransform();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::TexturePacket_Size* msize_;
+ ::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform_;
+ bool mis3d_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket_EffectMask* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class TexturePacket : public ::google::protobuf::MessageLite {
+ public:
+ TexturePacket();
+ virtual ~TexturePacket();
+
+ TexturePacket(const TexturePacket& from);
+
+ inline TexturePacket& operator=(const TexturePacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const TexturePacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const TexturePacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(TexturePacket* other);
+
+ // implements Message ----------------------------------------------
+
+ TexturePacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const TexturePacket& from);
+ void MergeFrom(const TexturePacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef TexturePacket_Rect Rect;
+ typedef TexturePacket_Size Size;
+ typedef TexturePacket_Matrix Matrix;
+ typedef TexturePacket_EffectMask EffectMask;
+
+ typedef TexturePacket_Filter Filter;
+ static const Filter GOOD = TexturePacket_Filter_GOOD;
+ static const Filter LINEAR = TexturePacket_Filter_LINEAR;
+ static const Filter POINT = TexturePacket_Filter_POINT;
+ static inline bool Filter_IsValid(int value) {
+ return TexturePacket_Filter_IsValid(value);
+ }
+ static const Filter Filter_MIN =
+ TexturePacket_Filter_Filter_MIN;
+ static const Filter Filter_MAX =
+ TexturePacket_Filter_Filter_MAX;
+ static const int Filter_ARRAYSIZE =
+ TexturePacket_Filter_Filter_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required uint64 layerref = 1;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 1;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // optional uint32 width = 2;
+ inline bool has_width() const;
+ inline void clear_width();
+ static const int kWidthFieldNumber = 2;
+ inline ::google::protobuf::uint32 width() const;
+ inline void set_width(::google::protobuf::uint32 value);
+
+ // optional uint32 height = 3;
+ inline bool has_height() const;
+ inline void clear_height();
+ static const int kHeightFieldNumber = 3;
+ inline ::google::protobuf::uint32 height() const;
+ inline void set_height(::google::protobuf::uint32 value);
+
+ // optional uint32 stride = 4;
+ inline bool has_stride() const;
+ inline void clear_stride();
+ static const int kStrideFieldNumber = 4;
+ inline ::google::protobuf::uint32 stride() const;
+ inline void set_stride(::google::protobuf::uint32 value);
+
+ // optional uint32 name = 5;
+ inline bool has_name() const;
+ inline void clear_name();
+ static const int kNameFieldNumber = 5;
+ inline ::google::protobuf::uint32 name() const;
+ inline void set_name(::google::protobuf::uint32 value);
+
+ // optional uint32 target = 6;
+ inline bool has_target() const;
+ inline void clear_target();
+ static const int kTargetFieldNumber = 6;
+ inline ::google::protobuf::uint32 target() const;
+ inline void set_target(::google::protobuf::uint32 value);
+
+ // optional uint32 dataformat = 7;
+ inline bool has_dataformat() const;
+ inline void clear_dataformat();
+ static const int kDataformatFieldNumber = 7;
+ inline ::google::protobuf::uint32 dataformat() const;
+ inline void set_dataformat(::google::protobuf::uint32 value);
+
+ // optional uint64 glcontext = 8;
+ inline bool has_glcontext() const;
+ inline void clear_glcontext();
+ static const int kGlcontextFieldNumber = 8;
+ inline ::google::protobuf::uint64 glcontext() const;
+ inline void set_glcontext(::google::protobuf::uint64 value);
+
+ // optional bytes data = 9;
+ inline bool has_data() const;
+ inline void clear_data();
+ static const int kDataFieldNumber = 9;
+ inline const ::std::string& data() const;
+ inline void set_data(const ::std::string& value);
+ inline void set_data(const char* value);
+ inline void set_data(const void* value, size_t size);
+ inline ::std::string* mutable_data();
+ inline ::std::string* release_data();
+ inline void set_allocated_data(::std::string* data);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+ inline bool has_mtexturecoords() const;
+ inline void clear_mtexturecoords();
+ static const int kMTextureCoordsFieldNumber = 10;
+ inline const ::mozilla::layers::layerscope::TexturePacket_Rect& mtexturecoords() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_Rect* mutable_mtexturecoords();
+ inline ::mozilla::layers::layerscope::TexturePacket_Rect* release_mtexturecoords();
+ inline void set_allocated_mtexturecoords(::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords);
+
+ // optional bool mPremultiplied = 11;
+ inline bool has_mpremultiplied() const;
+ inline void clear_mpremultiplied();
+ static const int kMPremultipliedFieldNumber = 11;
+ inline bool mpremultiplied() const;
+ inline void set_mpremultiplied(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+ inline bool has_mfilter() const;
+ inline void clear_mfilter();
+ static const int kMFilterFieldNumber = 12;
+ inline ::mozilla::layers::layerscope::TexturePacket_Filter mfilter() const;
+ inline void set_mfilter(::mozilla::layers::layerscope::TexturePacket_Filter value);
+
+ // optional bool isMask = 20;
+ inline bool has_ismask() const;
+ inline void clear_ismask();
+ static const int kIsMaskFieldNumber = 20;
+ inline bool ismask() const;
+ inline void set_ismask(bool value);
+
+ // optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+ inline bool has_mask() const;
+ inline void clear_mask();
+ static const int kMaskFieldNumber = 21;
+ inline const ::mozilla::layers::layerscope::TexturePacket_EffectMask& mask() const;
+ inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* mutable_mask();
+ inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* release_mask();
+ inline void set_allocated_mask(::mozilla::layers::layerscope::TexturePacket_EffectMask* mask);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket)
+ private:
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+ inline void set_has_width();
+ inline void clear_has_width();
+ inline void set_has_height();
+ inline void clear_has_height();
+ inline void set_has_stride();
+ inline void clear_has_stride();
+ inline void set_has_name();
+ inline void clear_has_name();
+ inline void set_has_target();
+ inline void clear_has_target();
+ inline void set_has_dataformat();
+ inline void clear_has_dataformat();
+ inline void set_has_glcontext();
+ inline void clear_has_glcontext();
+ inline void set_has_data();
+ inline void clear_has_data();
+ inline void set_has_mtexturecoords();
+ inline void clear_has_mtexturecoords();
+ inline void set_has_mpremultiplied();
+ inline void clear_has_mpremultiplied();
+ inline void set_has_mfilter();
+ inline void clear_has_mfilter();
+ inline void set_has_ismask();
+ inline void clear_has_ismask();
+ inline void set_has_mask();
+ inline void clear_has_mask();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::uint32 width_;
+ ::google::protobuf::uint32 height_;
+ ::google::protobuf::uint32 stride_;
+ ::google::protobuf::uint32 name_;
+ ::google::protobuf::uint32 target_;
+ ::google::protobuf::uint32 dataformat_;
+ ::google::protobuf::uint64 glcontext_;
+ ::std::string* data_;
+ ::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords_;
+ int mfilter_;
+ bool mpremultiplied_;
+ bool ismask_;
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask* mask_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static TexturePacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Size : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Size();
+ virtual ~LayersPacket_Layer_Size();
+
+ LayersPacket_Layer_Size(const LayersPacket_Layer_Size& from);
+
+ inline LayersPacket_Layer_Size& operator=(const LayersPacket_Layer_Size& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Size& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Size* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Size* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Size* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Size& from);
+ void MergeFrom(const LayersPacket_Layer_Size& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 w = 1;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 1;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 2;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 2;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Size)
+ private:
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Size* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Rect : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Rect();
+ virtual ~LayersPacket_Layer_Rect();
+
+ LayersPacket_Layer_Rect(const LayersPacket_Layer_Rect& from);
+
+ inline LayersPacket_Layer_Rect& operator=(const LayersPacket_Layer_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Rect& from);
+ void MergeFrom(const LayersPacket_Layer_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional int32 x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline ::google::protobuf::int32 x() const;
+ inline void set_x(::google::protobuf::int32 value);
+
+ // optional int32 y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline ::google::protobuf::int32 y() const;
+ inline void set_y(::google::protobuf::int32 value);
+
+ // optional int32 w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline ::google::protobuf::int32 w() const;
+ inline void set_w(::google::protobuf::int32 value);
+
+ // optional int32 h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline ::google::protobuf::int32 h() const;
+ inline void set_h(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::int32 x_;
+ ::google::protobuf::int32 y_;
+ ::google::protobuf::int32 w_;
+ ::google::protobuf::int32 h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Region : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Region();
+ virtual ~LayersPacket_Layer_Region();
+
+ LayersPacket_Layer_Region(const LayersPacket_Layer_Region& from);
+
+ inline LayersPacket_Layer_Region& operator=(const LayersPacket_Layer_Region& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Region& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Region* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Region* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Region* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Region& from);
+ void MergeFrom(const LayersPacket_Layer_Region& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+ inline int r_size() const;
+ inline void clear_r();
+ static const int kRFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& r(int index) const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_r(int index);
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* add_r();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >&
+ r() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >*
+ mutable_r();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Region)
+ private:
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect > r_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Region* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Matrix : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Matrix();
+ virtual ~LayersPacket_Layer_Matrix();
+
+ LayersPacket_Layer_Matrix(const LayersPacket_Layer_Matrix& from);
+
+ inline LayersPacket_Layer_Matrix& operator=(const LayersPacket_Layer_Matrix& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Matrix& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Matrix* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Matrix* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Matrix* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Matrix& from);
+ void MergeFrom(const LayersPacket_Layer_Matrix& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool is2D = 1;
+ inline bool has_is2d() const;
+ inline void clear_is2d();
+ static const int kIs2DFieldNumber = 1;
+ inline bool is2d() const;
+ inline void set_is2d(bool value);
+
+ // optional bool isId = 2;
+ inline bool has_isid() const;
+ inline void clear_isid();
+ static const int kIsIdFieldNumber = 2;
+ inline bool isid() const;
+ inline void set_isid(bool value);
+
+ // repeated float m = 3;
+ inline int m_size() const;
+ inline void clear_m();
+ static const int kMFieldNumber = 3;
+ inline float m(int index) const;
+ inline void set_m(int index, float value);
+ inline void add_m(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ m() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_m();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Matrix)
+ private:
+ inline void set_has_is2d();
+ inline void clear_has_is2d();
+ inline void set_has_isid();
+ inline void clear_has_isid();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedField< float > m_;
+ bool is2d_;
+ bool isid_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Matrix* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer_Shadow : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer_Shadow();
+ virtual ~LayersPacket_Layer_Shadow();
+
+ LayersPacket_Layer_Shadow(const LayersPacket_Layer_Shadow& from);
+
+ inline LayersPacket_Layer_Shadow& operator=(const LayersPacket_Layer_Shadow& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer_Shadow& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer_Shadow* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer_Shadow* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer_Shadow* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer_Shadow& from);
+ void MergeFrom(const LayersPacket_Layer_Shadow& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+ inline bool has_clip() const;
+ inline void clear_clip();
+ static const int kClipFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& clip() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_clip();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* release_clip();
+ inline void set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+ inline bool has_transform() const;
+ inline void clear_transform();
+ static const int kTransformFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& transform() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* mutable_transform();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* release_transform();
+ inline void set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+ inline bool has_vregion() const;
+ inline void clear_vregion();
+ static const int kVRegionFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vregion();
+ inline void set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer.Shadow)
+ private:
+ inline void set_has_clip();
+ inline void clear_has_clip();
+ inline void set_has_transform();
+ inline void clear_has_transform();
+ inline void set_has_vregion();
+ inline void clear_has_vregion();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer_Shadow* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket_Layer : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket_Layer();
+ virtual ~LayersPacket_Layer();
+
+ LayersPacket_Layer(const LayersPacket_Layer& from);
+
+ inline LayersPacket_Layer& operator=(const LayersPacket_Layer& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket_Layer& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket_Layer* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket_Layer* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket_Layer* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket_Layer& from);
+ void MergeFrom(const LayersPacket_Layer& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef LayersPacket_Layer_Size Size;
+ typedef LayersPacket_Layer_Rect Rect;
+ typedef LayersPacket_Layer_Region Region;
+ typedef LayersPacket_Layer_Matrix Matrix;
+ typedef LayersPacket_Layer_Shadow Shadow;
+
+ typedef LayersPacket_Layer_LayerType LayerType;
+ static const LayerType UnknownLayer = LayersPacket_Layer_LayerType_UnknownLayer;
+ static const LayerType LayerManager = LayersPacket_Layer_LayerType_LayerManager;
+ static const LayerType ContainerLayer = LayersPacket_Layer_LayerType_ContainerLayer;
+ static const LayerType PaintedLayer = LayersPacket_Layer_LayerType_PaintedLayer;
+ static const LayerType CanvasLayer = LayersPacket_Layer_LayerType_CanvasLayer;
+ static const LayerType ImageLayer = LayersPacket_Layer_LayerType_ImageLayer;
+ static const LayerType ColorLayer = LayersPacket_Layer_LayerType_ColorLayer;
+ static const LayerType RefLayer = LayersPacket_Layer_LayerType_RefLayer;
+ static const LayerType ReadbackLayer = LayersPacket_Layer_LayerType_ReadbackLayer;
+ static inline bool LayerType_IsValid(int value) {
+ return LayersPacket_Layer_LayerType_IsValid(value);
+ }
+ static const LayerType LayerType_MIN =
+ LayersPacket_Layer_LayerType_LayerType_MIN;
+ static const LayerType LayerType_MAX =
+ LayersPacket_Layer_LayerType_LayerType_MAX;
+ static const int LayerType_ARRAYSIZE =
+ LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE;
+
+ typedef LayersPacket_Layer_ScrollingDirect ScrollingDirect;
+ static const ScrollingDirect VERTICAL = LayersPacket_Layer_ScrollingDirect_VERTICAL;
+ static const ScrollingDirect HORIZONTAL = LayersPacket_Layer_ScrollingDirect_HORIZONTAL;
+ static inline bool ScrollingDirect_IsValid(int value) {
+ return LayersPacket_Layer_ScrollingDirect_IsValid(value);
+ }
+ static const ScrollingDirect ScrollingDirect_MIN =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MIN;
+ static const ScrollingDirect ScrollingDirect_MAX =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX;
+ static const int ScrollingDirect_ARRAYSIZE =
+ LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE;
+
+ typedef LayersPacket_Layer_Filter Filter;
+ static const Filter FILTER_FAST = LayersPacket_Layer_Filter_FILTER_FAST;
+ static const Filter FILTER_GOOD = LayersPacket_Layer_Filter_FILTER_GOOD;
+ static const Filter FILTER_BEST = LayersPacket_Layer_Filter_FILTER_BEST;
+ static const Filter FILTER_NEAREST = LayersPacket_Layer_Filter_FILTER_NEAREST;
+ static const Filter FILTER_BILINEAR = LayersPacket_Layer_Filter_FILTER_BILINEAR;
+ static const Filter FILTER_GAUSSIAN = LayersPacket_Layer_Filter_FILTER_GAUSSIAN;
+ static const Filter FILTER_SENTINEL = LayersPacket_Layer_Filter_FILTER_SENTINEL;
+ static const Filter FILTER_LINEAR = LayersPacket_Layer_Filter_FILTER_LINEAR;
+ static const Filter FILTER_POINT = LayersPacket_Layer_Filter_FILTER_POINT;
+ static inline bool Filter_IsValid(int value) {
+ return LayersPacket_Layer_Filter_IsValid(value);
+ }
+ static const Filter Filter_MIN =
+ LayersPacket_Layer_Filter_Filter_MIN;
+ static const Filter Filter_MAX =
+ LayersPacket_Layer_Filter_Filter_MAX;
+ static const int Filter_ARRAYSIZE =
+ LayersPacket_Layer_Filter_Filter_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType type() const;
+ inline void set_type(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType value);
+
+ // required uint64 ptr = 2;
+ inline bool has_ptr() const;
+ inline void clear_ptr();
+ static const int kPtrFieldNumber = 2;
+ inline ::google::protobuf::uint64 ptr() const;
+ inline void set_ptr(::google::protobuf::uint64 value);
+
+ // required uint64 parentPtr = 3;
+ inline bool has_parentptr() const;
+ inline void clear_parentptr();
+ static const int kParentPtrFieldNumber = 3;
+ inline ::google::protobuf::uint64 parentptr() const;
+ inline void set_parentptr(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+ inline bool has_clip() const;
+ inline void clear_clip();
+ static const int kClipFieldNumber = 10;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& clip() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* mutable_clip();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* release_clip();
+ inline void set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+ inline bool has_transform() const;
+ inline void clear_transform();
+ static const int kTransformFieldNumber = 11;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& transform() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* mutable_transform();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* release_transform();
+ inline void set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+ inline bool has_vregion() const;
+ inline void clear_vregion();
+ static const int kVRegionFieldNumber = 12;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vregion();
+ inline void set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+ inline bool has_shadow() const;
+ inline void clear_shadow();
+ static const int kShadowFieldNumber = 13;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow& shadow() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* mutable_shadow();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* release_shadow();
+ inline void set_allocated_shadow(::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow);
+
+ // optional float opacity = 14;
+ inline bool has_opacity() const;
+ inline void clear_opacity();
+ static const int kOpacityFieldNumber = 14;
+ inline float opacity() const;
+ inline void set_opacity(float value);
+
+ // optional bool cOpaque = 15;
+ inline bool has_copaque() const;
+ inline void clear_copaque();
+ static const int kCOpaqueFieldNumber = 15;
+ inline bool copaque() const;
+ inline void set_copaque(bool value);
+
+ // optional bool cAlpha = 16;
+ inline bool has_calpha() const;
+ inline void clear_calpha();
+ static const int kCAlphaFieldNumber = 16;
+ inline bool calpha() const;
+ inline void set_calpha(bool value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+ inline bool has_direct() const;
+ inline void clear_direct();
+ static const int kDirectFieldNumber = 17;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect direct() const;
+ inline void set_direct(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect value);
+
+ // optional uint64 barID = 18;
+ inline bool has_barid() const;
+ inline void clear_barid();
+ static const int kBarIDFieldNumber = 18;
+ inline ::google::protobuf::uint64 barid() const;
+ inline void set_barid(::google::protobuf::uint64 value);
+
+ // optional uint64 mask = 19;
+ inline bool has_mask() const;
+ inline void clear_mask();
+ static const int kMaskFieldNumber = 19;
+ inline ::google::protobuf::uint64 mask() const;
+ inline void set_mask(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+ inline bool has_hitregion() const;
+ inline void clear_hitregion();
+ static const int kHitRegionFieldNumber = 20;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& hitregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_hitregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_hitregion();
+ inline void set_allocated_hitregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+ inline bool has_dispatchregion() const;
+ inline void clear_dispatchregion();
+ static const int kDispatchRegionFieldNumber = 21;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& dispatchregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_dispatchregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_dispatchregion();
+ inline void set_allocated_dispatchregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+ inline bool has_noactionregion() const;
+ inline void clear_noactionregion();
+ static const int kNoActionRegionFieldNumber = 22;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& noactionregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_noactionregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_noactionregion();
+ inline void set_allocated_noactionregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+ inline bool has_hpanregion() const;
+ inline void clear_hpanregion();
+ static const int kHPanRegionFieldNumber = 23;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& hpanregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_hpanregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_hpanregion();
+ inline void set_allocated_hpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+ inline bool has_vpanregion() const;
+ inline void clear_vpanregion();
+ static const int kVPanRegionFieldNumber = 24;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& vpanregion() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_vpanregion();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_vpanregion();
+ inline void set_allocated_vpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+ inline bool has_valid() const;
+ inline void clear_valid();
+ static const int kValidFieldNumber = 100;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& valid() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* mutable_valid();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* release_valid();
+ inline void set_allocated_valid(::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid);
+
+ // optional uint32 color = 101;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 101;
+ inline ::google::protobuf::uint32 color() const;
+ inline void set_color(::google::protobuf::uint32 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+ inline bool has_filter() const;
+ inline void clear_filter();
+ static const int kFilterFieldNumber = 102;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Filter filter() const;
+ inline void set_filter(::mozilla::layers::layerscope::LayersPacket_Layer_Filter value);
+
+ // optional uint64 refID = 103;
+ inline bool has_refid() const;
+ inline void clear_refid();
+ static const int kRefIDFieldNumber = 103;
+ inline ::google::protobuf::uint64 refid() const;
+ inline void set_refid(::google::protobuf::uint64 value);
+
+ // optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+ inline bool has_size() const;
+ inline void clear_size();
+ static const int kSizeFieldNumber = 104;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Size& size() const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* mutable_size();
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* release_size();
+ inline void set_allocated_size(::mozilla::layers::layerscope::LayersPacket_Layer_Size* size);
+
+ // optional uint32 displayListLogLength = 105;
+ inline bool has_displaylistloglength() const;
+ inline void clear_displaylistloglength();
+ static const int kDisplayListLogLengthFieldNumber = 105;
+ inline ::google::protobuf::uint32 displaylistloglength() const;
+ inline void set_displaylistloglength(::google::protobuf::uint32 value);
+
+ // optional bytes displayListLog = 106;
+ inline bool has_displaylistlog() const;
+ inline void clear_displaylistlog();
+ static const int kDisplayListLogFieldNumber = 106;
+ inline const ::std::string& displaylistlog() const;
+ inline void set_displaylistlog(const ::std::string& value);
+ inline void set_displaylistlog(const char* value);
+ inline void set_displaylistlog(const void* value, size_t size);
+ inline ::std::string* mutable_displaylistlog();
+ inline ::std::string* release_displaylistlog();
+ inline void set_allocated_displaylistlog(::std::string* displaylistlog);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket.Layer)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_ptr();
+ inline void clear_has_ptr();
+ inline void set_has_parentptr();
+ inline void clear_has_parentptr();
+ inline void set_has_clip();
+ inline void clear_has_clip();
+ inline void set_has_transform();
+ inline void clear_has_transform();
+ inline void set_has_vregion();
+ inline void clear_has_vregion();
+ inline void set_has_shadow();
+ inline void clear_has_shadow();
+ inline void set_has_opacity();
+ inline void clear_has_opacity();
+ inline void set_has_copaque();
+ inline void clear_has_copaque();
+ inline void set_has_calpha();
+ inline void clear_has_calpha();
+ inline void set_has_direct();
+ inline void clear_has_direct();
+ inline void set_has_barid();
+ inline void clear_has_barid();
+ inline void set_has_mask();
+ inline void clear_has_mask();
+ inline void set_has_hitregion();
+ inline void clear_has_hitregion();
+ inline void set_has_dispatchregion();
+ inline void clear_has_dispatchregion();
+ inline void set_has_noactionregion();
+ inline void clear_has_noactionregion();
+ inline void set_has_hpanregion();
+ inline void clear_has_hpanregion();
+ inline void set_has_vpanregion();
+ inline void clear_has_vpanregion();
+ inline void set_has_valid();
+ inline void clear_has_valid();
+ inline void set_has_color();
+ inline void clear_has_color();
+ inline void set_has_filter();
+ inline void clear_has_filter();
+ inline void set_has_refid();
+ inline void clear_has_refid();
+ inline void set_has_size();
+ inline void clear_has_size();
+ inline void set_has_displaylistloglength();
+ inline void clear_has_displaylistloglength();
+ inline void set_has_displaylistlog();
+ inline void clear_has_displaylistlog();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::uint64 ptr_;
+ ::google::protobuf::uint64 parentptr_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip_;
+ int type_;
+ float opacity_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow_;
+ bool copaque_;
+ bool calpha_;
+ int direct_;
+ ::google::protobuf::uint64 barid_;
+ ::google::protobuf::uint64 mask_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid_;
+ ::google::protobuf::uint32 color_;
+ int filter_;
+ ::google::protobuf::uint64 refid_;
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size* size_;
+ ::std::string* displaylistlog_;
+ ::google::protobuf::uint32 displaylistloglength_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket_Layer* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LayersPacket : public ::google::protobuf::MessageLite {
+ public:
+ LayersPacket();
+ virtual ~LayersPacket();
+
+ LayersPacket(const LayersPacket& from);
+
+ inline LayersPacket& operator=(const LayersPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const LayersPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LayersPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(LayersPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ LayersPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LayersPacket& from);
+ void MergeFrom(const LayersPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef LayersPacket_Layer Layer;
+
+ // accessors -------------------------------------------------------
+
+ // repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+ inline int layer_size() const;
+ inline void clear_layer();
+ static const int kLayerFieldNumber = 1;
+ inline const ::mozilla::layers::layerscope::LayersPacket_Layer& layer(int index) const;
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer* mutable_layer(int index);
+ inline ::mozilla::layers::layerscope::LayersPacket_Layer* add_layer();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >&
+ layer() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >*
+ mutable_layer();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.LayersPacket)
+ private:
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer > layer_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static LayersPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class MetaPacket : public ::google::protobuf::MessageLite {
+ public:
+ MetaPacket();
+ virtual ~MetaPacket();
+
+ MetaPacket(const MetaPacket& from);
+
+ inline MetaPacket& operator=(const MetaPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const MetaPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const MetaPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(MetaPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ MetaPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const MetaPacket& from);
+ void MergeFrom(const MetaPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional bool composedByHwc = 1;
+ inline bool has_composedbyhwc() const;
+ inline void clear_composedbyhwc();
+ static const int kComposedByHwcFieldNumber = 1;
+ inline bool composedbyhwc() const;
+ inline void set_composedbyhwc(bool value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.MetaPacket)
+ private:
+ inline void set_has_composedbyhwc();
+ inline void clear_has_composedbyhwc();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ bool composedbyhwc_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static MetaPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class DrawPacket_Rect : public ::google::protobuf::MessageLite {
+ public:
+ DrawPacket_Rect();
+ virtual ~DrawPacket_Rect();
+
+ DrawPacket_Rect(const DrawPacket_Rect& from);
+
+ inline DrawPacket_Rect& operator=(const DrawPacket_Rect& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const DrawPacket_Rect& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const DrawPacket_Rect* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(DrawPacket_Rect* other);
+
+ // implements Message ----------------------------------------------
+
+ DrawPacket_Rect* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const DrawPacket_Rect& from);
+ void MergeFrom(const DrawPacket_Rect& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // required float x = 1;
+ inline bool has_x() const;
+ inline void clear_x();
+ static const int kXFieldNumber = 1;
+ inline float x() const;
+ inline void set_x(float value);
+
+ // required float y = 2;
+ inline bool has_y() const;
+ inline void clear_y();
+ static const int kYFieldNumber = 2;
+ inline float y() const;
+ inline void set_y(float value);
+
+ // required float w = 3;
+ inline bool has_w() const;
+ inline void clear_w();
+ static const int kWFieldNumber = 3;
+ inline float w() const;
+ inline void set_w(float value);
+
+ // required float h = 4;
+ inline bool has_h() const;
+ inline void clear_h();
+ static const int kHFieldNumber = 4;
+ inline float h() const;
+ inline void set_h(float value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.DrawPacket.Rect)
+ private:
+ inline void set_has_x();
+ inline void clear_has_x();
+ inline void set_has_y();
+ inline void clear_has_y();
+ inline void set_has_w();
+ inline void clear_has_w();
+ inline void set_has_h();
+ inline void clear_has_h();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float x_;
+ float y_;
+ float w_;
+ float h_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static DrawPacket_Rect* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class DrawPacket : public ::google::protobuf::MessageLite {
+ public:
+ DrawPacket();
+ virtual ~DrawPacket();
+
+ DrawPacket(const DrawPacket& from);
+
+ inline DrawPacket& operator=(const DrawPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const DrawPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const DrawPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(DrawPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ DrawPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const DrawPacket& from);
+ void MergeFrom(const DrawPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef DrawPacket_Rect Rect;
+
+ // accessors -------------------------------------------------------
+
+ // required float offsetX = 1;
+ inline bool has_offsetx() const;
+ inline void clear_offsetx();
+ static const int kOffsetXFieldNumber = 1;
+ inline float offsetx() const;
+ inline void set_offsetx(float value);
+
+ // required float offsetY = 2;
+ inline bool has_offsety() const;
+ inline void clear_offsety();
+ static const int kOffsetYFieldNumber = 2;
+ inline float offsety() const;
+ inline void set_offsety(float value);
+
+ // repeated float mvMatrix = 3;
+ inline int mvmatrix_size() const;
+ inline void clear_mvmatrix();
+ static const int kMvMatrixFieldNumber = 3;
+ inline float mvmatrix(int index) const;
+ inline void set_mvmatrix(int index, float value);
+ inline void add_mvmatrix(float value);
+ inline const ::google::protobuf::RepeatedField< float >&
+ mvmatrix() const;
+ inline ::google::protobuf::RepeatedField< float >*
+ mutable_mvmatrix();
+
+ // required uint32 totalRects = 4;
+ inline bool has_totalrects() const;
+ inline void clear_totalrects();
+ static const int kTotalRectsFieldNumber = 4;
+ inline ::google::protobuf::uint32 totalrects() const;
+ inline void set_totalrects(::google::protobuf::uint32 value);
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+ inline int layerrect_size() const;
+ inline void clear_layerrect();
+ static const int kLayerRectFieldNumber = 5;
+ inline const ::mozilla::layers::layerscope::DrawPacket_Rect& layerrect(int index) const;
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* mutable_layerrect(int index);
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* add_layerrect();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+ layerrect() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+ mutable_layerrect();
+
+ // required uint64 layerref = 6;
+ inline bool has_layerref() const;
+ inline void clear_layerref();
+ static const int kLayerrefFieldNumber = 6;
+ inline ::google::protobuf::uint64 layerref() const;
+ inline void set_layerref(::google::protobuf::uint64 value);
+
+ // repeated uint32 texIDs = 7;
+ inline int texids_size() const;
+ inline void clear_texids();
+ static const int kTexIDsFieldNumber = 7;
+ inline ::google::protobuf::uint32 texids(int index) const;
+ inline void set_texids(int index, ::google::protobuf::uint32 value);
+ inline void add_texids(::google::protobuf::uint32 value);
+ inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+ texids() const;
+ inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+ mutable_texids();
+
+ // repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+ inline int texturerect_size() const;
+ inline void clear_texturerect();
+ static const int kTextureRectFieldNumber = 8;
+ inline const ::mozilla::layers::layerscope::DrawPacket_Rect& texturerect(int index) const;
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* mutable_texturerect(int index);
+ inline ::mozilla::layers::layerscope::DrawPacket_Rect* add_texturerect();
+ inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+ texturerect() const;
+ inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+ mutable_texturerect();
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.DrawPacket)
+ private:
+ inline void set_has_offsetx();
+ inline void clear_has_offsetx();
+ inline void set_has_offsety();
+ inline void clear_has_offsety();
+ inline void set_has_totalrects();
+ inline void clear_has_totalrects();
+ inline void set_has_layerref();
+ inline void clear_has_layerref();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ float offsetx_;
+ float offsety_;
+ ::google::protobuf::RepeatedField< float > mvmatrix_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect > layerrect_;
+ ::google::protobuf::uint64 layerref_;
+ ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > texids_;
+ ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect > texturerect_;
+ ::google::protobuf::uint32 totalrects_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static DrawPacket* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class Packet : public ::google::protobuf::MessageLite {
+ public:
+ Packet();
+ virtual ~Packet();
+
+ Packet(const Packet& from);
+
+ inline Packet& operator=(const Packet& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const Packet& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const Packet* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(Packet* other);
+
+ // implements Message ----------------------------------------------
+
+ Packet* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const Packet& from);
+ void MergeFrom(const Packet& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef Packet_DataType DataType;
+ static const DataType FRAMESTART = Packet_DataType_FRAMESTART;
+ static const DataType FRAMEEND = Packet_DataType_FRAMEEND;
+ static const DataType COLOR = Packet_DataType_COLOR;
+ static const DataType TEXTURE = Packet_DataType_TEXTURE;
+ static const DataType LAYERS = Packet_DataType_LAYERS;
+ static const DataType META = Packet_DataType_META;
+ static const DataType DRAW = Packet_DataType_DRAW;
+ static inline bool DataType_IsValid(int value) {
+ return Packet_DataType_IsValid(value);
+ }
+ static const DataType DataType_MIN =
+ Packet_DataType_DataType_MIN;
+ static const DataType DataType_MAX =
+ Packet_DataType_DataType_MAX;
+ static const int DataType_ARRAYSIZE =
+ Packet_DataType_DataType_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.Packet.DataType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::Packet_DataType type() const;
+ inline void set_type(::mozilla::layers::layerscope::Packet_DataType value);
+
+ // optional .mozilla.layers.layerscope.FramePacket frame = 2;
+ inline bool has_frame() const;
+ inline void clear_frame();
+ static const int kFrameFieldNumber = 2;
+ inline const ::mozilla::layers::layerscope::FramePacket& frame() const;
+ inline ::mozilla::layers::layerscope::FramePacket* mutable_frame();
+ inline ::mozilla::layers::layerscope::FramePacket* release_frame();
+ inline void set_allocated_frame(::mozilla::layers::layerscope::FramePacket* frame);
+
+ // optional .mozilla.layers.layerscope.ColorPacket color = 3;
+ inline bool has_color() const;
+ inline void clear_color();
+ static const int kColorFieldNumber = 3;
+ inline const ::mozilla::layers::layerscope::ColorPacket& color() const;
+ inline ::mozilla::layers::layerscope::ColorPacket* mutable_color();
+ inline ::mozilla::layers::layerscope::ColorPacket* release_color();
+ inline void set_allocated_color(::mozilla::layers::layerscope::ColorPacket* color);
+
+ // optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+ inline bool has_texture() const;
+ inline void clear_texture();
+ static const int kTextureFieldNumber = 4;
+ inline const ::mozilla::layers::layerscope::TexturePacket& texture() const;
+ inline ::mozilla::layers::layerscope::TexturePacket* mutable_texture();
+ inline ::mozilla::layers::layerscope::TexturePacket* release_texture();
+ inline void set_allocated_texture(::mozilla::layers::layerscope::TexturePacket* texture);
+
+ // optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+ inline bool has_layers() const;
+ inline void clear_layers();
+ static const int kLayersFieldNumber = 5;
+ inline const ::mozilla::layers::layerscope::LayersPacket& layers() const;
+ inline ::mozilla::layers::layerscope::LayersPacket* mutable_layers();
+ inline ::mozilla::layers::layerscope::LayersPacket* release_layers();
+ inline void set_allocated_layers(::mozilla::layers::layerscope::LayersPacket* layers);
+
+ // optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+ inline bool has_meta() const;
+ inline void clear_meta();
+ static const int kMetaFieldNumber = 6;
+ inline const ::mozilla::layers::layerscope::MetaPacket& meta() const;
+ inline ::mozilla::layers::layerscope::MetaPacket* mutable_meta();
+ inline ::mozilla::layers::layerscope::MetaPacket* release_meta();
+ inline void set_allocated_meta(::mozilla::layers::layerscope::MetaPacket* meta);
+
+ // optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+ inline bool has_draw() const;
+ inline void clear_draw();
+ static const int kDrawFieldNumber = 7;
+ inline const ::mozilla::layers::layerscope::DrawPacket& draw() const;
+ inline ::mozilla::layers::layerscope::DrawPacket* mutable_draw();
+ inline ::mozilla::layers::layerscope::DrawPacket* release_draw();
+ inline void set_allocated_draw(::mozilla::layers::layerscope::DrawPacket* draw);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.Packet)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_frame();
+ inline void clear_has_frame();
+ inline void set_has_color();
+ inline void clear_has_color();
+ inline void set_has_texture();
+ inline void clear_has_texture();
+ inline void set_has_layers();
+ inline void clear_has_layers();
+ inline void set_has_meta();
+ inline void clear_has_meta();
+ inline void set_has_draw();
+ inline void clear_has_draw();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::mozilla::layers::layerscope::FramePacket* frame_;
+ ::mozilla::layers::layerscope::ColorPacket* color_;
+ ::mozilla::layers::layerscope::TexturePacket* texture_;
+ ::mozilla::layers::layerscope::LayersPacket* layers_;
+ ::mozilla::layers::layerscope::MetaPacket* meta_;
+ ::mozilla::layers::layerscope::DrawPacket* draw_;
+ int type_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static Packet* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class CommandPacket : public ::google::protobuf::MessageLite {
+ public:
+ CommandPacket();
+ virtual ~CommandPacket();
+
+ CommandPacket(const CommandPacket& from);
+
+ inline CommandPacket& operator=(const CommandPacket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::std::string& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::std::string* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const CommandPacket& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const CommandPacket* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ void Swap(CommandPacket* other);
+
+ // implements Message ----------------------------------------------
+
+ CommandPacket* New() const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const CommandPacket& from);
+ void MergeFrom(const CommandPacket& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef CommandPacket_CmdType CmdType;
+ static const CmdType NO_OP = CommandPacket_CmdType_NO_OP;
+ static const CmdType LAYERS_TREE = CommandPacket_CmdType_LAYERS_TREE;
+ static const CmdType LAYERS_BUFFER = CommandPacket_CmdType_LAYERS_BUFFER;
+ static inline bool CmdType_IsValid(int value) {
+ return CommandPacket_CmdType_IsValid(value);
+ }
+ static const CmdType CmdType_MIN =
+ CommandPacket_CmdType_CmdType_MIN;
+ static const CmdType CmdType_MAX =
+ CommandPacket_CmdType_CmdType_MAX;
+ static const int CmdType_ARRAYSIZE =
+ CommandPacket_CmdType_CmdType_ARRAYSIZE;
+
+ // accessors -------------------------------------------------------
+
+ // required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 1;
+ inline ::mozilla::layers::layerscope::CommandPacket_CmdType type() const;
+ inline void set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value);
+
+ // optional bool value = 2;
+ inline bool has_value() const;
+ inline void clear_value();
+ static const int kValueFieldNumber = 2;
+ inline bool value() const;
+ inline void set_value(bool value);
+
+ // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.CommandPacket)
+ private:
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_value();
+ inline void clear_has_value();
+
+ ::std::string _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ int type_;
+ bool value_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_LayerScopePacket_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_LayerScopePacket_2eproto();
+ friend void protobuf_ShutdownFile_LayerScopePacket_2eproto();
+
+ void InitAsDefaultInstance();
+ static CommandPacket* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+// FramePacket
+
+// optional uint64 value = 1;
+inline bool FramePacket::has_value() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void FramePacket::set_has_value() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void FramePacket::clear_has_value() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void FramePacket::clear_value() {
+ value_ = GOOGLE_ULONGLONG(0);
+ clear_has_value();
+}
+inline ::google::protobuf::uint64 FramePacket::value() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.FramePacket.value)
+ return value_;
+}
+inline void FramePacket::set_value(::google::protobuf::uint64 value) {
+ set_has_value();
+ value_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.FramePacket.value)
+}
+
+// optional float scale = 2;
+inline bool FramePacket::has_scale() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void FramePacket::set_has_scale() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void FramePacket::clear_has_scale() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void FramePacket::clear_scale() {
+ scale_ = 0;
+ clear_has_scale();
+}
+inline float FramePacket::scale() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.FramePacket.scale)
+ return scale_;
+}
+inline void FramePacket::set_scale(float value) {
+ set_has_scale();
+ scale_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.FramePacket.scale)
+}
+
+// -------------------------------------------------------------------
+
+// ColorPacket
+
+// required uint64 layerref = 1;
+inline bool ColorPacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void ColorPacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void ColorPacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void ColorPacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 ColorPacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.layerref)
+ return layerref_;
+}
+inline void ColorPacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.layerref)
+}
+
+// optional uint32 width = 2;
+inline bool ColorPacket::has_width() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void ColorPacket::set_has_width() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void ColorPacket::clear_has_width() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void ColorPacket::clear_width() {
+ width_ = 0u;
+ clear_has_width();
+}
+inline ::google::protobuf::uint32 ColorPacket::width() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.width)
+ return width_;
+}
+inline void ColorPacket::set_width(::google::protobuf::uint32 value) {
+ set_has_width();
+ width_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.width)
+}
+
+// optional uint32 height = 3;
+inline bool ColorPacket::has_height() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void ColorPacket::set_has_height() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void ColorPacket::clear_has_height() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void ColorPacket::clear_height() {
+ height_ = 0u;
+ clear_has_height();
+}
+inline ::google::protobuf::uint32 ColorPacket::height() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.height)
+ return height_;
+}
+inline void ColorPacket::set_height(::google::protobuf::uint32 value) {
+ set_has_height();
+ height_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.height)
+}
+
+// optional uint32 color = 4;
+inline bool ColorPacket::has_color() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void ColorPacket::set_has_color() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void ColorPacket::clear_has_color() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void ColorPacket::clear_color() {
+ color_ = 0u;
+ clear_has_color();
+}
+inline ::google::protobuf::uint32 ColorPacket::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.ColorPacket.color)
+ return color_;
+}
+inline void ColorPacket::set_color(::google::protobuf::uint32 value) {
+ set_has_color();
+ color_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.ColorPacket.color)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Rect
+
+// optional float x = 1;
+inline bool TexturePacket_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline float TexturePacket_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.x)
+ return x_;
+}
+inline void TexturePacket_Rect::set_x(float value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.x)
+}
+
+// optional float y = 2;
+inline bool TexturePacket_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline float TexturePacket_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.y)
+ return y_;
+}
+inline void TexturePacket_Rect::set_y(float value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.y)
+}
+
+// optional float w = 3;
+inline bool TexturePacket_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline float TexturePacket_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.w)
+ return w_;
+}
+inline void TexturePacket_Rect::set_w(float value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.w)
+}
+
+// optional float h = 4;
+inline bool TexturePacket_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void TexturePacket_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void TexturePacket_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void TexturePacket_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline float TexturePacket_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Rect.h)
+ return h_;
+}
+inline void TexturePacket_Rect::set_h(float value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Size
+
+// optional int32 w = 1;
+inline bool TexturePacket_Size::has_w() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Size::set_has_w() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Size::clear_has_w() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Size::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 TexturePacket_Size::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Size.w)
+ return w_;
+}
+inline void TexturePacket_Size::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Size.w)
+}
+
+// optional int32 h = 2;
+inline bool TexturePacket_Size::has_h() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Size::set_has_h() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Size::clear_has_h() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Size::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 TexturePacket_Size::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Size.h)
+ return h_;
+}
+inline void TexturePacket_Size::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Size.h)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_Matrix
+
+// optional bool is2D = 1;
+inline bool TexturePacket_Matrix::has_is2d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_Matrix::set_has_is2d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_Matrix::clear_has_is2d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_Matrix::clear_is2d() {
+ is2d_ = false;
+ clear_has_is2d();
+}
+inline bool TexturePacket_Matrix::is2d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.is2D)
+ return is2d_;
+}
+inline void TexturePacket_Matrix::set_is2d(bool value) {
+ set_has_is2d();
+ is2d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.is2D)
+}
+
+// optional bool isId = 2;
+inline bool TexturePacket_Matrix::has_isid() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_Matrix::set_has_isid() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_Matrix::clear_has_isid() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_Matrix::clear_isid() {
+ isid_ = false;
+ clear_has_isid();
+}
+inline bool TexturePacket_Matrix::isid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.isId)
+ return isid_;
+}
+inline void TexturePacket_Matrix::set_isid(bool value) {
+ set_has_isid();
+ isid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.isId)
+}
+
+// repeated float m = 3;
+inline int TexturePacket_Matrix::m_size() const {
+ return m_.size();
+}
+inline void TexturePacket_Matrix::clear_m() {
+ m_.Clear();
+}
+inline float TexturePacket_Matrix::m(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return m_.Get(index);
+}
+inline void TexturePacket_Matrix::set_m(int index, float value) {
+ m_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+}
+inline void TexturePacket_Matrix::add_m(float value) {
+ m_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+TexturePacket_Matrix::m() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return m_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+TexturePacket_Matrix::mutable_m() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.TexturePacket.Matrix.m)
+ return &m_;
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket_EffectMask
+
+// optional bool mIs3D = 1;
+inline bool TexturePacket_EffectMask::has_mis3d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_mis3d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket_EffectMask::clear_has_mis3d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket_EffectMask::clear_mis3d() {
+ mis3d_ = false;
+ clear_has_mis3d();
+}
+inline bool TexturePacket_EffectMask::mis3d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mIs3D)
+ return mis3d_;
+}
+inline void TexturePacket_EffectMask::set_mis3d(bool value) {
+ set_has_mis3d();
+ mis3d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.EffectMask.mIs3D)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Size mSize = 2;
+inline bool TexturePacket_EffectMask::has_msize() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_msize() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket_EffectMask::clear_has_msize() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket_EffectMask::clear_msize() {
+ if (msize_ != NULL) msize_->::mozilla::layers::layerscope::TexturePacket_Size::Clear();
+ clear_has_msize();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Size& TexturePacket_EffectMask::msize() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return msize_ != NULL ? *msize_ : *default_instance().msize_;
+#else
+ return msize_ != NULL ? *msize_ : *default_instance_->msize_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Size* TexturePacket_EffectMask::mutable_msize() {
+ set_has_msize();
+ if (msize_ == NULL) msize_ = new ::mozilla::layers::layerscope::TexturePacket_Size;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+ return msize_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Size* TexturePacket_EffectMask::release_msize() {
+ clear_has_msize();
+ ::mozilla::layers::layerscope::TexturePacket_Size* temp = msize_;
+ msize_ = NULL;
+ return temp;
+}
+inline void TexturePacket_EffectMask::set_allocated_msize(::mozilla::layers::layerscope::TexturePacket_Size* msize) {
+ delete msize_;
+ msize_ = msize;
+ if (msize) {
+ set_has_msize();
+ } else {
+ clear_has_msize();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.EffectMask.mSize)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Matrix mMaskTransform = 3;
+inline bool TexturePacket_EffectMask::has_mmasktransform() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket_EffectMask::set_has_mmasktransform() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket_EffectMask::clear_has_mmasktransform() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket_EffectMask::clear_mmasktransform() {
+ if (mmasktransform_ != NULL) mmasktransform_->::mozilla::layers::layerscope::TexturePacket_Matrix::Clear();
+ clear_has_mmasktransform();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Matrix& TexturePacket_EffectMask::mmasktransform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mmasktransform_ != NULL ? *mmasktransform_ : *default_instance().mmasktransform_;
+#else
+ return mmasktransform_ != NULL ? *mmasktransform_ : *default_instance_->mmasktransform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Matrix* TexturePacket_EffectMask::mutable_mmasktransform() {
+ set_has_mmasktransform();
+ if (mmasktransform_ == NULL) mmasktransform_ = new ::mozilla::layers::layerscope::TexturePacket_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+ return mmasktransform_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Matrix* TexturePacket_EffectMask::release_mmasktransform() {
+ clear_has_mmasktransform();
+ ::mozilla::layers::layerscope::TexturePacket_Matrix* temp = mmasktransform_;
+ mmasktransform_ = NULL;
+ return temp;
+}
+inline void TexturePacket_EffectMask::set_allocated_mmasktransform(::mozilla::layers::layerscope::TexturePacket_Matrix* mmasktransform) {
+ delete mmasktransform_;
+ mmasktransform_ = mmasktransform;
+ if (mmasktransform) {
+ set_has_mmasktransform();
+ } else {
+ clear_has_mmasktransform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.EffectMask.mMaskTransform)
+}
+
+// -------------------------------------------------------------------
+
+// TexturePacket
+
+// required uint64 layerref = 1;
+inline bool TexturePacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void TexturePacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void TexturePacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void TexturePacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 TexturePacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.layerref)
+ return layerref_;
+}
+inline void TexturePacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.layerref)
+}
+
+// optional uint32 width = 2;
+inline bool TexturePacket::has_width() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void TexturePacket::set_has_width() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void TexturePacket::clear_has_width() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void TexturePacket::clear_width() {
+ width_ = 0u;
+ clear_has_width();
+}
+inline ::google::protobuf::uint32 TexturePacket::width() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.width)
+ return width_;
+}
+inline void TexturePacket::set_width(::google::protobuf::uint32 value) {
+ set_has_width();
+ width_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.width)
+}
+
+// optional uint32 height = 3;
+inline bool TexturePacket::has_height() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void TexturePacket::set_has_height() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void TexturePacket::clear_has_height() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void TexturePacket::clear_height() {
+ height_ = 0u;
+ clear_has_height();
+}
+inline ::google::protobuf::uint32 TexturePacket::height() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.height)
+ return height_;
+}
+inline void TexturePacket::set_height(::google::protobuf::uint32 value) {
+ set_has_height();
+ height_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.height)
+}
+
+// optional uint32 stride = 4;
+inline bool TexturePacket::has_stride() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void TexturePacket::set_has_stride() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void TexturePacket::clear_has_stride() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void TexturePacket::clear_stride() {
+ stride_ = 0u;
+ clear_has_stride();
+}
+inline ::google::protobuf::uint32 TexturePacket::stride() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.stride)
+ return stride_;
+}
+inline void TexturePacket::set_stride(::google::protobuf::uint32 value) {
+ set_has_stride();
+ stride_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.stride)
+}
+
+// optional uint32 name = 5;
+inline bool TexturePacket::has_name() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void TexturePacket::set_has_name() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void TexturePacket::clear_has_name() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void TexturePacket::clear_name() {
+ name_ = 0u;
+ clear_has_name();
+}
+inline ::google::protobuf::uint32 TexturePacket::name() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.name)
+ return name_;
+}
+inline void TexturePacket::set_name(::google::protobuf::uint32 value) {
+ set_has_name();
+ name_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.name)
+}
+
+// optional uint32 target = 6;
+inline bool TexturePacket::has_target() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void TexturePacket::set_has_target() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void TexturePacket::clear_has_target() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void TexturePacket::clear_target() {
+ target_ = 0u;
+ clear_has_target();
+}
+inline ::google::protobuf::uint32 TexturePacket::target() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.target)
+ return target_;
+}
+inline void TexturePacket::set_target(::google::protobuf::uint32 value) {
+ set_has_target();
+ target_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.target)
+}
+
+// optional uint32 dataformat = 7;
+inline bool TexturePacket::has_dataformat() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void TexturePacket::set_has_dataformat() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void TexturePacket::clear_has_dataformat() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void TexturePacket::clear_dataformat() {
+ dataformat_ = 0u;
+ clear_has_dataformat();
+}
+inline ::google::protobuf::uint32 TexturePacket::dataformat() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.dataformat)
+ return dataformat_;
+}
+inline void TexturePacket::set_dataformat(::google::protobuf::uint32 value) {
+ set_has_dataformat();
+ dataformat_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.dataformat)
+}
+
+// optional uint64 glcontext = 8;
+inline bool TexturePacket::has_glcontext() const {
+ return (_has_bits_[0] & 0x00000080u) != 0;
+}
+inline void TexturePacket::set_has_glcontext() {
+ _has_bits_[0] |= 0x00000080u;
+}
+inline void TexturePacket::clear_has_glcontext() {
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline void TexturePacket::clear_glcontext() {
+ glcontext_ = GOOGLE_ULONGLONG(0);
+ clear_has_glcontext();
+}
+inline ::google::protobuf::uint64 TexturePacket::glcontext() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.glcontext)
+ return glcontext_;
+}
+inline void TexturePacket::set_glcontext(::google::protobuf::uint64 value) {
+ set_has_glcontext();
+ glcontext_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.glcontext)
+}
+
+// optional bytes data = 9;
+inline bool TexturePacket::has_data() const {
+ return (_has_bits_[0] & 0x00000100u) != 0;
+}
+inline void TexturePacket::set_has_data() {
+ _has_bits_[0] |= 0x00000100u;
+}
+inline void TexturePacket::clear_has_data() {
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline void TexturePacket::clear_data() {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_->clear();
+ }
+ clear_has_data();
+}
+inline const ::std::string& TexturePacket::data() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.data)
+ return *data_;
+}
+inline void TexturePacket::set_data(const ::std::string& value) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline void TexturePacket::set_data(const char* value) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(value);
+ // @@protoc_insertion_point(field_set_char:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline void TexturePacket::set_data(const void* value, size_t size) {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ data_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:mozilla.layers.layerscope.TexturePacket.data)
+}
+inline ::std::string* TexturePacket::mutable_data() {
+ set_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ data_ = new ::std::string;
+ }
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.data)
+ return data_;
+}
+inline ::std::string* TexturePacket::release_data() {
+ clear_has_data();
+ if (data_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = data_;
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void TexturePacket::set_allocated_data(::std::string* data) {
+ if (data_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete data_;
+ }
+ if (data) {
+ set_has_data();
+ data_ = data;
+ } else {
+ clear_has_data();
+ data_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.data)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Rect mTextureCoords = 10;
+inline bool TexturePacket::has_mtexturecoords() const {
+ return (_has_bits_[0] & 0x00000200u) != 0;
+}
+inline void TexturePacket::set_has_mtexturecoords() {
+ _has_bits_[0] |= 0x00000200u;
+}
+inline void TexturePacket::clear_has_mtexturecoords() {
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline void TexturePacket::clear_mtexturecoords() {
+ if (mtexturecoords_ != NULL) mtexturecoords_->::mozilla::layers::layerscope::TexturePacket_Rect::Clear();
+ clear_has_mtexturecoords();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_Rect& TexturePacket::mtexturecoords() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mtexturecoords_ != NULL ? *mtexturecoords_ : *default_instance().mtexturecoords_;
+#else
+ return mtexturecoords_ != NULL ? *mtexturecoords_ : *default_instance_->mtexturecoords_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Rect* TexturePacket::mutable_mtexturecoords() {
+ set_has_mtexturecoords();
+ if (mtexturecoords_ == NULL) mtexturecoords_ = new ::mozilla::layers::layerscope::TexturePacket_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+ return mtexturecoords_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Rect* TexturePacket::release_mtexturecoords() {
+ clear_has_mtexturecoords();
+ ::mozilla::layers::layerscope::TexturePacket_Rect* temp = mtexturecoords_;
+ mtexturecoords_ = NULL;
+ return temp;
+}
+inline void TexturePacket::set_allocated_mtexturecoords(::mozilla::layers::layerscope::TexturePacket_Rect* mtexturecoords) {
+ delete mtexturecoords_;
+ mtexturecoords_ = mtexturecoords;
+ if (mtexturecoords) {
+ set_has_mtexturecoords();
+ } else {
+ clear_has_mtexturecoords();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.mTextureCoords)
+}
+
+// optional bool mPremultiplied = 11;
+inline bool TexturePacket::has_mpremultiplied() const {
+ return (_has_bits_[0] & 0x00000400u) != 0;
+}
+inline void TexturePacket::set_has_mpremultiplied() {
+ _has_bits_[0] |= 0x00000400u;
+}
+inline void TexturePacket::clear_has_mpremultiplied() {
+ _has_bits_[0] &= ~0x00000400u;
+}
+inline void TexturePacket::clear_mpremultiplied() {
+ mpremultiplied_ = false;
+ clear_has_mpremultiplied();
+}
+inline bool TexturePacket::mpremultiplied() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mPremultiplied)
+ return mpremultiplied_;
+}
+inline void TexturePacket::set_mpremultiplied(bool value) {
+ set_has_mpremultiplied();
+ mpremultiplied_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.mPremultiplied)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.Filter mFilter = 12;
+inline bool TexturePacket::has_mfilter() const {
+ return (_has_bits_[0] & 0x00000800u) != 0;
+}
+inline void TexturePacket::set_has_mfilter() {
+ _has_bits_[0] |= 0x00000800u;
+}
+inline void TexturePacket::clear_has_mfilter() {
+ _has_bits_[0] &= ~0x00000800u;
+}
+inline void TexturePacket::clear_mfilter() {
+ mfilter_ = 0;
+ clear_has_mfilter();
+}
+inline ::mozilla::layers::layerscope::TexturePacket_Filter TexturePacket::mfilter() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mFilter)
+ return static_cast< ::mozilla::layers::layerscope::TexturePacket_Filter >(mfilter_);
+}
+inline void TexturePacket::set_mfilter(::mozilla::layers::layerscope::TexturePacket_Filter value) {
+ assert(::mozilla::layers::layerscope::TexturePacket_Filter_IsValid(value));
+ set_has_mfilter();
+ mfilter_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.mFilter)
+}
+
+// optional bool isMask = 20;
+inline bool TexturePacket::has_ismask() const {
+ return (_has_bits_[0] & 0x00001000u) != 0;
+}
+inline void TexturePacket::set_has_ismask() {
+ _has_bits_[0] |= 0x00001000u;
+}
+inline void TexturePacket::clear_has_ismask() {
+ _has_bits_[0] &= ~0x00001000u;
+}
+inline void TexturePacket::clear_ismask() {
+ ismask_ = false;
+ clear_has_ismask();
+}
+inline bool TexturePacket::ismask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.isMask)
+ return ismask_;
+}
+inline void TexturePacket::set_ismask(bool value) {
+ set_has_ismask();
+ ismask_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.TexturePacket.isMask)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket.EffectMask mask = 21;
+inline bool TexturePacket::has_mask() const {
+ return (_has_bits_[0] & 0x00002000u) != 0;
+}
+inline void TexturePacket::set_has_mask() {
+ _has_bits_[0] |= 0x00002000u;
+}
+inline void TexturePacket::clear_has_mask() {
+ _has_bits_[0] &= ~0x00002000u;
+}
+inline void TexturePacket::clear_mask() {
+ if (mask_ != NULL) mask_->::mozilla::layers::layerscope::TexturePacket_EffectMask::Clear();
+ clear_has_mask();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket_EffectMask& TexturePacket::mask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.TexturePacket.mask)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return mask_ != NULL ? *mask_ : *default_instance().mask_;
+#else
+ return mask_ != NULL ? *mask_ : *default_instance_->mask_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* TexturePacket::mutable_mask() {
+ set_has_mask();
+ if (mask_ == NULL) mask_ = new ::mozilla::layers::layerscope::TexturePacket_EffectMask;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.TexturePacket.mask)
+ return mask_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket_EffectMask* TexturePacket::release_mask() {
+ clear_has_mask();
+ ::mozilla::layers::layerscope::TexturePacket_EffectMask* temp = mask_;
+ mask_ = NULL;
+ return temp;
+}
+inline void TexturePacket::set_allocated_mask(::mozilla::layers::layerscope::TexturePacket_EffectMask* mask) {
+ delete mask_;
+ mask_ = mask;
+ if (mask) {
+ set_has_mask();
+ } else {
+ clear_has_mask();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.TexturePacket.mask)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Size
+
+// optional int32 w = 1;
+inline bool LayersPacket_Layer_Size::has_w() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Size::set_has_w() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Size::clear_has_w() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Size::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Size::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Size.w)
+ return w_;
+}
+inline void LayersPacket_Layer_Size::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Size.w)
+}
+
+// optional int32 h = 2;
+inline bool LayersPacket_Layer_Size::has_h() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Size::set_has_h() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Size::clear_has_h() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Size::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Size::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Size.h)
+ return h_;
+}
+inline void LayersPacket_Layer_Size::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Size.h)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Rect
+
+// optional int32 x = 1;
+inline bool LayersPacket_Layer_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.x)
+ return x_;
+}
+inline void LayersPacket_Layer_Rect::set_x(::google::protobuf::int32 value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.x)
+}
+
+// optional int32 y = 2;
+inline bool LayersPacket_Layer_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.y)
+ return y_;
+}
+inline void LayersPacket_Layer_Rect::set_y(::google::protobuf::int32 value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.y)
+}
+
+// optional int32 w = 3;
+inline bool LayersPacket_Layer_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.w)
+ return w_;
+}
+inline void LayersPacket_Layer_Rect::set_w(::google::protobuf::int32 value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.w)
+}
+
+// optional int32 h = 4;
+inline bool LayersPacket_Layer_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void LayersPacket_Layer_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void LayersPacket_Layer_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void LayersPacket_Layer_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline ::google::protobuf::int32 LayersPacket_Layer_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Rect.h)
+ return h_;
+}
+inline void LayersPacket_Layer_Rect::set_h(::google::protobuf::int32 value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Region
+
+// repeated .mozilla.layers.layerscope.LayersPacket.Layer.Rect r = 1;
+inline int LayersPacket_Layer_Region::r_size() const {
+ return r_.size();
+}
+inline void LayersPacket_Layer_Region::clear_r() {
+ r_.Clear();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer_Region::r(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Get(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Region::mutable_r(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Region::add_r() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >&
+LayersPacket_Layer_Region::r() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return r_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer_Rect >*
+LayersPacket_Layer_Region::mutable_r() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.Layer.Region.r)
+ return &r_;
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Matrix
+
+// optional bool is2D = 1;
+inline bool LayersPacket_Layer_Matrix::has_is2d() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Matrix::set_has_is2d() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Matrix::clear_has_is2d() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Matrix::clear_is2d() {
+ is2d_ = false;
+ clear_has_is2d();
+}
+inline bool LayersPacket_Layer_Matrix::is2d() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.is2D)
+ return is2d_;
+}
+inline void LayersPacket_Layer_Matrix::set_is2d(bool value) {
+ set_has_is2d();
+ is2d_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.is2D)
+}
+
+// optional bool isId = 2;
+inline bool LayersPacket_Layer_Matrix::has_isid() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Matrix::set_has_isid() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Matrix::clear_has_isid() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Matrix::clear_isid() {
+ isid_ = false;
+ clear_has_isid();
+}
+inline bool LayersPacket_Layer_Matrix::isid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.isId)
+ return isid_;
+}
+inline void LayersPacket_Layer_Matrix::set_isid(bool value) {
+ set_has_isid();
+ isid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.isId)
+}
+
+// repeated float m = 3;
+inline int LayersPacket_Layer_Matrix::m_size() const {
+ return m_.size();
+}
+inline void LayersPacket_Layer_Matrix::clear_m() {
+ m_.Clear();
+}
+inline float LayersPacket_Layer_Matrix::m(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return m_.Get(index);
+}
+inline void LayersPacket_Layer_Matrix::set_m(int index, float value) {
+ m_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+}
+inline void LayersPacket_Layer_Matrix::add_m(float value) {
+ m_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+LayersPacket_Layer_Matrix::m() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return m_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+LayersPacket_Layer_Matrix::mutable_m() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.Layer.Matrix.m)
+ return &m_;
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer_Shadow
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 1;
+inline bool LayersPacket_Layer_Shadow::has_clip() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_clip() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_clip() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer_Shadow::clear_clip() {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ clear_has_clip();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer_Shadow::clip() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return clip_ != NULL ? *clip_ : *default_instance().clip_;
+#else
+ return clip_ != NULL ? *clip_ : *default_instance_->clip_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Shadow::mutable_clip() {
+ set_has_clip();
+ if (clip_ == NULL) clip_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+ return clip_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer_Shadow::release_clip() {
+ clear_has_clip();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* temp = clip_;
+ clip_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip) {
+ delete clip_;
+ clip_ = clip;
+ if (clip) {
+ set_has_clip();
+ } else {
+ clear_has_clip();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.clip)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 2;
+inline bool LayersPacket_Layer_Shadow::has_transform() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_transform() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_transform() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer_Shadow::clear_transform() {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ clear_has_transform();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& LayersPacket_Layer_Shadow::transform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return transform_ != NULL ? *transform_ : *default_instance().transform_;
+#else
+ return transform_ != NULL ? *transform_ : *default_instance_->transform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer_Shadow::mutable_transform() {
+ set_has_transform();
+ if (transform_ == NULL) transform_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+ return transform_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer_Shadow::release_transform() {
+ clear_has_transform();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* temp = transform_;
+ transform_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform) {
+ delete transform_;
+ transform_ = transform;
+ if (transform) {
+ set_has_transform();
+ } else {
+ clear_has_transform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.transform)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 3;
+inline bool LayersPacket_Layer_Shadow::has_vregion() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer_Shadow::set_has_vregion() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer_Shadow::clear_has_vregion() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer_Shadow::clear_vregion() {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer_Shadow::vregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vregion_ != NULL ? *vregion_ : *default_instance().vregion_;
+#else
+ return vregion_ != NULL ? *vregion_ : *default_instance_->vregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer_Shadow::mutable_vregion() {
+ set_has_vregion();
+ if (vregion_ == NULL) vregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+ return vregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer_Shadow::release_vregion() {
+ clear_has_vregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vregion_;
+ vregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer_Shadow::set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion) {
+ delete vregion_;
+ vregion_ = vregion;
+ if (vregion) {
+ set_has_vregion();
+ } else {
+ clear_has_vregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.Shadow.vRegion)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket_Layer
+
+// required .mozilla.layers.layerscope.LayersPacket.Layer.LayerType type = 1;
+inline bool LayersPacket_Layer::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void LayersPacket_Layer::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void LayersPacket_Layer::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void LayersPacket_Layer::clear_type() {
+ type_ = 0;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType LayersPacket_Layer::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.type)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_LayerType >(type_);
+}
+inline void LayersPacket_Layer::set_type(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_LayerType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.type)
+}
+
+// required uint64 ptr = 2;
+inline bool LayersPacket_Layer::has_ptr() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void LayersPacket_Layer::set_has_ptr() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void LayersPacket_Layer::clear_has_ptr() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void LayersPacket_Layer::clear_ptr() {
+ ptr_ = GOOGLE_ULONGLONG(0);
+ clear_has_ptr();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::ptr() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.ptr)
+ return ptr_;
+}
+inline void LayersPacket_Layer::set_ptr(::google::protobuf::uint64 value) {
+ set_has_ptr();
+ ptr_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.ptr)
+}
+
+// required uint64 parentPtr = 3;
+inline bool LayersPacket_Layer::has_parentptr() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void LayersPacket_Layer::set_has_parentptr() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void LayersPacket_Layer::clear_has_parentptr() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void LayersPacket_Layer::clear_parentptr() {
+ parentptr_ = GOOGLE_ULONGLONG(0);
+ clear_has_parentptr();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::parentptr() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.parentPtr)
+ return parentptr_;
+}
+inline void LayersPacket_Layer::set_parentptr(::google::protobuf::uint64 value) {
+ set_has_parentptr();
+ parentptr_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.parentPtr)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Rect clip = 10;
+inline bool LayersPacket_Layer::has_clip() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void LayersPacket_Layer::set_has_clip() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void LayersPacket_Layer::clear_has_clip() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void LayersPacket_Layer::clear_clip() {
+ if (clip_ != NULL) clip_->::mozilla::layers::layerscope::LayersPacket_Layer_Rect::Clear();
+ clear_has_clip();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Rect& LayersPacket_Layer::clip() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return clip_ != NULL ? *clip_ : *default_instance().clip_;
+#else
+ return clip_ != NULL ? *clip_ : *default_instance_->clip_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer::mutable_clip() {
+ set_has_clip();
+ if (clip_ == NULL) clip_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Rect;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+ return clip_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* LayersPacket_Layer::release_clip() {
+ clear_has_clip();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Rect* temp = clip_;
+ clip_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_clip(::mozilla::layers::layerscope::LayersPacket_Layer_Rect* clip) {
+ delete clip_;
+ clip_ = clip;
+ if (clip) {
+ set_has_clip();
+ } else {
+ clear_has_clip();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.clip)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Matrix transform = 11;
+inline bool LayersPacket_Layer::has_transform() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void LayersPacket_Layer::set_has_transform() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void LayersPacket_Layer::clear_has_transform() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void LayersPacket_Layer::clear_transform() {
+ if (transform_ != NULL) transform_->::mozilla::layers::layerscope::LayersPacket_Layer_Matrix::Clear();
+ clear_has_transform();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix& LayersPacket_Layer::transform() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return transform_ != NULL ? *transform_ : *default_instance().transform_;
+#else
+ return transform_ != NULL ? *transform_ : *default_instance_->transform_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer::mutable_transform() {
+ set_has_transform();
+ if (transform_ == NULL) transform_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+ return transform_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* LayersPacket_Layer::release_transform() {
+ clear_has_transform();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* temp = transform_;
+ transform_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_transform(::mozilla::layers::layerscope::LayersPacket_Layer_Matrix* transform) {
+ delete transform_;
+ transform_ = transform;
+ if (transform) {
+ set_has_transform();
+ } else {
+ clear_has_transform();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.transform)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vRegion = 12;
+inline bool LayersPacket_Layer::has_vregion() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void LayersPacket_Layer::set_has_vregion() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void LayersPacket_Layer::clear_has_vregion() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void LayersPacket_Layer::clear_vregion() {
+ if (vregion_ != NULL) vregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::vregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vregion_ != NULL ? *vregion_ : *default_instance().vregion_;
+#else
+ return vregion_ != NULL ? *vregion_ : *default_instance_->vregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_vregion() {
+ set_has_vregion();
+ if (vregion_ == NULL) vregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+ return vregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_vregion() {
+ clear_has_vregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vregion_;
+ vregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_vregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vregion) {
+ delete vregion_;
+ vregion_ = vregion;
+ if (vregion) {
+ set_has_vregion();
+ } else {
+ clear_has_vregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.vRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Shadow shadow = 13;
+inline bool LayersPacket_Layer::has_shadow() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void LayersPacket_Layer::set_has_shadow() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void LayersPacket_Layer::clear_has_shadow() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void LayersPacket_Layer::clear_shadow() {
+ if (shadow_ != NULL) shadow_->::mozilla::layers::layerscope::LayersPacket_Layer_Shadow::Clear();
+ clear_has_shadow();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow& LayersPacket_Layer::shadow() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return shadow_ != NULL ? *shadow_ : *default_instance().shadow_;
+#else
+ return shadow_ != NULL ? *shadow_ : *default_instance_->shadow_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* LayersPacket_Layer::mutable_shadow() {
+ set_has_shadow();
+ if (shadow_ == NULL) shadow_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+ return shadow_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* LayersPacket_Layer::release_shadow() {
+ clear_has_shadow();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* temp = shadow_;
+ shadow_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_shadow(::mozilla::layers::layerscope::LayersPacket_Layer_Shadow* shadow) {
+ delete shadow_;
+ shadow_ = shadow;
+ if (shadow) {
+ set_has_shadow();
+ } else {
+ clear_has_shadow();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.shadow)
+}
+
+// optional float opacity = 14;
+inline bool LayersPacket_Layer::has_opacity() const {
+ return (_has_bits_[0] & 0x00000080u) != 0;
+}
+inline void LayersPacket_Layer::set_has_opacity() {
+ _has_bits_[0] |= 0x00000080u;
+}
+inline void LayersPacket_Layer::clear_has_opacity() {
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline void LayersPacket_Layer::clear_opacity() {
+ opacity_ = 0;
+ clear_has_opacity();
+}
+inline float LayersPacket_Layer::opacity() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.opacity)
+ return opacity_;
+}
+inline void LayersPacket_Layer::set_opacity(float value) {
+ set_has_opacity();
+ opacity_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.opacity)
+}
+
+// optional bool cOpaque = 15;
+inline bool LayersPacket_Layer::has_copaque() const {
+ return (_has_bits_[0] & 0x00000100u) != 0;
+}
+inline void LayersPacket_Layer::set_has_copaque() {
+ _has_bits_[0] |= 0x00000100u;
+}
+inline void LayersPacket_Layer::clear_has_copaque() {
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline void LayersPacket_Layer::clear_copaque() {
+ copaque_ = false;
+ clear_has_copaque();
+}
+inline bool LayersPacket_Layer::copaque() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.cOpaque)
+ return copaque_;
+}
+inline void LayersPacket_Layer::set_copaque(bool value) {
+ set_has_copaque();
+ copaque_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.cOpaque)
+}
+
+// optional bool cAlpha = 16;
+inline bool LayersPacket_Layer::has_calpha() const {
+ return (_has_bits_[0] & 0x00000200u) != 0;
+}
+inline void LayersPacket_Layer::set_has_calpha() {
+ _has_bits_[0] |= 0x00000200u;
+}
+inline void LayersPacket_Layer::clear_has_calpha() {
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline void LayersPacket_Layer::clear_calpha() {
+ calpha_ = false;
+ clear_has_calpha();
+}
+inline bool LayersPacket_Layer::calpha() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.cAlpha)
+ return calpha_;
+}
+inline void LayersPacket_Layer::set_calpha(bool value) {
+ set_has_calpha();
+ calpha_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.cAlpha)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.ScrollingDirect direct = 17;
+inline bool LayersPacket_Layer::has_direct() const {
+ return (_has_bits_[0] & 0x00000400u) != 0;
+}
+inline void LayersPacket_Layer::set_has_direct() {
+ _has_bits_[0] |= 0x00000400u;
+}
+inline void LayersPacket_Layer::clear_has_direct() {
+ _has_bits_[0] &= ~0x00000400u;
+}
+inline void LayersPacket_Layer::clear_direct() {
+ direct_ = 1;
+ clear_has_direct();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect LayersPacket_Layer::direct() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.direct)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect >(direct_);
+}
+inline void LayersPacket_Layer::set_direct(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_ScrollingDirect_IsValid(value));
+ set_has_direct();
+ direct_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.direct)
+}
+
+// optional uint64 barID = 18;
+inline bool LayersPacket_Layer::has_barid() const {
+ return (_has_bits_[0] & 0x00000800u) != 0;
+}
+inline void LayersPacket_Layer::set_has_barid() {
+ _has_bits_[0] |= 0x00000800u;
+}
+inline void LayersPacket_Layer::clear_has_barid() {
+ _has_bits_[0] &= ~0x00000800u;
+}
+inline void LayersPacket_Layer::clear_barid() {
+ barid_ = GOOGLE_ULONGLONG(0);
+ clear_has_barid();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::barid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.barID)
+ return barid_;
+}
+inline void LayersPacket_Layer::set_barid(::google::protobuf::uint64 value) {
+ set_has_barid();
+ barid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.barID)
+}
+
+// optional uint64 mask = 19;
+inline bool LayersPacket_Layer::has_mask() const {
+ return (_has_bits_[0] & 0x00001000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_mask() {
+ _has_bits_[0] |= 0x00001000u;
+}
+inline void LayersPacket_Layer::clear_has_mask() {
+ _has_bits_[0] &= ~0x00001000u;
+}
+inline void LayersPacket_Layer::clear_mask() {
+ mask_ = GOOGLE_ULONGLONG(0);
+ clear_has_mask();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::mask() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.mask)
+ return mask_;
+}
+inline void LayersPacket_Layer::set_mask(::google::protobuf::uint64 value) {
+ set_has_mask();
+ mask_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.mask)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hitRegion = 20;
+inline bool LayersPacket_Layer::has_hitregion() const {
+ return (_has_bits_[0] & 0x00002000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_hitregion() {
+ _has_bits_[0] |= 0x00002000u;
+}
+inline void LayersPacket_Layer::clear_has_hitregion() {
+ _has_bits_[0] &= ~0x00002000u;
+}
+inline void LayersPacket_Layer::clear_hitregion() {
+ if (hitregion_ != NULL) hitregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_hitregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::hitregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return hitregion_ != NULL ? *hitregion_ : *default_instance().hitregion_;
+#else
+ return hitregion_ != NULL ? *hitregion_ : *default_instance_->hitregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_hitregion() {
+ set_has_hitregion();
+ if (hitregion_ == NULL) hitregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+ return hitregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_hitregion() {
+ clear_has_hitregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = hitregion_;
+ hitregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_hitregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hitregion) {
+ delete hitregion_;
+ hitregion_ = hitregion;
+ if (hitregion) {
+ set_has_hitregion();
+ } else {
+ clear_has_hitregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.hitRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region dispatchRegion = 21;
+inline bool LayersPacket_Layer::has_dispatchregion() const {
+ return (_has_bits_[0] & 0x00004000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_dispatchregion() {
+ _has_bits_[0] |= 0x00004000u;
+}
+inline void LayersPacket_Layer::clear_has_dispatchregion() {
+ _has_bits_[0] &= ~0x00004000u;
+}
+inline void LayersPacket_Layer::clear_dispatchregion() {
+ if (dispatchregion_ != NULL) dispatchregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_dispatchregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::dispatchregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return dispatchregion_ != NULL ? *dispatchregion_ : *default_instance().dispatchregion_;
+#else
+ return dispatchregion_ != NULL ? *dispatchregion_ : *default_instance_->dispatchregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_dispatchregion() {
+ set_has_dispatchregion();
+ if (dispatchregion_ == NULL) dispatchregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+ return dispatchregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_dispatchregion() {
+ clear_has_dispatchregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = dispatchregion_;
+ dispatchregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_dispatchregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* dispatchregion) {
+ delete dispatchregion_;
+ dispatchregion_ = dispatchregion;
+ if (dispatchregion) {
+ set_has_dispatchregion();
+ } else {
+ clear_has_dispatchregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.dispatchRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region noActionRegion = 22;
+inline bool LayersPacket_Layer::has_noactionregion() const {
+ return (_has_bits_[0] & 0x00008000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_noactionregion() {
+ _has_bits_[0] |= 0x00008000u;
+}
+inline void LayersPacket_Layer::clear_has_noactionregion() {
+ _has_bits_[0] &= ~0x00008000u;
+}
+inline void LayersPacket_Layer::clear_noactionregion() {
+ if (noactionregion_ != NULL) noactionregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_noactionregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::noactionregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return noactionregion_ != NULL ? *noactionregion_ : *default_instance().noactionregion_;
+#else
+ return noactionregion_ != NULL ? *noactionregion_ : *default_instance_->noactionregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_noactionregion() {
+ set_has_noactionregion();
+ if (noactionregion_ == NULL) noactionregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+ return noactionregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_noactionregion() {
+ clear_has_noactionregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = noactionregion_;
+ noactionregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_noactionregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* noactionregion) {
+ delete noactionregion_;
+ noactionregion_ = noactionregion;
+ if (noactionregion) {
+ set_has_noactionregion();
+ } else {
+ clear_has_noactionregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.noActionRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region hPanRegion = 23;
+inline bool LayersPacket_Layer::has_hpanregion() const {
+ return (_has_bits_[0] & 0x00010000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_hpanregion() {
+ _has_bits_[0] |= 0x00010000u;
+}
+inline void LayersPacket_Layer::clear_has_hpanregion() {
+ _has_bits_[0] &= ~0x00010000u;
+}
+inline void LayersPacket_Layer::clear_hpanregion() {
+ if (hpanregion_ != NULL) hpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_hpanregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::hpanregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return hpanregion_ != NULL ? *hpanregion_ : *default_instance().hpanregion_;
+#else
+ return hpanregion_ != NULL ? *hpanregion_ : *default_instance_->hpanregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_hpanregion() {
+ set_has_hpanregion();
+ if (hpanregion_ == NULL) hpanregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+ return hpanregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_hpanregion() {
+ clear_has_hpanregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = hpanregion_;
+ hpanregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_hpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* hpanregion) {
+ delete hpanregion_;
+ hpanregion_ = hpanregion;
+ if (hpanregion) {
+ set_has_hpanregion();
+ } else {
+ clear_has_hpanregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.hPanRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region vPanRegion = 24;
+inline bool LayersPacket_Layer::has_vpanregion() const {
+ return (_has_bits_[0] & 0x00020000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_vpanregion() {
+ _has_bits_[0] |= 0x00020000u;
+}
+inline void LayersPacket_Layer::clear_has_vpanregion() {
+ _has_bits_[0] &= ~0x00020000u;
+}
+inline void LayersPacket_Layer::clear_vpanregion() {
+ if (vpanregion_ != NULL) vpanregion_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_vpanregion();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::vpanregion() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return vpanregion_ != NULL ? *vpanregion_ : *default_instance().vpanregion_;
+#else
+ return vpanregion_ != NULL ? *vpanregion_ : *default_instance_->vpanregion_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_vpanregion() {
+ set_has_vpanregion();
+ if (vpanregion_ == NULL) vpanregion_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+ return vpanregion_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_vpanregion() {
+ clear_has_vpanregion();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = vpanregion_;
+ vpanregion_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_vpanregion(::mozilla::layers::layerscope::LayersPacket_Layer_Region* vpanregion) {
+ delete vpanregion_;
+ vpanregion_ = vpanregion;
+ if (vpanregion) {
+ set_has_vpanregion();
+ } else {
+ clear_has_vpanregion();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.vPanRegion)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Region valid = 100;
+inline bool LayersPacket_Layer::has_valid() const {
+ return (_has_bits_[0] & 0x00040000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_valid() {
+ _has_bits_[0] |= 0x00040000u;
+}
+inline void LayersPacket_Layer::clear_has_valid() {
+ _has_bits_[0] &= ~0x00040000u;
+}
+inline void LayersPacket_Layer::clear_valid() {
+ if (valid_ != NULL) valid_->::mozilla::layers::layerscope::LayersPacket_Layer_Region::Clear();
+ clear_has_valid();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Region& LayersPacket_Layer::valid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return valid_ != NULL ? *valid_ : *default_instance().valid_;
+#else
+ return valid_ != NULL ? *valid_ : *default_instance_->valid_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::mutable_valid() {
+ set_has_valid();
+ if (valid_ == NULL) valid_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Region;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+ return valid_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Region* LayersPacket_Layer::release_valid() {
+ clear_has_valid();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Region* temp = valid_;
+ valid_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_valid(::mozilla::layers::layerscope::LayersPacket_Layer_Region* valid) {
+ delete valid_;
+ valid_ = valid;
+ if (valid) {
+ set_has_valid();
+ } else {
+ clear_has_valid();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.valid)
+}
+
+// optional uint32 color = 101;
+inline bool LayersPacket_Layer::has_color() const {
+ return (_has_bits_[0] & 0x00080000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_color() {
+ _has_bits_[0] |= 0x00080000u;
+}
+inline void LayersPacket_Layer::clear_has_color() {
+ _has_bits_[0] &= ~0x00080000u;
+}
+inline void LayersPacket_Layer::clear_color() {
+ color_ = 0u;
+ clear_has_color();
+}
+inline ::google::protobuf::uint32 LayersPacket_Layer::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.color)
+ return color_;
+}
+inline void LayersPacket_Layer::set_color(::google::protobuf::uint32 value) {
+ set_has_color();
+ color_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.color)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Filter filter = 102;
+inline bool LayersPacket_Layer::has_filter() const {
+ return (_has_bits_[0] & 0x00100000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_filter() {
+ _has_bits_[0] |= 0x00100000u;
+}
+inline void LayersPacket_Layer::clear_has_filter() {
+ _has_bits_[0] &= ~0x00100000u;
+}
+inline void LayersPacket_Layer::clear_filter() {
+ filter_ = 0;
+ clear_has_filter();
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Filter LayersPacket_Layer::filter() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.filter)
+ return static_cast< ::mozilla::layers::layerscope::LayersPacket_Layer_Filter >(filter_);
+}
+inline void LayersPacket_Layer::set_filter(::mozilla::layers::layerscope::LayersPacket_Layer_Filter value) {
+ assert(::mozilla::layers::layerscope::LayersPacket_Layer_Filter_IsValid(value));
+ set_has_filter();
+ filter_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.filter)
+}
+
+// optional uint64 refID = 103;
+inline bool LayersPacket_Layer::has_refid() const {
+ return (_has_bits_[0] & 0x00200000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_refid() {
+ _has_bits_[0] |= 0x00200000u;
+}
+inline void LayersPacket_Layer::clear_has_refid() {
+ _has_bits_[0] &= ~0x00200000u;
+}
+inline void LayersPacket_Layer::clear_refid() {
+ refid_ = GOOGLE_ULONGLONG(0);
+ clear_has_refid();
+}
+inline ::google::protobuf::uint64 LayersPacket_Layer::refid() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.refID)
+ return refid_;
+}
+inline void LayersPacket_Layer::set_refid(::google::protobuf::uint64 value) {
+ set_has_refid();
+ refid_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.refID)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket.Layer.Size size = 104;
+inline bool LayersPacket_Layer::has_size() const {
+ return (_has_bits_[0] & 0x00400000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_size() {
+ _has_bits_[0] |= 0x00400000u;
+}
+inline void LayersPacket_Layer::clear_has_size() {
+ _has_bits_[0] &= ~0x00400000u;
+}
+inline void LayersPacket_Layer::clear_size() {
+ if (size_ != NULL) size_->::mozilla::layers::layerscope::LayersPacket_Layer_Size::Clear();
+ clear_has_size();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer_Size& LayersPacket_Layer::size() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.size)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return size_ != NULL ? *size_ : *default_instance().size_;
+#else
+ return size_ != NULL ? *size_ : *default_instance_->size_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* LayersPacket_Layer::mutable_size() {
+ set_has_size();
+ if (size_ == NULL) size_ = new ::mozilla::layers::layerscope::LayersPacket_Layer_Size;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.size)
+ return size_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer_Size* LayersPacket_Layer::release_size() {
+ clear_has_size();
+ ::mozilla::layers::layerscope::LayersPacket_Layer_Size* temp = size_;
+ size_ = NULL;
+ return temp;
+}
+inline void LayersPacket_Layer::set_allocated_size(::mozilla::layers::layerscope::LayersPacket_Layer_Size* size) {
+ delete size_;
+ size_ = size;
+ if (size) {
+ set_has_size();
+ } else {
+ clear_has_size();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.size)
+}
+
+// optional uint32 displayListLogLength = 105;
+inline bool LayersPacket_Layer::has_displaylistloglength() const {
+ return (_has_bits_[0] & 0x00800000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_displaylistloglength() {
+ _has_bits_[0] |= 0x00800000u;
+}
+inline void LayersPacket_Layer::clear_has_displaylistloglength() {
+ _has_bits_[0] &= ~0x00800000u;
+}
+inline void LayersPacket_Layer::clear_displaylistloglength() {
+ displaylistloglength_ = 0u;
+ clear_has_displaylistloglength();
+}
+inline ::google::protobuf::uint32 LayersPacket_Layer::displaylistloglength() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.displayListLogLength)
+ return displaylistloglength_;
+}
+inline void LayersPacket_Layer::set_displaylistloglength(::google::protobuf::uint32 value) {
+ set_has_displaylistloglength();
+ displaylistloglength_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.displayListLogLength)
+}
+
+// optional bytes displayListLog = 106;
+inline bool LayersPacket_Layer::has_displaylistlog() const {
+ return (_has_bits_[0] & 0x01000000u) != 0;
+}
+inline void LayersPacket_Layer::set_has_displaylistlog() {
+ _has_bits_[0] |= 0x01000000u;
+}
+inline void LayersPacket_Layer::clear_has_displaylistlog() {
+ _has_bits_[0] &= ~0x01000000u;
+}
+inline void LayersPacket_Layer::clear_displaylistlog() {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_->clear();
+ }
+ clear_has_displaylistlog();
+}
+inline const ::std::string& LayersPacket_Layer::displaylistlog() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+ return *displaylistlog_;
+}
+inline void LayersPacket_Layer::set_displaylistlog(const ::std::string& value) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline void LayersPacket_Layer::set_displaylistlog(const char* value) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(value);
+ // @@protoc_insertion_point(field_set_char:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline void LayersPacket_Layer::set_displaylistlog(const void* value, size_t size) {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ displaylistlog_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+inline ::std::string* LayersPacket_Layer::mutable_displaylistlog() {
+ set_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ displaylistlog_ = new ::std::string;
+ }
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+ return displaylistlog_;
+}
+inline ::std::string* LayersPacket_Layer::release_displaylistlog() {
+ clear_has_displaylistlog();
+ if (displaylistlog_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = displaylistlog_;
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void LayersPacket_Layer::set_allocated_displaylistlog(::std::string* displaylistlog) {
+ if (displaylistlog_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete displaylistlog_;
+ }
+ if (displaylistlog) {
+ set_has_displaylistlog();
+ displaylistlog_ = displaylistlog;
+ } else {
+ clear_has_displaylistlog();
+ displaylistlog_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.LayersPacket.Layer.displayListLog)
+}
+
+// -------------------------------------------------------------------
+
+// LayersPacket
+
+// repeated .mozilla.layers.layerscope.LayersPacket.Layer layer = 1;
+inline int LayersPacket::layer_size() const {
+ return layer_.size();
+}
+inline void LayersPacket::clear_layer() {
+ layer_.Clear();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket_Layer& LayersPacket::layer(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Get(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer* LayersPacket::mutable_layer(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::LayersPacket_Layer* LayersPacket::add_layer() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >&
+LayersPacket::layer() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.LayersPacket.layer)
+ return layer_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::LayersPacket_Layer >*
+LayersPacket::mutable_layer() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.LayersPacket.layer)
+ return &layer_;
+}
+
+// -------------------------------------------------------------------
+
+// MetaPacket
+
+// optional bool composedByHwc = 1;
+inline bool MetaPacket::has_composedbyhwc() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void MetaPacket::set_has_composedbyhwc() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void MetaPacket::clear_has_composedbyhwc() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void MetaPacket::clear_composedbyhwc() {
+ composedbyhwc_ = false;
+ clear_has_composedbyhwc();
+}
+inline bool MetaPacket::composedbyhwc() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.MetaPacket.composedByHwc)
+ return composedbyhwc_;
+}
+inline void MetaPacket::set_composedbyhwc(bool value) {
+ set_has_composedbyhwc();
+ composedbyhwc_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.MetaPacket.composedByHwc)
+}
+
+// -------------------------------------------------------------------
+
+// DrawPacket_Rect
+
+// required float x = 1;
+inline bool DrawPacket_Rect::has_x() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void DrawPacket_Rect::set_has_x() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void DrawPacket_Rect::clear_has_x() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void DrawPacket_Rect::clear_x() {
+ x_ = 0;
+ clear_has_x();
+}
+inline float DrawPacket_Rect::x() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.x)
+ return x_;
+}
+inline void DrawPacket_Rect::set_x(float value) {
+ set_has_x();
+ x_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.x)
+}
+
+// required float y = 2;
+inline bool DrawPacket_Rect::has_y() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void DrawPacket_Rect::set_has_y() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void DrawPacket_Rect::clear_has_y() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void DrawPacket_Rect::clear_y() {
+ y_ = 0;
+ clear_has_y();
+}
+inline float DrawPacket_Rect::y() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.y)
+ return y_;
+}
+inline void DrawPacket_Rect::set_y(float value) {
+ set_has_y();
+ y_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.y)
+}
+
+// required float w = 3;
+inline bool DrawPacket_Rect::has_w() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void DrawPacket_Rect::set_has_w() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void DrawPacket_Rect::clear_has_w() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void DrawPacket_Rect::clear_w() {
+ w_ = 0;
+ clear_has_w();
+}
+inline float DrawPacket_Rect::w() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.w)
+ return w_;
+}
+inline void DrawPacket_Rect::set_w(float value) {
+ set_has_w();
+ w_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.w)
+}
+
+// required float h = 4;
+inline bool DrawPacket_Rect::has_h() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void DrawPacket_Rect::set_has_h() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void DrawPacket_Rect::clear_has_h() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void DrawPacket_Rect::clear_h() {
+ h_ = 0;
+ clear_has_h();
+}
+inline float DrawPacket_Rect::h() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.Rect.h)
+ return h_;
+}
+inline void DrawPacket_Rect::set_h(float value) {
+ set_has_h();
+ h_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.Rect.h)
+}
+
+// -------------------------------------------------------------------
+
+// DrawPacket
+
+// required float offsetX = 1;
+inline bool DrawPacket::has_offsetx() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void DrawPacket::set_has_offsetx() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void DrawPacket::clear_has_offsetx() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void DrawPacket::clear_offsetx() {
+ offsetx_ = 0;
+ clear_has_offsetx();
+}
+inline float DrawPacket::offsetx() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.offsetX)
+ return offsetx_;
+}
+inline void DrawPacket::set_offsetx(float value) {
+ set_has_offsetx();
+ offsetx_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.offsetX)
+}
+
+// required float offsetY = 2;
+inline bool DrawPacket::has_offsety() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void DrawPacket::set_has_offsety() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void DrawPacket::clear_has_offsety() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void DrawPacket::clear_offsety() {
+ offsety_ = 0;
+ clear_has_offsety();
+}
+inline float DrawPacket::offsety() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.offsetY)
+ return offsety_;
+}
+inline void DrawPacket::set_offsety(float value) {
+ set_has_offsety();
+ offsety_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.offsetY)
+}
+
+// repeated float mvMatrix = 3;
+inline int DrawPacket::mvmatrix_size() const {
+ return mvmatrix_.size();
+}
+inline void DrawPacket::clear_mvmatrix() {
+ mvmatrix_.Clear();
+}
+inline float DrawPacket::mvmatrix(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return mvmatrix_.Get(index);
+}
+inline void DrawPacket::set_mvmatrix(int index, float value) {
+ mvmatrix_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+}
+inline void DrawPacket::add_mvmatrix(float value) {
+ mvmatrix_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+}
+inline const ::google::protobuf::RepeatedField< float >&
+DrawPacket::mvmatrix() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return mvmatrix_;
+}
+inline ::google::protobuf::RepeatedField< float >*
+DrawPacket::mutable_mvmatrix() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.mvMatrix)
+ return &mvmatrix_;
+}
+
+// required uint32 totalRects = 4;
+inline bool DrawPacket::has_totalrects() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void DrawPacket::set_has_totalrects() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void DrawPacket::clear_has_totalrects() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void DrawPacket::clear_totalrects() {
+ totalrects_ = 0u;
+ clear_has_totalrects();
+}
+inline ::google::protobuf::uint32 DrawPacket::totalrects() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.totalRects)
+ return totalrects_;
+}
+inline void DrawPacket::set_totalrects(::google::protobuf::uint32 value) {
+ set_has_totalrects();
+ totalrects_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.totalRects)
+}
+
+// repeated .mozilla.layers.layerscope.DrawPacket.Rect layerRect = 5;
+inline int DrawPacket::layerrect_size() const {
+ return layerrect_.size();
+}
+inline void DrawPacket::clear_layerrect() {
+ layerrect_.Clear();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket_Rect& DrawPacket::layerrect(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Get(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::mutable_layerrect(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::add_layerrect() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+DrawPacket::layerrect() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return layerrect_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+DrawPacket::mutable_layerrect() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.layerRect)
+ return &layerrect_;
+}
+
+// required uint64 layerref = 6;
+inline bool DrawPacket::has_layerref() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void DrawPacket::set_has_layerref() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void DrawPacket::clear_has_layerref() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void DrawPacket::clear_layerref() {
+ layerref_ = GOOGLE_ULONGLONG(0);
+ clear_has_layerref();
+}
+inline ::google::protobuf::uint64 DrawPacket::layerref() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.layerref)
+ return layerref_;
+}
+inline void DrawPacket::set_layerref(::google::protobuf::uint64 value) {
+ set_has_layerref();
+ layerref_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.layerref)
+}
+
+// repeated uint32 texIDs = 7;
+inline int DrawPacket::texids_size() const {
+ return texids_.size();
+}
+inline void DrawPacket::clear_texids() {
+ texids_.Clear();
+}
+inline ::google::protobuf::uint32 DrawPacket::texids(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return texids_.Get(index);
+}
+inline void DrawPacket::set_texids(int index, ::google::protobuf::uint32 value) {
+ texids_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.DrawPacket.texIDs)
+}
+inline void DrawPacket::add_texids(::google::protobuf::uint32 value) {
+ texids_.Add(value);
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.texIDs)
+}
+inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+DrawPacket::texids() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return texids_;
+}
+inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+DrawPacket::mutable_texids() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.texIDs)
+ return &texids_;
+}
+
+// repeated .mozilla.layers.layerscope.DrawPacket.Rect textureRect = 8;
+inline int DrawPacket::texturerect_size() const {
+ return texturerect_.size();
+}
+inline void DrawPacket::clear_texturerect() {
+ texturerect_.Clear();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket_Rect& DrawPacket::texturerect(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Get(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::mutable_texturerect(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Mutable(index);
+}
+inline ::mozilla::layers::layerscope::DrawPacket_Rect* DrawPacket::add_texturerect() {
+ // @@protoc_insertion_point(field_add:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_.Add();
+}
+inline const ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >&
+DrawPacket::texturerect() const {
+ // @@protoc_insertion_point(field_list:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return texturerect_;
+}
+inline ::google::protobuf::RepeatedPtrField< ::mozilla::layers::layerscope::DrawPacket_Rect >*
+DrawPacket::mutable_texturerect() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.layers.layerscope.DrawPacket.textureRect)
+ return &texturerect_;
+}
+
+// -------------------------------------------------------------------
+
+// Packet
+
+// required .mozilla.layers.layerscope.Packet.DataType type = 1;
+inline bool Packet::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void Packet::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void Packet::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void Packet::clear_type() {
+ type_ = 1;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::Packet_DataType Packet::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.type)
+ return static_cast< ::mozilla::layers::layerscope::Packet_DataType >(type_);
+}
+inline void Packet::set_type(::mozilla::layers::layerscope::Packet_DataType value) {
+ assert(::mozilla::layers::layerscope::Packet_DataType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.Packet.type)
+}
+
+// optional .mozilla.layers.layerscope.FramePacket frame = 2;
+inline bool Packet::has_frame() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void Packet::set_has_frame() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void Packet::clear_has_frame() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void Packet::clear_frame() {
+ if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear();
+ clear_has_frame();
+}
+inline const ::mozilla::layers::layerscope::FramePacket& Packet::frame() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.frame)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return frame_ != NULL ? *frame_ : *default_instance().frame_;
+#else
+ return frame_ != NULL ? *frame_ : *default_instance_->frame_;
+#endif
+}
+inline ::mozilla::layers::layerscope::FramePacket* Packet::mutable_frame() {
+ set_has_frame();
+ if (frame_ == NULL) frame_ = new ::mozilla::layers::layerscope::FramePacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.frame)
+ return frame_;
+}
+inline ::mozilla::layers::layerscope::FramePacket* Packet::release_frame() {
+ clear_has_frame();
+ ::mozilla::layers::layerscope::FramePacket* temp = frame_;
+ frame_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_frame(::mozilla::layers::layerscope::FramePacket* frame) {
+ delete frame_;
+ frame_ = frame;
+ if (frame) {
+ set_has_frame();
+ } else {
+ clear_has_frame();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.frame)
+}
+
+// optional .mozilla.layers.layerscope.ColorPacket color = 3;
+inline bool Packet::has_color() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void Packet::set_has_color() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void Packet::clear_has_color() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void Packet::clear_color() {
+ if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear();
+ clear_has_color();
+}
+inline const ::mozilla::layers::layerscope::ColorPacket& Packet::color() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.color)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return color_ != NULL ? *color_ : *default_instance().color_;
+#else
+ return color_ != NULL ? *color_ : *default_instance_->color_;
+#endif
+}
+inline ::mozilla::layers::layerscope::ColorPacket* Packet::mutable_color() {
+ set_has_color();
+ if (color_ == NULL) color_ = new ::mozilla::layers::layerscope::ColorPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.color)
+ return color_;
+}
+inline ::mozilla::layers::layerscope::ColorPacket* Packet::release_color() {
+ clear_has_color();
+ ::mozilla::layers::layerscope::ColorPacket* temp = color_;
+ color_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_color(::mozilla::layers::layerscope::ColorPacket* color) {
+ delete color_;
+ color_ = color;
+ if (color) {
+ set_has_color();
+ } else {
+ clear_has_color();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.color)
+}
+
+// optional .mozilla.layers.layerscope.TexturePacket texture = 4;
+inline bool Packet::has_texture() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void Packet::set_has_texture() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void Packet::clear_has_texture() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void Packet::clear_texture() {
+ if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear();
+ clear_has_texture();
+}
+inline const ::mozilla::layers::layerscope::TexturePacket& Packet::texture() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.texture)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return texture_ != NULL ? *texture_ : *default_instance().texture_;
+#else
+ return texture_ != NULL ? *texture_ : *default_instance_->texture_;
+#endif
+}
+inline ::mozilla::layers::layerscope::TexturePacket* Packet::mutable_texture() {
+ set_has_texture();
+ if (texture_ == NULL) texture_ = new ::mozilla::layers::layerscope::TexturePacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.texture)
+ return texture_;
+}
+inline ::mozilla::layers::layerscope::TexturePacket* Packet::release_texture() {
+ clear_has_texture();
+ ::mozilla::layers::layerscope::TexturePacket* temp = texture_;
+ texture_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_texture(::mozilla::layers::layerscope::TexturePacket* texture) {
+ delete texture_;
+ texture_ = texture;
+ if (texture) {
+ set_has_texture();
+ } else {
+ clear_has_texture();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.texture)
+}
+
+// optional .mozilla.layers.layerscope.LayersPacket layers = 5;
+inline bool Packet::has_layers() const {
+ return (_has_bits_[0] & 0x00000010u) != 0;
+}
+inline void Packet::set_has_layers() {
+ _has_bits_[0] |= 0x00000010u;
+}
+inline void Packet::clear_has_layers() {
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline void Packet::clear_layers() {
+ if (layers_ != NULL) layers_->::mozilla::layers::layerscope::LayersPacket::Clear();
+ clear_has_layers();
+}
+inline const ::mozilla::layers::layerscope::LayersPacket& Packet::layers() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.layers)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return layers_ != NULL ? *layers_ : *default_instance().layers_;
+#else
+ return layers_ != NULL ? *layers_ : *default_instance_->layers_;
+#endif
+}
+inline ::mozilla::layers::layerscope::LayersPacket* Packet::mutable_layers() {
+ set_has_layers();
+ if (layers_ == NULL) layers_ = new ::mozilla::layers::layerscope::LayersPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.layers)
+ return layers_;
+}
+inline ::mozilla::layers::layerscope::LayersPacket* Packet::release_layers() {
+ clear_has_layers();
+ ::mozilla::layers::layerscope::LayersPacket* temp = layers_;
+ layers_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_layers(::mozilla::layers::layerscope::LayersPacket* layers) {
+ delete layers_;
+ layers_ = layers;
+ if (layers) {
+ set_has_layers();
+ } else {
+ clear_has_layers();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.layers)
+}
+
+// optional .mozilla.layers.layerscope.MetaPacket meta = 6;
+inline bool Packet::has_meta() const {
+ return (_has_bits_[0] & 0x00000020u) != 0;
+}
+inline void Packet::set_has_meta() {
+ _has_bits_[0] |= 0x00000020u;
+}
+inline void Packet::clear_has_meta() {
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline void Packet::clear_meta() {
+ if (meta_ != NULL) meta_->::mozilla::layers::layerscope::MetaPacket::Clear();
+ clear_has_meta();
+}
+inline const ::mozilla::layers::layerscope::MetaPacket& Packet::meta() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.meta)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return meta_ != NULL ? *meta_ : *default_instance().meta_;
+#else
+ return meta_ != NULL ? *meta_ : *default_instance_->meta_;
+#endif
+}
+inline ::mozilla::layers::layerscope::MetaPacket* Packet::mutable_meta() {
+ set_has_meta();
+ if (meta_ == NULL) meta_ = new ::mozilla::layers::layerscope::MetaPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.meta)
+ return meta_;
+}
+inline ::mozilla::layers::layerscope::MetaPacket* Packet::release_meta() {
+ clear_has_meta();
+ ::mozilla::layers::layerscope::MetaPacket* temp = meta_;
+ meta_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_meta(::mozilla::layers::layerscope::MetaPacket* meta) {
+ delete meta_;
+ meta_ = meta;
+ if (meta) {
+ set_has_meta();
+ } else {
+ clear_has_meta();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.meta)
+}
+
+// optional .mozilla.layers.layerscope.DrawPacket draw = 7;
+inline bool Packet::has_draw() const {
+ return (_has_bits_[0] & 0x00000040u) != 0;
+}
+inline void Packet::set_has_draw() {
+ _has_bits_[0] |= 0x00000040u;
+}
+inline void Packet::clear_has_draw() {
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline void Packet::clear_draw() {
+ if (draw_ != NULL) draw_->::mozilla::layers::layerscope::DrawPacket::Clear();
+ clear_has_draw();
+}
+inline const ::mozilla::layers::layerscope::DrawPacket& Packet::draw() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.Packet.draw)
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ return draw_ != NULL ? *draw_ : *default_instance().draw_;
+#else
+ return draw_ != NULL ? *draw_ : *default_instance_->draw_;
+#endif
+}
+inline ::mozilla::layers::layerscope::DrawPacket* Packet::mutable_draw() {
+ set_has_draw();
+ if (draw_ == NULL) draw_ = new ::mozilla::layers::layerscope::DrawPacket;
+ // @@protoc_insertion_point(field_mutable:mozilla.layers.layerscope.Packet.draw)
+ return draw_;
+}
+inline ::mozilla::layers::layerscope::DrawPacket* Packet::release_draw() {
+ clear_has_draw();
+ ::mozilla::layers::layerscope::DrawPacket* temp = draw_;
+ draw_ = NULL;
+ return temp;
+}
+inline void Packet::set_allocated_draw(::mozilla::layers::layerscope::DrawPacket* draw) {
+ delete draw_;
+ draw_ = draw;
+ if (draw) {
+ set_has_draw();
+ } else {
+ clear_has_draw();
+ }
+ // @@protoc_insertion_point(field_set_allocated:mozilla.layers.layerscope.Packet.draw)
+}
+
+// -------------------------------------------------------------------
+
+// CommandPacket
+
+// required .mozilla.layers.layerscope.CommandPacket.CmdType type = 1;
+inline bool CommandPacket::has_type() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void CommandPacket::set_has_type() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void CommandPacket::clear_has_type() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void CommandPacket::clear_type() {
+ type_ = 0;
+ clear_has_type();
+}
+inline ::mozilla::layers::layerscope::CommandPacket_CmdType CommandPacket::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.CommandPacket.type)
+ return static_cast< ::mozilla::layers::layerscope::CommandPacket_CmdType >(type_);
+}
+inline void CommandPacket::set_type(::mozilla::layers::layerscope::CommandPacket_CmdType value) {
+ assert(::mozilla::layers::layerscope::CommandPacket_CmdType_IsValid(value));
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.CommandPacket.type)
+}
+
+// optional bool value = 2;
+inline bool CommandPacket::has_value() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void CommandPacket::set_has_value() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void CommandPacket::clear_has_value() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void CommandPacket::clear_value() {
+ value_ = false;
+ clear_has_value();
+}
+inline bool CommandPacket::value() const {
+ // @@protoc_insertion_point(field_get:mozilla.layers.layerscope.CommandPacket.value)
+ return value_;
+}
+inline void CommandPacket::set_value(bool value) {
+ set_has_value();
+ value_ = value;
+ // @@protoc_insertion_point(field_set:mozilla.layers.layerscope.CommandPacket.value)
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace layerscope
+} // namespace layers
+} // namespace mozilla
+
+// @@protoc_insertion_point(global_scope)
+
+#endif // PROTOBUF_LayerScopePacket_2eproto__INCLUDED
diff --git a/gfx/layers/protobuf/LayerScopePacket.proto b/gfx/layers/protobuf/LayerScopePacket.proto
new file mode 100644
index 000000000..9b4306b2a
--- /dev/null
+++ b/gfx/layers/protobuf/LayerScopePacket.proto
@@ -0,0 +1,218 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+
+option optimize_for = LITE_RUNTIME;
+
+package mozilla.layers.layerscope;
+
+// ===============================
+// Server to Client messages
+// ===============================
+message FramePacket {
+ optional uint64 value = 1;
+ optional float scale = 2;
+}
+
+message ColorPacket {
+ required uint64 layerref = 1;
+ optional uint32 width = 2;
+ optional uint32 height = 3;
+ optional uint32 color = 4;
+}
+
+message TexturePacket {
+ enum Filter {
+ GOOD = 0;
+ LINEAR = 1;
+ POINT = 2;
+ }
+ message Rect {
+ optional float x = 1;
+ optional float y = 2;
+ optional float w = 3;
+ optional float h = 4;
+ }
+ message Size {
+ optional int32 w = 1;
+ optional int32 h = 2;
+ }
+ message Matrix {
+ optional bool is2D = 1;
+ optional bool isId = 2;
+ repeated float m = 3;
+ }
+ message EffectMask {
+ optional bool mIs3D = 1;
+ optional Size mSize = 2;
+ optional Matrix mMaskTransform = 3;
+ }
+
+ // Basic info
+ required uint64 layerref = 1;
+ optional uint32 width = 2;
+ optional uint32 height = 3;
+ optional uint32 stride = 4;
+ optional uint32 name = 5;
+ optional uint32 target = 6;
+ optional uint32 dataformat = 7;
+ optional uint64 glcontext = 8;
+ optional bytes data = 9;
+
+ // TextureEffect attributes
+ optional Rect mTextureCoords = 10;
+ optional bool mPremultiplied = 11;
+ optional Filter mFilter = 12;
+
+ // Mask attributes
+ optional bool isMask = 20;
+ optional EffectMask mask = 21;
+}
+
+message LayersPacket {
+ message Layer {
+ enum LayerType {
+ UnknownLayer = 0;
+ LayerManager = 1;
+ ContainerLayer = 2;
+ PaintedLayer = 3;
+ CanvasLayer = 4;
+ ImageLayer = 5;
+ ColorLayer = 6;
+ RefLayer = 7;
+ ReadbackLayer = 8;
+ }
+ enum ScrollingDirect {
+ VERTICAL = 1;
+ HORIZONTAL = 2;
+ }
+ enum Filter {
+ FILTER_FAST = 0; // deprecated
+ FILTER_GOOD = 1;
+ FILTER_BEST = 2; // deprecated
+ FILTER_NEAREST = 3; //deprecated
+ FILTER_BILINEAR = 4; //deprecated
+ FILTER_GAUSSIAN = 5; //deprecated
+ FILTER_SENTINEL = 6; //deprecated
+ FILTER_LINEAR = 7;
+ FILTER_POINT = 8;
+ }
+ message Size {
+ optional int32 w = 1;
+ optional int32 h = 2;
+ }
+ message Rect {
+ optional int32 x = 1;
+ optional int32 y = 2;
+ optional int32 w = 3;
+ optional int32 h = 4;
+ }
+ message Region {
+ repeated Rect r = 1;
+ }
+ message Matrix {
+ optional bool is2D = 1;
+ optional bool isId = 2;
+ repeated float m = 3;
+ }
+ message Shadow {
+ optional Rect clip = 1;
+ optional Matrix transform = 2;
+ optional Region vRegion = 3;
+ }
+
+ // Basic info
+ // Note: Parent's pointer is used to recontruct the layer tree
+ required LayerType type = 1;
+ required uint64 ptr = 2;
+ required uint64 parentPtr = 3;
+
+ // Common info (10 to 99)
+ optional Rect clip = 10;
+ optional Matrix transform = 11;
+ optional Region vRegion = 12; // visible region
+ optional Shadow shadow = 13; // shadow info
+ optional float opacity = 14;
+ optional bool cOpaque = 15; // content opaque
+ optional bool cAlpha = 16; // component alpha
+ optional ScrollingDirect direct = 17;
+ optional uint64 barID = 18;
+ optional uint64 mask = 19; // mask layer
+ optional Region hitRegion = 20;
+ optional Region dispatchRegion = 21;
+ optional Region noActionRegion = 22;
+ optional Region hPanRegion = 23;
+ optional Region vPanRegion = 24;
+
+ // Specific info (100 to max)
+ // Painted Layer
+ optional Region valid = 100;
+ // Color Layer
+ optional uint32 color = 101;
+ // Canvas & Image Layer
+ optional Filter filter = 102;
+ // Ref Layer
+ optional uint64 refID = 103;
+ // Readback Layer
+ optional Size size = 104;
+ optional uint32 displayListLogLength = 105;
+ optional bytes displayListLog = 106;
+ }
+ repeated Layer layer = 1;
+}
+
+message MetaPacket {
+ optional bool composedByHwc = 1;
+}
+
+message DrawPacket {
+ message Rect {
+ required float x = 1;
+ required float y = 2;
+ required float w = 3;
+ required float h = 4;
+ }
+
+ required float offsetX = 1;
+ required float offsetY = 2;
+ repeated float mvMatrix = 3;
+ required uint32 totalRects = 4;
+ repeated Rect layerRect = 5;
+ required uint64 layerref = 6;
+ repeated uint32 texIDs = 7;
+ repeated Rect textureRect = 8;
+}
+
+// We only need to use this Packet.
+// Other packet definitions are just type defines
+message Packet {
+ enum DataType {
+ FRAMESTART = 1;
+ FRAMEEND = 2;
+ COLOR = 3;
+ TEXTURE = 4;
+ LAYERS = 5;
+ META = 6;
+ DRAW = 7;
+ }
+ required DataType type = 1;
+
+ optional FramePacket frame = 2;
+ optional ColorPacket color = 3;
+ optional TexturePacket texture = 4;
+ optional LayersPacket layers = 5;
+ optional MetaPacket meta = 6;
+ optional DrawPacket draw = 7;
+}
+
+
+// ===============================
+// Client to Server messages
+// ===============================
+message CommandPacket {
+ enum CmdType {
+ NO_OP = 0;
+ LAYERS_TREE = 1;
+ LAYERS_BUFFER = 2;
+ }
+ required CmdType type = 1;
+ optional bool value = 2;
+}