From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- gfx/layers/AsyncCanvasRenderer.cpp | 290 + gfx/layers/AsyncCanvasRenderer.h | 168 + gfx/layers/AtomicRefCountedWithFinalize.h | 196 + gfx/layers/AxisPhysicsMSDModel.cpp | 93 + gfx/layers/AxisPhysicsMSDModel.h | 86 + gfx/layers/AxisPhysicsModel.cpp | 121 + gfx/layers/AxisPhysicsModel.h | 131 + gfx/layers/BSPTree.cpp | 107 + gfx/layers/BSPTree.h | 106 + gfx/layers/BufferTexture.cpp | 594 ++ gfx/layers/BufferTexture.h | 99 + gfx/layers/BufferUnrotate.cpp | 64 + gfx/layers/BufferUnrotate.h | 14 + gfx/layers/Compositor.cpp | 609 ++ gfx/layers/Compositor.h | 726 ++ gfx/layers/CompositorTypes.h | 249 + gfx/layers/CopyableCanvasLayer.cpp | 102 + gfx/layers/CopyableCanvasLayer.h | 67 + gfx/layers/D3D11ShareHandleImage.cpp | 163 + gfx/layers/D3D11ShareHandleImage.h | 72 + gfx/layers/D3D9SurfaceImage.cpp | 223 + gfx/layers/D3D9SurfaceImage.h | 86 + gfx/layers/DirectedGraph.h | 116 + gfx/layers/Effects.cpp | 67 + gfx/layers/Effects.h | 331 + gfx/layers/FrameMetrics.cpp | 23 + gfx/layers/FrameMetrics.h | 1112 +++ gfx/layers/GLImages.cpp | 113 + gfx/layers/GLImages.h | 94 + gfx/layers/GPUVideoImage.h | 71 + gfx/layers/IMFYCbCrImage.cpp | 306 + gfx/layers/IMFYCbCrImage.h | 39 + gfx/layers/IPDLActor.h | 62 + gfx/layers/ImageContainer.cpp | 797 ++ gfx/layers/ImageContainer.h | 903 ++ gfx/layers/ImageDataSerializer.cpp | 261 + gfx/layers/ImageDataSerializer.h | 88 + gfx/layers/ImageLayers.cpp | 64 + gfx/layers/ImageLayers.h | 94 + gfx/layers/ImageTypes.h | 127 + gfx/layers/LayerMetricsWrapper.h | 516 ++ gfx/layers/LayerScope.cpp | 1833 ++++ gfx/layers/LayerScope.h | 68 + gfx/layers/LayerSorter.cpp | 361 + gfx/layers/LayerSorter.h | 21 + gfx/layers/LayerTreeInvalidation.cpp | 679 ++ gfx/layers/LayerTreeInvalidation.h | 77 + gfx/layers/LayerUserData.h | 28 + gfx/layers/Layers.cpp | 2505 ++++++ gfx/layers/Layers.h | 2619 ++++++ gfx/layers/LayersLogging.cpp | 421 + gfx/layers/LayersLogging.h | 267 + gfx/layers/LayersTypes.cpp | 29 + gfx/layers/LayersTypes.h | 250 + gfx/layers/MacIOSurfaceHelpers.cpp | 176 + gfx/layers/MacIOSurfaceHelpers.h | 28 + gfx/layers/MacIOSurfaceImage.cpp | 36 + gfx/layers/MacIOSurfaceImage.h | 48 + gfx/layers/PersistentBufferProvider.cpp | 445 + gfx/layers/PersistentBufferProvider.h | 187 + gfx/layers/ReadbackLayer.h | 204 + gfx/layers/ReadbackProcessor.cpp | 186 + gfx/layers/ReadbackProcessor.h | 80 + gfx/layers/RenderTrace.cpp | 82 + gfx/layers/RenderTrace.h | 75 + gfx/layers/RotatedBuffer.cpp | 798 ++ gfx/layers/RotatedBuffer.h | 430 + gfx/layers/TextureDIB.cpp | 505 ++ gfx/layers/TextureDIB.h | 132 + gfx/layers/TextureWrapperImage.cpp | 58 + gfx/layers/TextureWrapperImage.h | 37 + gfx/layers/TiledLayerBuffer.h | 220 + gfx/layers/TransactionIdAllocator.h | 66 + gfx/layers/TreeTraversal.h | 281 + gfx/layers/apz/public/CompositorController.h | 33 + gfx/layers/apz/public/GeckoContentController.h | 179 + gfx/layers/apz/public/IAPZCTreeManager.cpp | 164 + gfx/layers/apz/public/IAPZCTreeManager.h | 223 + gfx/layers/apz/public/MetricsSharingController.h | 40 + gfx/layers/apz/src/APZCTreeManager.cpp | 2099 +++++ gfx/layers/apz/src/APZCTreeManager.h | 531 ++ gfx/layers/apz/src/APZUtils.h | 83 + gfx/layers/apz/src/AndroidAPZ.cpp | 274 + gfx/layers/apz/src/AndroidAPZ.h | 61 + gfx/layers/apz/src/AsyncDragMetrics.h | 65 + gfx/layers/apz/src/AsyncPanZoomAnimation.h | 80 + gfx/layers/apz/src/AsyncPanZoomController.cpp | 4030 +++++++++ gfx/layers/apz/src/AsyncPanZoomController.h | 1224 +++ gfx/layers/apz/src/Axis.cpp | 681 ++ gfx/layers/apz/src/Axis.h | 336 + gfx/layers/apz/src/CheckerboardEvent.cpp | 230 + gfx/layers/apz/src/CheckerboardEvent.h | 221 + gfx/layers/apz/src/DragTracker.cpp | 70 + gfx/layers/apz/src/DragTracker.h | 39 + gfx/layers/apz/src/GenericFlingAnimation.h | 207 + gfx/layers/apz/src/GestureEventListener.cpp | 552 ++ gfx/layers/apz/src/GestureEventListener.h | 252 + gfx/layers/apz/src/HitTestingTreeNode.cpp | 336 + gfx/layers/apz/src/HitTestingTreeNode.h | 166 + gfx/layers/apz/src/InputBlockState.cpp | 868 ++ gfx/layers/apz/src/InputBlockState.h | 483 + gfx/layers/apz/src/InputQueue.cpp | 731 ++ gfx/layers/apz/src/InputQueue.h | 217 + gfx/layers/apz/src/Overscroll.h | 137 + gfx/layers/apz/src/OverscrollHandoffState.cpp | 175 + gfx/layers/apz/src/OverscrollHandoffState.h | 159 + .../src/PotentialCheckerboardDurationTracker.cpp | 79 + .../apz/src/PotentialCheckerboardDurationTracker.h | 60 + gfx/layers/apz/src/QueuedInput.cpp | 54 + gfx/layers/apz/src/QueuedInput.h | 58 + gfx/layers/apz/src/TouchCounter.cpp | 50 + gfx/layers/apz/src/TouchCounter.h | 33 + gfx/layers/apz/src/WheelScrollAnimation.cpp | 119 + gfx/layers/apz/src/WheelScrollAnimation.h | 51 + gfx/layers/apz/test/gtest/APZCBasicTester.h | 120 + gfx/layers/apz/test/gtest/APZCTreeManagerTester.h | 194 + gfx/layers/apz/test/gtest/APZTestCommon.h | 609 ++ gfx/layers/apz/test/gtest/InputUtils.h | 297 + gfx/layers/apz/test/gtest/TestBasic.cpp | 356 + gfx/layers/apz/test/gtest/TestEventRegions.cpp | 272 + gfx/layers/apz/test/gtest/TestGestureDetector.cpp | 638 ++ gfx/layers/apz/test/gtest/TestHitTesting.cpp | 578 ++ gfx/layers/apz/test/gtest/TestInputQueue.cpp | 44 + gfx/layers/apz/test/gtest/TestPanning.cpp | 128 + gfx/layers/apz/test/gtest/TestPinching.cpp | 294 + gfx/layers/apz/test/gtest/TestScrollHandoff.cpp | 521 ++ gfx/layers/apz/test/gtest/TestSnapping.cpp | 64 + gfx/layers/apz/test/gtest/TestTreeManager.cpp | 112 + gfx/layers/apz/test/gtest/moz.build | 33 + .../test/mochitest/apz_test_native_event_utils.js | 261 + gfx/layers/apz/test/mochitest/apz_test_utils.js | 403 + gfx/layers/apz/test/mochitest/chrome.ini | 9 + .../apz/test/mochitest/helper_basic_pan.html | 38 + .../apz/test/mochitest/helper_bug1151663.html | 83 + .../apz/test/mochitest/helper_bug1162771.html | 104 + .../apz/test/mochitest/helper_bug1271432.html | 574 ++ .../apz/test/mochitest/helper_bug1280013.html | 74 + .../apz/test/mochitest/helper_bug1285070.html | 44 + .../apz/test/mochitest/helper_bug1299195.html | 47 + .../apz/test/mochitest/helper_bug982141.html | 149 + gfx/layers/apz/test/mochitest/helper_click.html | 41 + gfx/layers/apz/test/mochitest/helper_div_pan.html | 44 + .../apz/test/mochitest/helper_drag_click.html | 43 + .../apz/test/mochitest/helper_drag_scroll.html | 603 ++ gfx/layers/apz/test/mochitest/helper_iframe1.html | 14 + gfx/layers/apz/test/mochitest/helper_iframe2.html | 14 + .../apz/test/mochitest/helper_iframe_pan.html | 41 + gfx/layers/apz/test/mochitest/helper_long_tap.html | 81 + .../helper_scroll_inactive_perspective.html | 46 + .../mochitest/helper_scroll_inactive_zindex.html | 47 + .../mochitest/helper_scroll_on_position_fixed.html | 62 + .../apz/test/mochitest/helper_scrollto_tap.html | 61 + .../apz/test/mochitest/helper_subframe_style.css | 15 + gfx/layers/apz/test/mochitest/helper_tall.html | 504 ++ gfx/layers/apz/test/mochitest/helper_tap.html | 32 + .../apz/test/mochitest/helper_tap_fullzoom.html | 33 + .../apz/test/mochitest/helper_tap_passive.html | 64 + .../apz/test/mochitest/helper_touch_action.html | 115 + .../mochitest/helper_touch_action_complex.html | 143 + .../mochitest/helper_touch_action_regions.html | 246 + gfx/layers/apz/test/mochitest/mochitest.ini | 67 + gfx/layers/apz/test/mochitest/test_bug1151663.html | 38 + gfx/layers/apz/test/mochitest/test_bug1151667.html | 65 + gfx/layers/apz/test/mochitest/test_bug1253683.html | 59 + gfx/layers/apz/test/mochitest/test_bug1277814.html | 106 + .../apz/test/mochitest/test_bug1304689-2.html | 131 + gfx/layers/apz/test/mochitest/test_bug1304689.html | 135 + gfx/layers/apz/test/mochitest/test_bug982141.html | 38 + .../test/mochitest/test_frame_reconstruction.html | 218 + .../apz/test/mochitest/test_group_mouseevents.html | 35 + .../test/mochitest/test_group_pointerevents.html | 31 + .../apz/test/mochitest/test_group_touchevents.html | 104 + .../apz/test/mochitest/test_group_wheelevents.html | 41 + gfx/layers/apz/test/mochitest/test_group_zoom.html | 44 + .../test/mochitest/test_interrupted_reflow.html | 719 ++ .../apz/test/mochitest/test_layerization.html | 214 + .../mochitest/test_scroll_inactive_bug1190112.html | 541 ++ .../test_scroll_inactive_flattened_frame.html | 49 + .../mochitest/test_scroll_subframe_scrollbar.html | 117 + gfx/layers/apz/test/mochitest/test_smoothness.html | 77 + .../test_touch_listeners_impacting_wheel.html | 114 + .../apz/test/mochitest/test_wheel_scroll.html | 106 + .../test/mochitest/test_wheel_transactions.html | 137 + .../apz/test/reftest/async-scrollbar-1-h-ref.html | 9 + .../test/reftest/async-scrollbar-1-h-rtl-ref.html | 10 + .../apz/test/reftest/async-scrollbar-1-h-rtl.html | 14 + .../apz/test/reftest/async-scrollbar-1-h.html | 13 + .../apz/test/reftest/async-scrollbar-1-v-ref.html | 9 + .../test/reftest/async-scrollbar-1-v-rtl-ref.html | 10 + .../apz/test/reftest/async-scrollbar-1-v-rtl.html | 14 + .../apz/test/reftest/async-scrollbar-1-v.html | 13 + .../apz/test/reftest/async-scrollbar-1-vh-ref.html | 9 + .../test/reftest/async-scrollbar-1-vh-rtl-ref.html | 10 + .../apz/test/reftest/async-scrollbar-1-vh-rtl.html | 14 + .../apz/test/reftest/async-scrollbar-1-vh.html | 13 + .../test/reftest/async-scrollbar-zoom-1-ref.html | 9 + .../apz/test/reftest/async-scrollbar-zoom-1.html | 14 + .../test/reftest/async-scrollbar-zoom-2-ref.html | 9 + .../apz/test/reftest/async-scrollbar-zoom-2.html | 14 + .../frame-reconstruction-scroll-clamping-ref.html | 27 + .../frame-reconstruction-scroll-clamping.html | 53 + .../apz/test/reftest/initial-scale-1-ref.html | 10 + gfx/layers/apz/test/reftest/initial-scale-1.html | 10 + gfx/layers/apz/test/reftest/reftest-stylo.list | 20 + gfx/layers/apz/test/reftest/reftest.list | 19 + gfx/layers/apz/testutil/APZTestData.cpp | 66 + gfx/layers/apz/testutil/APZTestData.h | 168 + gfx/layers/apz/util/APZCCallbackHelper.cpp | 942 ++ gfx/layers/apz/util/APZCCallbackHelper.h | 209 + gfx/layers/apz/util/APZEventState.cpp | 512 ++ gfx/layers/apz/util/APZEventState.h | 103 + gfx/layers/apz/util/APZThreadUtils.cpp | 96 + gfx/layers/apz/util/APZThreadUtils.h | 104 + gfx/layers/apz/util/ActiveElementManager.cpp | 237 + gfx/layers/apz/util/ActiveElementManager.h | 104 + gfx/layers/apz/util/CheckerboardReportService.cpp | 228 + gfx/layers/apz/util/CheckerboardReportService.h | 144 + gfx/layers/apz/util/ChromeProcessController.cpp | 276 + gfx/layers/apz/util/ChromeProcessController.h | 83 + gfx/layers/apz/util/ContentProcessController.cpp | 207 + gfx/layers/apz/util/ContentProcessController.h | 90 + gfx/layers/apz/util/DoubleTapToZoom.cpp | 178 + gfx/layers/apz/util/DoubleTapToZoom.h | 29 + gfx/layers/apz/util/InputAPZContext.cpp | 69 + gfx/layers/apz/util/InputAPZContext.h | 50 + gfx/layers/apz/util/ScrollInputMethods.h | 62 + gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp | 49 + gfx/layers/apz/util/ScrollLinkedEffectDetector.h | 43 + gfx/layers/apz/util/TouchActionHelper.cpp | 96 + gfx/layers/apz/util/TouchActionHelper.h | 42 + gfx/layers/basic/AutoMaskData.h | 60 + gfx/layers/basic/BasicCanvasLayer.cpp | 141 + gfx/layers/basic/BasicCanvasLayer.h | 51 + gfx/layers/basic/BasicColorLayer.cpp | 83 + gfx/layers/basic/BasicCompositor.cpp | 996 +++ gfx/layers/basic/BasicCompositor.h | 162 + gfx/layers/basic/BasicContainerLayer.cpp | 172 + gfx/layers/basic/BasicContainerLayer.h | 106 + gfx/layers/basic/BasicImageLayer.cpp | 119 + gfx/layers/basic/BasicImages.cpp | 178 + gfx/layers/basic/BasicImplData.h | 132 + gfx/layers/basic/BasicLayerManager.cpp | 991 ++ gfx/layers/basic/BasicLayers.h | 217 + gfx/layers/basic/BasicLayersImpl.cpp | 211 + gfx/layers/basic/BasicLayersImpl.h | 151 + gfx/layers/basic/BasicPaintedLayer.cpp | 245 + gfx/layers/basic/BasicPaintedLayer.h | 133 + gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp | 107 + gfx/layers/basic/MacIOSurfaceTextureHostBasic.h | 97 + gfx/layers/basic/TextureClientX11.cpp | 156 + gfx/layers/basic/TextureClientX11.h | 57 + gfx/layers/basic/TextureHostBasic.cpp | 33 + gfx/layers/basic/TextureHostBasic.h | 34 + gfx/layers/basic/X11BasicCompositor.cpp | 136 + gfx/layers/basic/X11BasicCompositor.h | 65 + gfx/layers/basic/X11TextureSourceBasic.cpp | 67 + gfx/layers/basic/X11TextureSourceBasic.h | 54 + gfx/layers/client/CanvasClient.cpp | 531 ++ gfx/layers/client/CanvasClient.h | 216 + gfx/layers/client/ClientCanvasLayer.cpp | 261 + gfx/layers/client/ClientCanvasLayer.h | 116 + gfx/layers/client/ClientColorLayer.cpp | 79 + gfx/layers/client/ClientContainerLayer.cpp | 36 + gfx/layers/client/ClientContainerLayer.h | 189 + gfx/layers/client/ClientImageLayer.cpp | 170 + gfx/layers/client/ClientLayerManager.cpp | 907 ++ gfx/layers/client/ClientLayerManager.h | 422 + gfx/layers/client/ClientPaintedLayer.cpp | 175 + gfx/layers/client/ClientPaintedLayer.h | 127 + gfx/layers/client/ClientReadbackLayer.h | 34 + gfx/layers/client/ClientTiledPaintedLayer.cpp | 615 ++ gfx/layers/client/ClientTiledPaintedLayer.h | 153 + gfx/layers/client/CompositableChild.cpp | 119 + gfx/layers/client/CompositableChild.h | 91 + gfx/layers/client/CompositableClient.cpp | 274 + gfx/layers/client/CompositableClient.h | 209 + gfx/layers/client/ContentClient.cpp | 668 ++ gfx/layers/client/ContentClient.h | 412 + gfx/layers/client/GPUVideoTextureClient.cpp | 72 + gfx/layers/client/GPUVideoTextureClient.h | 56 + gfx/layers/client/ImageClient.cpp | 300 + gfx/layers/client/ImageClient.h | 138 + gfx/layers/client/SingleTiledContentClient.cpp | 263 + gfx/layers/client/SingleTiledContentClient.h | 135 + gfx/layers/client/TextureClient.cpp | 1707 ++++ gfx/layers/client/TextureClient.h | 826 ++ gfx/layers/client/TextureClientPool.cpp | 339 + gfx/layers/client/TextureClientPool.h | 180 + .../client/TextureClientRecycleAllocator.cpp | 279 + gfx/layers/client/TextureClientRecycleAllocator.h | 140 + gfx/layers/client/TextureClientSharedSurface.cpp | 96 + gfx/layers/client/TextureClientSharedSurface.h | 77 + gfx/layers/client/TiledContentClient.cpp | 1430 +++ gfx/layers/client/TiledContentClient.h | 545 ++ gfx/layers/composite/AsyncCompositionManager.cpp | 1495 ++++ gfx/layers/composite/AsyncCompositionManager.h | 283 + gfx/layers/composite/CanvasLayerComposite.cpp | 166 + gfx/layers/composite/CanvasLayerComposite.h | 81 + gfx/layers/composite/ColorLayerComposite.cpp | 47 + gfx/layers/composite/ColorLayerComposite.h | 65 + gfx/layers/composite/CompositableHost.cpp | 292 + gfx/layers/composite/CompositableHost.h | 316 + gfx/layers/composite/ContainerLayerComposite.cpp | 698 ++ gfx/layers/composite/ContainerLayerComposite.h | 191 + gfx/layers/composite/ContentHost.cpp | 491 + gfx/layers/composite/ContentHost.h | 228 + gfx/layers/composite/FPSCounter.cpp | 450 + gfx/layers/composite/FPSCounter.h | 114 + gfx/layers/composite/FontData.h | 13 + gfx/layers/composite/FrameUniformityData.cpp | 152 + gfx/layers/composite/FrameUniformityData.h | 73 + gfx/layers/composite/GPUVideoTextureHost.cpp | 90 + gfx/layers/composite/GPUVideoTextureHost.h | 53 + gfx/layers/composite/ImageHost.cpp | 739 ++ gfx/layers/composite/ImageHost.h | 201 + gfx/layers/composite/ImageLayerComposite.cpp | 236 + gfx/layers/composite/ImageLayerComposite.h | 79 + gfx/layers/composite/LayerManagerComposite.cpp | 1451 +++ gfx/layers/composite/LayerManagerComposite.h | 687 ++ gfx/layers/composite/PaintCounter.cpp | 79 + gfx/layers/composite/PaintCounter.h | 49 + gfx/layers/composite/PaintedLayerComposite.cpp | 194 + gfx/layers/composite/PaintedLayerComposite.h | 94 + gfx/layers/composite/TextRenderer.cpp | 173 + gfx/layers/composite/TextRenderer.h | 50 + gfx/layers/composite/TextureHost.cpp | 1142 +++ gfx/layers/composite/TextureHost.h | 900 ++ gfx/layers/composite/TiledContentHost.cpp | 645 ++ gfx/layers/composite/TiledContentHost.h | 292 + gfx/layers/composite/X11TextureHost.cpp | 107 + gfx/layers/composite/X11TextureHost.h | 72 + gfx/layers/composite/qrcode_table.h | 259 + gfx/layers/d3d11/BlendShaderConstants.h | 59 + gfx/layers/d3d11/BlendingHelpers.hlslh | 184 + gfx/layers/d3d11/CompositorD3D11.cpp | 1586 ++++ gfx/layers/d3d11/CompositorD3D11.h | 208 + gfx/layers/d3d11/CompositorD3D11.hlsl | 421 + gfx/layers/d3d11/CompositorD3D11Shaders.h | 9434 ++++++++++++++++++++ gfx/layers/d3d11/ReadbackManagerD3D11.cpp | 160 + gfx/layers/d3d11/ReadbackManagerD3D11.h | 65 + gfx/layers/d3d11/TextureD3D11.cpp | 1291 +++ gfx/layers/d3d11/TextureD3D11.h | 455 + gfx/layers/d3d11/genshaders.sh | 50 + gfx/layers/d3d9/CompositorD3D9.cpp | 1045 +++ gfx/layers/d3d9/CompositorD3D9.h | 189 + gfx/layers/d3d9/DeviceManagerD3D9.cpp | 966 ++ gfx/layers/d3d9/DeviceManagerD3D9.h | 353 + gfx/layers/d3d9/LayerManagerD3D9Shaders.h | 1631 ++++ gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl | 242 + gfx/layers/d3d9/Nv3DVUtils.cpp | 148 + gfx/layers/d3d9/Nv3DVUtils.h | 86 + gfx/layers/d3d9/ReadbackLayerD3D9.h | 34 + gfx/layers/d3d9/TextureD3D9.cpp | 1218 +++ gfx/layers/d3d9/TextureD3D9.h | 433 + gfx/layers/d3d9/genshaders.sh | 35 + gfx/layers/ipc/APZCTreeManagerChild.cpp | 266 + gfx/layers/ipc/APZCTreeManagerChild.h | 111 + gfx/layers/ipc/APZCTreeManagerParent.cpp | 313 + gfx/layers/ipc/APZCTreeManagerParent.h | 151 + gfx/layers/ipc/APZChild.cpp | 98 + gfx/layers/ipc/APZChild.h | 55 + gfx/layers/ipc/CompositableForwarder.cpp | 28 + gfx/layers/ipc/CompositableForwarder.h | 127 + gfx/layers/ipc/CompositableTransactionParent.cpp | 245 + gfx/layers/ipc/CompositableTransactionParent.h | 55 + gfx/layers/ipc/CompositorBench.cpp | 345 + gfx/layers/ipc/CompositorBench.h | 31 + gfx/layers/ipc/CompositorBridgeChild.cpp | 1150 +++ gfx/layers/ipc/CompositorBridgeChild.h | 333 + gfx/layers/ipc/CompositorBridgeParent.cpp | 2441 +++++ gfx/layers/ipc/CompositorBridgeParent.h | 688 ++ gfx/layers/ipc/CompositorThread.cpp | 155 + gfx/layers/ipc/CompositorThread.h | 68 + .../ipc/CrossProcessCompositorBridgeParent.cpp | 578 ++ .../ipc/CrossProcessCompositorBridgeParent.h | 177 + gfx/layers/ipc/GonkNativeHandle.cpp | 80 + gfx/layers/ipc/GonkNativeHandle.h | 24 + gfx/layers/ipc/GonkNativeHandleUtils.cpp | 93 + gfx/layers/ipc/GonkNativeHandleUtils.h | 26 + gfx/layers/ipc/ISurfaceAllocator.cpp | 235 + gfx/layers/ipc/ISurfaceAllocator.h | 318 + gfx/layers/ipc/ImageBridgeChild.cpp | 1239 +++ gfx/layers/ipc/ImageBridgeChild.h | 400 + gfx/layers/ipc/ImageBridgeParent.cpp | 449 + gfx/layers/ipc/ImageBridgeParent.h | 155 + gfx/layers/ipc/ImageContainerChild.cpp | 70 + gfx/layers/ipc/ImageContainerChild.h | 61 + gfx/layers/ipc/ImageContainerParent.cpp | 31 + gfx/layers/ipc/ImageContainerParent.h | 37 + gfx/layers/ipc/KnowsCompositor.h | 90 + gfx/layers/ipc/LayerAnimationUtils.cpp | 45 + gfx/layers/ipc/LayerAnimationUtils.h | 30 + gfx/layers/ipc/LayerTransactionChild.cpp | 87 + gfx/layers/ipc/LayerTransactionChild.h | 90 + gfx/layers/ipc/LayerTransactionParent.cpp | 1098 +++ gfx/layers/ipc/LayerTransactionParent.h | 240 + gfx/layers/ipc/LayerTreeOwnerTracker.cpp | 83 + gfx/layers/ipc/LayerTreeOwnerTracker.h | 71 + gfx/layers/ipc/LayersMessages.ipdlh | 499 ++ gfx/layers/ipc/LayersSurfaces.ipdlh | 142 + gfx/layers/ipc/PAPZ.ipdl | 71 + gfx/layers/ipc/PAPZCTreeManager.ipdl | 139 + gfx/layers/ipc/PCompositable.ipdl | 28 + gfx/layers/ipc/PCompositorBridge.ipdl | 237 + gfx/layers/ipc/PImageBridge.ipdl | 70 + gfx/layers/ipc/PImageContainer.ipdl | 33 + gfx/layers/ipc/PLayer.ipdl | 39 + gfx/layers/ipc/PLayerTransaction.ipdl | 133 + gfx/layers/ipc/PTexture.ipdl | 46 + gfx/layers/ipc/PVideoBridge.ipdl | 31 + gfx/layers/ipc/RemoteContentController.cpp | 272 + gfx/layers/ipc/RemoteContentController.h | 96 + gfx/layers/ipc/ShadowLayerChild.cpp | 45 + gfx/layers/ipc/ShadowLayerChild.h | 39 + gfx/layers/ipc/ShadowLayerParent.cpp | 138 + gfx/layers/ipc/ShadowLayerParent.h | 58 + gfx/layers/ipc/ShadowLayerUtils.h | 52 + gfx/layers/ipc/ShadowLayerUtilsMac.cpp | 40 + gfx/layers/ipc/ShadowLayerUtilsX11.cpp | 153 + gfx/layers/ipc/ShadowLayerUtilsX11.h | 85 + gfx/layers/ipc/ShadowLayers.cpp | 1044 +++ gfx/layers/ipc/ShadowLayers.h | 465 + gfx/layers/ipc/SharedPlanarYCbCrImage.cpp | 246 + gfx/layers/ipc/SharedPlanarYCbCrImage.h | 60 + gfx/layers/ipc/SharedRGBImage.cpp | 113 + gfx/layers/ipc/SharedRGBImage.h | 59 + gfx/layers/ipc/SynchronousTask.h | 65 + gfx/layers/ipc/TextureForwarder.h | 79 + ...hreadSafeRefcountingWithMainThreadDestruction.h | 91 + gfx/layers/ipc/VideoBridgeChild.cpp | 122 + gfx/layers/ipc/VideoBridgeChild.h | 75 + gfx/layers/ipc/VideoBridgeParent.cpp | 125 + gfx/layers/ipc/VideoBridgeParent.h | 72 + gfx/layers/layerviewer/hide.png | Bin 0 -> 3079 bytes gfx/layers/layerviewer/index.html | 47 + gfx/layers/layerviewer/layerTreeView.js | 885 ++ gfx/layers/layerviewer/noise.png | Bin 0 -> 2118 bytes gfx/layers/layerviewer/show.png | Bin 0 -> 3187 bytes gfx/layers/layerviewer/tree.css | 36 + gfx/layers/moz.build | 451 + gfx/layers/opengl/Composer2D.h | 73 + gfx/layers/opengl/CompositingRenderTargetOGL.cpp | 120 + gfx/layers/opengl/CompositingRenderTargetOGL.h | 195 + gfx/layers/opengl/CompositorOGL.cpp | 1916 ++++ gfx/layers/opengl/CompositorOGL.h | 502 ++ gfx/layers/opengl/EGLImageHelpers.cpp | 53 + gfx/layers/opengl/EGLImageHelpers.h | 27 + gfx/layers/opengl/GLBlitTextureImageHelper.cpp | 280 + gfx/layers/opengl/GLBlitTextureImageHelper.h | 71 + gfx/layers/opengl/GLManager.cpp | 70 + gfx/layers/opengl/GLManager.h | 44 + gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp | 140 + gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h | 58 + gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp | 172 + gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h | 114 + gfx/layers/opengl/OGLShaderProgram.cpp | 974 ++ gfx/layers/opengl/OGLShaderProgram.h | 621 ++ gfx/layers/opengl/TextureClientOGL.cpp | 136 + gfx/layers/opengl/TextureClientOGL.h | 89 + gfx/layers/opengl/TextureHostOGL.cpp | 771 ++ gfx/layers/opengl/TextureHostOGL.h | 539 ++ gfx/layers/opengl/TexturePoolOGL.cpp | 123 + gfx/layers/opengl/TexturePoolOGL.h | 40 + gfx/layers/opengl/X11TextureSourceOGL.cpp | 114 + gfx/layers/opengl/X11TextureSourceOGL.h | 65 + gfx/layers/protobuf/LayerScopePacket.pb.cc | 6802 ++++++++++++++ gfx/layers/protobuf/LayerScopePacket.pb.h | 5779 ++++++++++++ gfx/layers/protobuf/LayerScopePacket.proto | 218 + 468 files changed, 143658 insertions(+) create mode 100644 gfx/layers/AsyncCanvasRenderer.cpp create mode 100644 gfx/layers/AsyncCanvasRenderer.h create mode 100644 gfx/layers/AtomicRefCountedWithFinalize.h create mode 100644 gfx/layers/AxisPhysicsMSDModel.cpp create mode 100644 gfx/layers/AxisPhysicsMSDModel.h create mode 100644 gfx/layers/AxisPhysicsModel.cpp create mode 100644 gfx/layers/AxisPhysicsModel.h create mode 100644 gfx/layers/BSPTree.cpp create mode 100644 gfx/layers/BSPTree.h create mode 100644 gfx/layers/BufferTexture.cpp create mode 100644 gfx/layers/BufferTexture.h create mode 100644 gfx/layers/BufferUnrotate.cpp create mode 100644 gfx/layers/BufferUnrotate.h create mode 100644 gfx/layers/Compositor.cpp create mode 100644 gfx/layers/Compositor.h create mode 100644 gfx/layers/CompositorTypes.h create mode 100644 gfx/layers/CopyableCanvasLayer.cpp create mode 100644 gfx/layers/CopyableCanvasLayer.h create mode 100644 gfx/layers/D3D11ShareHandleImage.cpp create mode 100644 gfx/layers/D3D11ShareHandleImage.h create mode 100644 gfx/layers/D3D9SurfaceImage.cpp create mode 100644 gfx/layers/D3D9SurfaceImage.h create mode 100644 gfx/layers/DirectedGraph.h create mode 100644 gfx/layers/Effects.cpp create mode 100644 gfx/layers/Effects.h create mode 100644 gfx/layers/FrameMetrics.cpp create mode 100644 gfx/layers/FrameMetrics.h create mode 100644 gfx/layers/GLImages.cpp create mode 100644 gfx/layers/GLImages.h create mode 100644 gfx/layers/GPUVideoImage.h create mode 100644 gfx/layers/IMFYCbCrImage.cpp create mode 100644 gfx/layers/IMFYCbCrImage.h create mode 100644 gfx/layers/IPDLActor.h create mode 100644 gfx/layers/ImageContainer.cpp create mode 100644 gfx/layers/ImageContainer.h create mode 100644 gfx/layers/ImageDataSerializer.cpp create mode 100644 gfx/layers/ImageDataSerializer.h create mode 100644 gfx/layers/ImageLayers.cpp create mode 100644 gfx/layers/ImageLayers.h create mode 100644 gfx/layers/ImageTypes.h create mode 100644 gfx/layers/LayerMetricsWrapper.h create mode 100644 gfx/layers/LayerScope.cpp create mode 100644 gfx/layers/LayerScope.h create mode 100644 gfx/layers/LayerSorter.cpp create mode 100644 gfx/layers/LayerSorter.h create mode 100644 gfx/layers/LayerTreeInvalidation.cpp create mode 100644 gfx/layers/LayerTreeInvalidation.h create mode 100644 gfx/layers/LayerUserData.h create mode 100644 gfx/layers/Layers.cpp create mode 100644 gfx/layers/Layers.h create mode 100644 gfx/layers/LayersLogging.cpp create mode 100644 gfx/layers/LayersLogging.h create mode 100644 gfx/layers/LayersTypes.cpp create mode 100644 gfx/layers/LayersTypes.h create mode 100644 gfx/layers/MacIOSurfaceHelpers.cpp create mode 100644 gfx/layers/MacIOSurfaceHelpers.h create mode 100644 gfx/layers/MacIOSurfaceImage.cpp create mode 100644 gfx/layers/MacIOSurfaceImage.h create mode 100644 gfx/layers/PersistentBufferProvider.cpp create mode 100644 gfx/layers/PersistentBufferProvider.h create mode 100644 gfx/layers/ReadbackLayer.h create mode 100644 gfx/layers/ReadbackProcessor.cpp create mode 100644 gfx/layers/ReadbackProcessor.h create mode 100644 gfx/layers/RenderTrace.cpp create mode 100644 gfx/layers/RenderTrace.h create mode 100644 gfx/layers/RotatedBuffer.cpp create mode 100644 gfx/layers/RotatedBuffer.h create mode 100644 gfx/layers/TextureDIB.cpp create mode 100644 gfx/layers/TextureDIB.h create mode 100644 gfx/layers/TextureWrapperImage.cpp create mode 100644 gfx/layers/TextureWrapperImage.h create mode 100644 gfx/layers/TiledLayerBuffer.h create mode 100644 gfx/layers/TransactionIdAllocator.h create mode 100644 gfx/layers/TreeTraversal.h create mode 100644 gfx/layers/apz/public/CompositorController.h create mode 100644 gfx/layers/apz/public/GeckoContentController.h create mode 100644 gfx/layers/apz/public/IAPZCTreeManager.cpp create mode 100644 gfx/layers/apz/public/IAPZCTreeManager.h create mode 100644 gfx/layers/apz/public/MetricsSharingController.h create mode 100644 gfx/layers/apz/src/APZCTreeManager.cpp create mode 100644 gfx/layers/apz/src/APZCTreeManager.h create mode 100644 gfx/layers/apz/src/APZUtils.h create mode 100644 gfx/layers/apz/src/AndroidAPZ.cpp create mode 100644 gfx/layers/apz/src/AndroidAPZ.h create mode 100644 gfx/layers/apz/src/AsyncDragMetrics.h create mode 100644 gfx/layers/apz/src/AsyncPanZoomAnimation.h create mode 100644 gfx/layers/apz/src/AsyncPanZoomController.cpp create mode 100644 gfx/layers/apz/src/AsyncPanZoomController.h create mode 100644 gfx/layers/apz/src/Axis.cpp create mode 100644 gfx/layers/apz/src/Axis.h create mode 100644 gfx/layers/apz/src/CheckerboardEvent.cpp create mode 100644 gfx/layers/apz/src/CheckerboardEvent.h create mode 100644 gfx/layers/apz/src/DragTracker.cpp create mode 100644 gfx/layers/apz/src/DragTracker.h create mode 100644 gfx/layers/apz/src/GenericFlingAnimation.h create mode 100644 gfx/layers/apz/src/GestureEventListener.cpp create mode 100644 gfx/layers/apz/src/GestureEventListener.h create mode 100644 gfx/layers/apz/src/HitTestingTreeNode.cpp create mode 100644 gfx/layers/apz/src/HitTestingTreeNode.h create mode 100644 gfx/layers/apz/src/InputBlockState.cpp create mode 100644 gfx/layers/apz/src/InputBlockState.h create mode 100644 gfx/layers/apz/src/InputQueue.cpp create mode 100644 gfx/layers/apz/src/InputQueue.h create mode 100644 gfx/layers/apz/src/Overscroll.h create mode 100644 gfx/layers/apz/src/OverscrollHandoffState.cpp create mode 100644 gfx/layers/apz/src/OverscrollHandoffState.h create mode 100644 gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp create mode 100644 gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h create mode 100644 gfx/layers/apz/src/QueuedInput.cpp create mode 100644 gfx/layers/apz/src/QueuedInput.h create mode 100644 gfx/layers/apz/src/TouchCounter.cpp create mode 100644 gfx/layers/apz/src/TouchCounter.h create mode 100644 gfx/layers/apz/src/WheelScrollAnimation.cpp create mode 100644 gfx/layers/apz/src/WheelScrollAnimation.h create mode 100644 gfx/layers/apz/test/gtest/APZCBasicTester.h create mode 100644 gfx/layers/apz/test/gtest/APZCTreeManagerTester.h create mode 100644 gfx/layers/apz/test/gtest/APZTestCommon.h create mode 100644 gfx/layers/apz/test/gtest/InputUtils.h create mode 100644 gfx/layers/apz/test/gtest/TestBasic.cpp create mode 100644 gfx/layers/apz/test/gtest/TestEventRegions.cpp create mode 100644 gfx/layers/apz/test/gtest/TestGestureDetector.cpp create mode 100644 gfx/layers/apz/test/gtest/TestHitTesting.cpp create mode 100644 gfx/layers/apz/test/gtest/TestInputQueue.cpp create mode 100644 gfx/layers/apz/test/gtest/TestPanning.cpp create mode 100644 gfx/layers/apz/test/gtest/TestPinching.cpp create mode 100644 gfx/layers/apz/test/gtest/TestScrollHandoff.cpp create mode 100644 gfx/layers/apz/test/gtest/TestSnapping.cpp create mode 100644 gfx/layers/apz/test/gtest/TestTreeManager.cpp create mode 100644 gfx/layers/apz/test/gtest/moz.build create mode 100644 gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js create mode 100644 gfx/layers/apz/test/mochitest/apz_test_utils.js create mode 100644 gfx/layers/apz/test/mochitest/chrome.ini create mode 100644 gfx/layers/apz/test/mochitest/helper_basic_pan.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1151663.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1162771.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1271432.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1280013.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1285070.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug1299195.html create mode 100644 gfx/layers/apz/test/mochitest/helper_bug982141.html create mode 100644 gfx/layers/apz/test/mochitest/helper_click.html create mode 100644 gfx/layers/apz/test/mochitest/helper_div_pan.html create mode 100644 gfx/layers/apz/test/mochitest/helper_drag_click.html create mode 100644 gfx/layers/apz/test/mochitest/helper_drag_scroll.html create mode 100644 gfx/layers/apz/test/mochitest/helper_iframe1.html create mode 100644 gfx/layers/apz/test/mochitest/helper_iframe2.html create mode 100644 gfx/layers/apz/test/mochitest/helper_iframe_pan.html create mode 100644 gfx/layers/apz/test/mochitest/helper_long_tap.html create mode 100644 gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html create mode 100644 gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html create mode 100644 gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html create mode 100644 gfx/layers/apz/test/mochitest/helper_scrollto_tap.html create mode 100644 gfx/layers/apz/test/mochitest/helper_subframe_style.css create mode 100644 gfx/layers/apz/test/mochitest/helper_tall.html create mode 100644 gfx/layers/apz/test/mochitest/helper_tap.html create mode 100644 gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html create mode 100644 gfx/layers/apz/test/mochitest/helper_tap_passive.html create mode 100644 gfx/layers/apz/test/mochitest/helper_touch_action.html create mode 100644 gfx/layers/apz/test/mochitest/helper_touch_action_complex.html create mode 100644 gfx/layers/apz/test/mochitest/helper_touch_action_regions.html create mode 100644 gfx/layers/apz/test/mochitest/mochitest.ini create mode 100644 gfx/layers/apz/test/mochitest/test_bug1151663.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug1151667.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug1253683.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug1277814.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug1304689-2.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug1304689.html create mode 100644 gfx/layers/apz/test/mochitest/test_bug982141.html create mode 100644 gfx/layers/apz/test/mochitest/test_frame_reconstruction.html create mode 100644 gfx/layers/apz/test/mochitest/test_group_mouseevents.html create mode 100644 gfx/layers/apz/test/mochitest/test_group_pointerevents.html create mode 100644 gfx/layers/apz/test/mochitest/test_group_touchevents.html create mode 100644 gfx/layers/apz/test/mochitest/test_group_wheelevents.html create mode 100644 gfx/layers/apz/test/mochitest/test_group_zoom.html create mode 100644 gfx/layers/apz/test/mochitest/test_interrupted_reflow.html create mode 100644 gfx/layers/apz/test/mochitest/test_layerization.html create mode 100644 gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html create mode 100644 gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html create mode 100644 gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html create mode 100644 gfx/layers/apz/test/mochitest/test_smoothness.html create mode 100644 gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html create mode 100644 gfx/layers/apz/test/mochitest/test_wheel_scroll.html create mode 100644 gfx/layers/apz/test/mochitest/test_wheel_transactions.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-h.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-v.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html create mode 100644 gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html create mode 100644 gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping-ref.html create mode 100644 gfx/layers/apz/test/reftest/frame-reconstruction-scroll-clamping.html create mode 100644 gfx/layers/apz/test/reftest/initial-scale-1-ref.html create mode 100644 gfx/layers/apz/test/reftest/initial-scale-1.html create mode 100644 gfx/layers/apz/test/reftest/reftest-stylo.list create mode 100644 gfx/layers/apz/test/reftest/reftest.list create mode 100644 gfx/layers/apz/testutil/APZTestData.cpp create mode 100644 gfx/layers/apz/testutil/APZTestData.h create mode 100644 gfx/layers/apz/util/APZCCallbackHelper.cpp create mode 100644 gfx/layers/apz/util/APZCCallbackHelper.h create mode 100644 gfx/layers/apz/util/APZEventState.cpp create mode 100644 gfx/layers/apz/util/APZEventState.h create mode 100644 gfx/layers/apz/util/APZThreadUtils.cpp create mode 100644 gfx/layers/apz/util/APZThreadUtils.h create mode 100644 gfx/layers/apz/util/ActiveElementManager.cpp create mode 100644 gfx/layers/apz/util/ActiveElementManager.h create mode 100644 gfx/layers/apz/util/CheckerboardReportService.cpp create mode 100644 gfx/layers/apz/util/CheckerboardReportService.h create mode 100644 gfx/layers/apz/util/ChromeProcessController.cpp create mode 100644 gfx/layers/apz/util/ChromeProcessController.h create mode 100644 gfx/layers/apz/util/ContentProcessController.cpp create mode 100644 gfx/layers/apz/util/ContentProcessController.h create mode 100644 gfx/layers/apz/util/DoubleTapToZoom.cpp create mode 100644 gfx/layers/apz/util/DoubleTapToZoom.h create mode 100644 gfx/layers/apz/util/InputAPZContext.cpp create mode 100644 gfx/layers/apz/util/InputAPZContext.h create mode 100644 gfx/layers/apz/util/ScrollInputMethods.h create mode 100644 gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp create mode 100644 gfx/layers/apz/util/ScrollLinkedEffectDetector.h create mode 100644 gfx/layers/apz/util/TouchActionHelper.cpp create mode 100644 gfx/layers/apz/util/TouchActionHelper.h create mode 100644 gfx/layers/basic/AutoMaskData.h create mode 100644 gfx/layers/basic/BasicCanvasLayer.cpp create mode 100644 gfx/layers/basic/BasicCanvasLayer.h create mode 100644 gfx/layers/basic/BasicColorLayer.cpp create mode 100644 gfx/layers/basic/BasicCompositor.cpp create mode 100644 gfx/layers/basic/BasicCompositor.h create mode 100644 gfx/layers/basic/BasicContainerLayer.cpp create mode 100644 gfx/layers/basic/BasicContainerLayer.h create mode 100644 gfx/layers/basic/BasicImageLayer.cpp create mode 100644 gfx/layers/basic/BasicImages.cpp create mode 100644 gfx/layers/basic/BasicImplData.h create mode 100644 gfx/layers/basic/BasicLayerManager.cpp create mode 100644 gfx/layers/basic/BasicLayers.h create mode 100644 gfx/layers/basic/BasicLayersImpl.cpp create mode 100644 gfx/layers/basic/BasicLayersImpl.h create mode 100644 gfx/layers/basic/BasicPaintedLayer.cpp create mode 100644 gfx/layers/basic/BasicPaintedLayer.h create mode 100644 gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp create mode 100644 gfx/layers/basic/MacIOSurfaceTextureHostBasic.h create mode 100644 gfx/layers/basic/TextureClientX11.cpp create mode 100644 gfx/layers/basic/TextureClientX11.h create mode 100644 gfx/layers/basic/TextureHostBasic.cpp create mode 100644 gfx/layers/basic/TextureHostBasic.h create mode 100644 gfx/layers/basic/X11BasicCompositor.cpp create mode 100644 gfx/layers/basic/X11BasicCompositor.h create mode 100644 gfx/layers/basic/X11TextureSourceBasic.cpp create mode 100644 gfx/layers/basic/X11TextureSourceBasic.h create mode 100644 gfx/layers/client/CanvasClient.cpp create mode 100644 gfx/layers/client/CanvasClient.h create mode 100644 gfx/layers/client/ClientCanvasLayer.cpp create mode 100644 gfx/layers/client/ClientCanvasLayer.h create mode 100644 gfx/layers/client/ClientColorLayer.cpp create mode 100644 gfx/layers/client/ClientContainerLayer.cpp create mode 100644 gfx/layers/client/ClientContainerLayer.h create mode 100644 gfx/layers/client/ClientImageLayer.cpp create mode 100644 gfx/layers/client/ClientLayerManager.cpp create mode 100644 gfx/layers/client/ClientLayerManager.h create mode 100644 gfx/layers/client/ClientPaintedLayer.cpp create mode 100644 gfx/layers/client/ClientPaintedLayer.h create mode 100644 gfx/layers/client/ClientReadbackLayer.h create mode 100644 gfx/layers/client/ClientTiledPaintedLayer.cpp create mode 100644 gfx/layers/client/ClientTiledPaintedLayer.h create mode 100644 gfx/layers/client/CompositableChild.cpp create mode 100644 gfx/layers/client/CompositableChild.h create mode 100644 gfx/layers/client/CompositableClient.cpp create mode 100644 gfx/layers/client/CompositableClient.h create mode 100644 gfx/layers/client/ContentClient.cpp create mode 100644 gfx/layers/client/ContentClient.h create mode 100644 gfx/layers/client/GPUVideoTextureClient.cpp create mode 100644 gfx/layers/client/GPUVideoTextureClient.h create mode 100644 gfx/layers/client/ImageClient.cpp create mode 100644 gfx/layers/client/ImageClient.h create mode 100644 gfx/layers/client/SingleTiledContentClient.cpp create mode 100644 gfx/layers/client/SingleTiledContentClient.h create mode 100644 gfx/layers/client/TextureClient.cpp create mode 100644 gfx/layers/client/TextureClient.h create mode 100644 gfx/layers/client/TextureClientPool.cpp create mode 100644 gfx/layers/client/TextureClientPool.h create mode 100644 gfx/layers/client/TextureClientRecycleAllocator.cpp create mode 100644 gfx/layers/client/TextureClientRecycleAllocator.h create mode 100644 gfx/layers/client/TextureClientSharedSurface.cpp create mode 100644 gfx/layers/client/TextureClientSharedSurface.h create mode 100644 gfx/layers/client/TiledContentClient.cpp create mode 100644 gfx/layers/client/TiledContentClient.h create mode 100644 gfx/layers/composite/AsyncCompositionManager.cpp create mode 100644 gfx/layers/composite/AsyncCompositionManager.h create mode 100644 gfx/layers/composite/CanvasLayerComposite.cpp create mode 100644 gfx/layers/composite/CanvasLayerComposite.h create mode 100644 gfx/layers/composite/ColorLayerComposite.cpp create mode 100644 gfx/layers/composite/ColorLayerComposite.h create mode 100644 gfx/layers/composite/CompositableHost.cpp create mode 100644 gfx/layers/composite/CompositableHost.h create mode 100755 gfx/layers/composite/ContainerLayerComposite.cpp create mode 100644 gfx/layers/composite/ContainerLayerComposite.h create mode 100644 gfx/layers/composite/ContentHost.cpp create mode 100644 gfx/layers/composite/ContentHost.h create mode 100644 gfx/layers/composite/FPSCounter.cpp create mode 100644 gfx/layers/composite/FPSCounter.h create mode 100644 gfx/layers/composite/FontData.h create mode 100644 gfx/layers/composite/FrameUniformityData.cpp create mode 100644 gfx/layers/composite/FrameUniformityData.h create mode 100644 gfx/layers/composite/GPUVideoTextureHost.cpp create mode 100644 gfx/layers/composite/GPUVideoTextureHost.h create mode 100644 gfx/layers/composite/ImageHost.cpp create mode 100644 gfx/layers/composite/ImageHost.h create mode 100644 gfx/layers/composite/ImageLayerComposite.cpp create mode 100644 gfx/layers/composite/ImageLayerComposite.h create mode 100644 gfx/layers/composite/LayerManagerComposite.cpp create mode 100644 gfx/layers/composite/LayerManagerComposite.h create mode 100644 gfx/layers/composite/PaintCounter.cpp create mode 100644 gfx/layers/composite/PaintCounter.h create mode 100644 gfx/layers/composite/PaintedLayerComposite.cpp create mode 100644 gfx/layers/composite/PaintedLayerComposite.h create mode 100644 gfx/layers/composite/TextRenderer.cpp create mode 100644 gfx/layers/composite/TextRenderer.h create mode 100644 gfx/layers/composite/TextureHost.cpp create mode 100644 gfx/layers/composite/TextureHost.h create mode 100644 gfx/layers/composite/TiledContentHost.cpp create mode 100644 gfx/layers/composite/TiledContentHost.h create mode 100644 gfx/layers/composite/X11TextureHost.cpp create mode 100644 gfx/layers/composite/X11TextureHost.h create mode 100644 gfx/layers/composite/qrcode_table.h create mode 100644 gfx/layers/d3d11/BlendShaderConstants.h create mode 100644 gfx/layers/d3d11/BlendingHelpers.hlslh create mode 100644 gfx/layers/d3d11/CompositorD3D11.cpp create mode 100644 gfx/layers/d3d11/CompositorD3D11.h create mode 100644 gfx/layers/d3d11/CompositorD3D11.hlsl create mode 100755 gfx/layers/d3d11/CompositorD3D11Shaders.h create mode 100644 gfx/layers/d3d11/ReadbackManagerD3D11.cpp create mode 100644 gfx/layers/d3d11/ReadbackManagerD3D11.h create mode 100644 gfx/layers/d3d11/TextureD3D11.cpp create mode 100644 gfx/layers/d3d11/TextureD3D11.h create mode 100644 gfx/layers/d3d11/genshaders.sh create mode 100644 gfx/layers/d3d9/CompositorD3D9.cpp create mode 100644 gfx/layers/d3d9/CompositorD3D9.h create mode 100644 gfx/layers/d3d9/DeviceManagerD3D9.cpp create mode 100644 gfx/layers/d3d9/DeviceManagerD3D9.h create mode 100755 gfx/layers/d3d9/LayerManagerD3D9Shaders.h create mode 100644 gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl create mode 100644 gfx/layers/d3d9/Nv3DVUtils.cpp create mode 100644 gfx/layers/d3d9/Nv3DVUtils.h create mode 100644 gfx/layers/d3d9/ReadbackLayerD3D9.h create mode 100644 gfx/layers/d3d9/TextureD3D9.cpp create mode 100644 gfx/layers/d3d9/TextureD3D9.h create mode 100644 gfx/layers/d3d9/genshaders.sh create mode 100644 gfx/layers/ipc/APZCTreeManagerChild.cpp create mode 100644 gfx/layers/ipc/APZCTreeManagerChild.h create mode 100644 gfx/layers/ipc/APZCTreeManagerParent.cpp create mode 100644 gfx/layers/ipc/APZCTreeManagerParent.h create mode 100644 gfx/layers/ipc/APZChild.cpp create mode 100644 gfx/layers/ipc/APZChild.h create mode 100644 gfx/layers/ipc/CompositableForwarder.cpp create mode 100644 gfx/layers/ipc/CompositableForwarder.h create mode 100644 gfx/layers/ipc/CompositableTransactionParent.cpp create mode 100644 gfx/layers/ipc/CompositableTransactionParent.h create mode 100644 gfx/layers/ipc/CompositorBench.cpp create mode 100644 gfx/layers/ipc/CompositorBench.h create mode 100644 gfx/layers/ipc/CompositorBridgeChild.cpp create mode 100644 gfx/layers/ipc/CompositorBridgeChild.h create mode 100644 gfx/layers/ipc/CompositorBridgeParent.cpp create mode 100644 gfx/layers/ipc/CompositorBridgeParent.h create mode 100644 gfx/layers/ipc/CompositorThread.cpp create mode 100644 gfx/layers/ipc/CompositorThread.h create mode 100644 gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp create mode 100644 gfx/layers/ipc/CrossProcessCompositorBridgeParent.h create mode 100644 gfx/layers/ipc/GonkNativeHandle.cpp create mode 100644 gfx/layers/ipc/GonkNativeHandle.h create mode 100644 gfx/layers/ipc/GonkNativeHandleUtils.cpp create mode 100644 gfx/layers/ipc/GonkNativeHandleUtils.h create mode 100644 gfx/layers/ipc/ISurfaceAllocator.cpp create mode 100644 gfx/layers/ipc/ISurfaceAllocator.h create mode 100644 gfx/layers/ipc/ImageBridgeChild.cpp create mode 100644 gfx/layers/ipc/ImageBridgeChild.h create mode 100644 gfx/layers/ipc/ImageBridgeParent.cpp create mode 100644 gfx/layers/ipc/ImageBridgeParent.h create mode 100644 gfx/layers/ipc/ImageContainerChild.cpp create mode 100644 gfx/layers/ipc/ImageContainerChild.h create mode 100644 gfx/layers/ipc/ImageContainerParent.cpp create mode 100644 gfx/layers/ipc/ImageContainerParent.h create mode 100755 gfx/layers/ipc/KnowsCompositor.h create mode 100644 gfx/layers/ipc/LayerAnimationUtils.cpp create mode 100644 gfx/layers/ipc/LayerAnimationUtils.h create mode 100644 gfx/layers/ipc/LayerTransactionChild.cpp create mode 100644 gfx/layers/ipc/LayerTransactionChild.h create mode 100644 gfx/layers/ipc/LayerTransactionParent.cpp create mode 100644 gfx/layers/ipc/LayerTransactionParent.h create mode 100644 gfx/layers/ipc/LayerTreeOwnerTracker.cpp create mode 100644 gfx/layers/ipc/LayerTreeOwnerTracker.h create mode 100644 gfx/layers/ipc/LayersMessages.ipdlh create mode 100644 gfx/layers/ipc/LayersSurfaces.ipdlh create mode 100644 gfx/layers/ipc/PAPZ.ipdl create mode 100644 gfx/layers/ipc/PAPZCTreeManager.ipdl create mode 100644 gfx/layers/ipc/PCompositable.ipdl create mode 100644 gfx/layers/ipc/PCompositorBridge.ipdl create mode 100644 gfx/layers/ipc/PImageBridge.ipdl create mode 100644 gfx/layers/ipc/PImageContainer.ipdl create mode 100644 gfx/layers/ipc/PLayer.ipdl create mode 100644 gfx/layers/ipc/PLayerTransaction.ipdl create mode 100644 gfx/layers/ipc/PTexture.ipdl create mode 100644 gfx/layers/ipc/PVideoBridge.ipdl create mode 100644 gfx/layers/ipc/RemoteContentController.cpp create mode 100644 gfx/layers/ipc/RemoteContentController.h create mode 100644 gfx/layers/ipc/ShadowLayerChild.cpp create mode 100644 gfx/layers/ipc/ShadowLayerChild.h create mode 100644 gfx/layers/ipc/ShadowLayerParent.cpp create mode 100644 gfx/layers/ipc/ShadowLayerParent.h create mode 100644 gfx/layers/ipc/ShadowLayerUtils.h create mode 100644 gfx/layers/ipc/ShadowLayerUtilsMac.cpp create mode 100644 gfx/layers/ipc/ShadowLayerUtilsX11.cpp create mode 100644 gfx/layers/ipc/ShadowLayerUtilsX11.h create mode 100644 gfx/layers/ipc/ShadowLayers.cpp create mode 100644 gfx/layers/ipc/ShadowLayers.h create mode 100644 gfx/layers/ipc/SharedPlanarYCbCrImage.cpp create mode 100644 gfx/layers/ipc/SharedPlanarYCbCrImage.h create mode 100644 gfx/layers/ipc/SharedRGBImage.cpp create mode 100644 gfx/layers/ipc/SharedRGBImage.h create mode 100644 gfx/layers/ipc/SynchronousTask.h create mode 100644 gfx/layers/ipc/TextureForwarder.h create mode 100644 gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h create mode 100644 gfx/layers/ipc/VideoBridgeChild.cpp create mode 100644 gfx/layers/ipc/VideoBridgeChild.h create mode 100644 gfx/layers/ipc/VideoBridgeParent.cpp create mode 100644 gfx/layers/ipc/VideoBridgeParent.h create mode 100644 gfx/layers/layerviewer/hide.png create mode 100644 gfx/layers/layerviewer/index.html create mode 100644 gfx/layers/layerviewer/layerTreeView.js create mode 100755 gfx/layers/layerviewer/noise.png create mode 100644 gfx/layers/layerviewer/show.png create mode 100644 gfx/layers/layerviewer/tree.css create mode 100644 gfx/layers/moz.build create mode 100644 gfx/layers/opengl/Composer2D.h create mode 100644 gfx/layers/opengl/CompositingRenderTargetOGL.cpp create mode 100644 gfx/layers/opengl/CompositingRenderTargetOGL.h create mode 100644 gfx/layers/opengl/CompositorOGL.cpp create mode 100644 gfx/layers/opengl/CompositorOGL.h create mode 100644 gfx/layers/opengl/EGLImageHelpers.cpp create mode 100644 gfx/layers/opengl/EGLImageHelpers.h create mode 100644 gfx/layers/opengl/GLBlitTextureImageHelper.cpp create mode 100644 gfx/layers/opengl/GLBlitTextureImageHelper.h create mode 100644 gfx/layers/opengl/GLManager.cpp create mode 100644 gfx/layers/opengl/GLManager.h create mode 100644 gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp create mode 100644 gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h create mode 100644 gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp create mode 100644 gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h create mode 100644 gfx/layers/opengl/OGLShaderProgram.cpp create mode 100644 gfx/layers/opengl/OGLShaderProgram.h create mode 100644 gfx/layers/opengl/TextureClientOGL.cpp create mode 100644 gfx/layers/opengl/TextureClientOGL.h create mode 100644 gfx/layers/opengl/TextureHostOGL.cpp create mode 100644 gfx/layers/opengl/TextureHostOGL.h create mode 100644 gfx/layers/opengl/TexturePoolOGL.cpp create mode 100644 gfx/layers/opengl/TexturePoolOGL.h create mode 100644 gfx/layers/opengl/X11TextureSourceOGL.cpp create mode 100644 gfx/layers/opengl/X11TextureSourceOGL.h create mode 100644 gfx/layers/protobuf/LayerScopePacket.pb.cc create mode 100644 gfx/layers/protobuf/LayerScopePacket.pb.h create mode 100644 gfx/layers/protobuf/LayerScopePacket.proto (limited to 'gfx/layers') 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 mRenderer; + }; + + nsCOMPtr 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 mRenderer; + }; + + nsCOMPtr 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 +AsyncCanvasRenderer::GetActiveThread() +{ + MutexAutoLock lock(mMutex); + nsCOMPtr 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 +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 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 +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 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 surface = GetSurface(); + if (!surface) { + return NS_ERROR_FAILURE; + } + + // Handle y flip. + RefPtr 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 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 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 mGLContext; +private: + + virtual ~AsyncCanvasRenderer(); + + // Readback current WebGL's content and return it as DataSourceSurface. + already_AddRefed 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 mSurfaceForBasic; + + // Protect non thread-safe objects. + Mutex mMutex; + + // Can be accessed in any thread, need protect by mutex. + nsCOMPtr 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 StaticRefPtr; + +namespace gl { +template +class RefSet; + +template +class RefQueue; +} // namespace gl + +template +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 + friend class ::mozilla::StaticRefPtr; + + template + friend struct mozilla::RefPtrTraits; + + template + friend class ::mozilla::gl::RefSet; + + template + 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(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(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 mRefCount; +#ifdef DEBUG +public: + bool mSpew; +private: + Atomic mManualAddRefs; + Atomic 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 // 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 // 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& aLayers) +{ + LayerPolygon layer = Move(aLayers.front()); + aLayers.pop_front(); + return layer; +} + +void +BSPTree::BuildDrawOrder(const UniquePtr& aNode, + nsTArray& aLayers) const +{ + const gfx::Point3D& normal = aNode->First().GetNormal(); + + UniquePtr *front = &aNode->front; + UniquePtr *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& aRoot, + std::deque& aLayers) +{ + if (aLayers.empty()) { + return; + } + + const gfx::Polygon3D& plane = aRoot->First(); + std::deque backLayers, frontLayers; + + for (LayerPolygon& layerPolygon : aLayers) { + const Maybe& geometry = layerPolygon.geometry; + + size_t pos = 0, neg = 0; + nsTArray 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 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 + +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&& aPoints, const gfx::Point3D& aNormal) + : layer(aLayer), geometry(Some(gfx::Polygon3D(Move(aPoints), aNormal))) {} + + Layer *layer; + Maybe geometry; +}; + +LayerPolygon PopFront(std::deque& 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 front; + UniquePtr back; + std::deque 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& aLayers) + { + MOZ_ASSERT(!aLayers.empty()); + mRoot.reset(new BSPTreeNode(PopFront(aLayers))); + + BuildTree(mRoot, aLayers); + } + + // Returns the root node of the BSP tree. + const UniquePtr& GetRoot() const + { + return mRoot; + } + + // Builds and returns the back-to-front draw order for the created BSP tree. + nsTArray GetDrawOrder() const + { + nsTArray layers; + BuildDrawOrder(mRoot, layers); + return layers; + } + +private: + UniquePtr 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& aNode, + nsTArray& aLayers) const; + void BuildTree(UniquePtr& aRoot, + std::deque& 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()); + } + + virtual uint8_t* GetBuffer() override { return mShmem.get(); } + + virtual size_t GetBufferSize() override { return mShmem.Size(); } + +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 +BufferTextureData::GetCbCrSize() const +{ + return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor); +} + +Maybe +BufferTextureData::GetYUVColorSpace() const +{ + return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor); +} + +Maybe +BufferTextureData::GetStereoMode() const +{ + return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor); +} + +gfx::SurfaceFormat +BufferTextureData::GetFormat() const +{ + return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor); +} + +already_AddRefed +BufferTextureData::BorrowDrawTarget() +{ + if (mDrawTarget) { + mDrawTarget->SetTransform(gfx::Matrix()); + RefPtr 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 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 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 surface = + gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride, + rgb.size(), rgb.format()); + + if (!surface) { + gfxCriticalError() << "Failed to get serializer as surface!"; + return false; + } + + RefPtr 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(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(); + 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 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 GetCbCrSize() const; + + Maybe GetYUVColorSpace() const; + + Maybe 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 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 // min & max +#include +#include +#include +#include +#include + +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& 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 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(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 +#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 CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0; + + virtual already_AddRefed + CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; } + + virtual already_AddRefed + 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 + 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 + 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& aGeometry); + + void DrawGeometry(const gfx::Rect& aRect, + const gfx::IntRect& aClipRect, + const EffectChain &aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const Maybe& 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> mUnlockAfterComposition; + + /** + * An array of TextureHosts that will need to call NotifyNotUsed() after the next composition. + */ + nsTArray> 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 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 // for uint32_t +#include // 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 // 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 mGLContext; + RefPtr mBufferProvider; + UniquePtr mGLFrontbuffer; + + bool mIsAlphaPremultiplied; + gl::OriginPos mOriginPos; + bool mIsMirror; + + RefPtr 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(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 +D3D11ShareHandleImage::GetAsSourceSurface() +{ + RefPtr texture = GetTexture(); + if (!texture) { + NS_WARNING("Cannot readback from shared texture because no texture is available."); + return nullptr; + } + + RefPtr 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 softTexture; + HRESULT hr = device->CreateTexture2D(&softDesc, + NULL, + static_cast(getter_AddRefs(softTexture))); + + if (FAILED(hr)) { + NS_WARNING("Failed to create 2D staging texture."); + return nullptr; + } + + RefPtr context; + device->GetImmediateContext(getter_AddRefs(context)); + if (!context) { + return nullptr; + } + + context->CopyResource(softTexture, texture); + + RefPtr 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 +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 +D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat, + const gfx::IntSize& aSize) +{ + RefPtr 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 + CreateOrRecycleClient(gfx::SurfaceFormat aFormat, + const gfx::IntSize& aSize); + +protected: + virtual already_AddRefed + Allocate(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) override; + + RefPtr 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 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 mTextureClient; + RefPtr 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 surface = aSurface; + + RefPtr device; + hr = surface->GetDevice(getter_AddRefs(device)); + NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); + + RefPtr 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(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 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 +D3D9SurfaceImage::GetD3D9Surface() +{ + RefPtr 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 +D3D9SurfaceImage::GetAsSourceSurface() +{ + if (!mTexture) { + return nullptr; + } + + HRESULT hr; + RefPtr 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 textureSurface = GetD3D9Surface(); + if (!textureSurface) { + return nullptr; + } + + RefPtr device; + hr = textureSurface->GetDevice(getter_AddRefs(device)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + RefPtr 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 +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(data, aTextureFlags, + mSurfaceAllocator->GetTextureForwarder()); +} + +already_AddRefed +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 + CreateOrRecycleClient(gfx::SurfaceFormat aFormat, + const gfx::IntSize& aSize); + +protected: + virtual already_AddRefed + Allocate(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) override; + + RefPtr 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 GetAsSourceSurface() override; + + virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + + already_AddRefed GetD3D9Surface(); + + HANDLE GetShareHandle() const; + + virtual bool IsValid() override { return mValid; } + + void Invalidate() { mValid = false; } + +private: + + gfx::IntSize mSize; + RefPtr mTextureClient; + RefPtr 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 +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& 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& 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 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 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 mPrimaryEffect; + EnumeratedArray> + 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 +CreateTexturedEffect(gfx::SurfaceFormat aFormat, + TextureSource* aSource, + const gfx::SamplingFilter aSamplingFilter, + bool isAlphaPremultiplied, + const LayerRenderState &state = LayerRenderState()) +{ + MOZ_ASSERT(aSource); + RefPtr 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 +CreateTexturedEffect(TextureHost* aHost, + TextureSource* aSource, + const gfx::SamplingFilter aSamplingFilter, + bool isAlphaPremultiplied, + const LayerRenderState &state = LayerRenderState()) +{ + MOZ_ASSERT(aHost); + MOZ_ASSERT(aSource); + + RefPtr 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 +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(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 +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 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 // 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 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; +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 + // 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 mScrollSnapIntervalX; + Maybe 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 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; + +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& aIndex) { + mMaskLayerIndex = aIndex; + } + const Maybe& 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 mMaskLayerIndex; +}; + +typedef Maybe 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; + + typedef FrameMetrics::ViewID ViewID; +public: + static StaticAutoPtr 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& aScrollClip) { + mScrollClip = aScrollClip; + } + const Maybe& 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 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 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 +gfx::Log& operator<<(gfx::Log& 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 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 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 +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 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 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 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 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 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 mMutex; +}; + +static already_AddRefed +InitTextures(IDirect3DDevice9* aDevice, + const IntSize &aSize, + _D3DFORMAT aFormat, + RefPtr& aSurface, + HANDLE& aHandle, + D3DLOCKED_RECT& aLockedRect) +{ + if (!aDevice) { + return nullptr; + } + + RefPtr 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 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 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& aTexture, + HANDLE& aHandle, + uint8_t* aSrc, + const gfx::IntSize& aSrcSize, + int32_t aSrcStride) +{ + RefPtr 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 device = DeviceManagerD3D9::GetDevice(); + if (!device) { + return nullptr; + } + + RefPtr textureY; + HANDLE shareHandleY = 0; + if (!UploadData(device, textureY, shareHandleY, + mData.mYChannel, mData.mYSize, mData.mYStride)) { + return nullptr; + } + + RefPtr textureCb; + HANDLE shareHandleCb = 0; + if (!UploadData(device, textureCb, shareHandleCb, + mData.mCbChannel, mData.mCbCrSize, mData.mCbCrStride)) { + return nullptr; + } + + RefPtr textureCr; + HANDLE shareHandleCr = 0; + if (!UploadData(device, textureCr, shareHandleCr, + mData.mCrChannel, mData.mCbCrSize, mData.mCbCrStride)) { + return nullptr; + } + + RefPtr 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 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 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 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 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 mBuffer; + RefPtr m2DBuffer; + RefPtr 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 +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 // 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 +#endif + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace android; +using namespace mozilla::gfx; + +Atomic Image::sSerialCounter(0); + +Atomic ImageContainer::sGenerationCounter(0); + +RefPtr +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 aBuffer, uint32_t aSize) +{ + MutexAutoLock lock(mLock); + + if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) { + mRecycledBuffers.Clear(); + } + mRecycledBufferSize = aSize; + mRecycledBuffers.AppendElement(Move(aBuffer)); +} + +UniquePtr +BufferRecycleBin::GetBuffer(uint32_t aSize) +{ + MutexAutoLock lock(mLock); + + if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) + return MakeUnique(aSize); + + uint32_t last = mRecycledBuffers.Length() - 1; + UniquePtr 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 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 imageBridge = ImageBridgeChild::GetSingleton()) { + imageBridge->ReleaseImageContainer(mIPDLChild); + } + } +} + +RefPtr +ImageContainer::CreatePlanarYCbCrImage() +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + EnsureImageClient(false); + if (mImageClient && mImageClient->AsImageClientSingle()) { + return new SharedPlanarYCbCrImage(mImageClient); + } + return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin); +} + +RefPtr +ImageContainer::CreateSharedRGBImage() +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + EnsureImageClient(false); + if (!mImageClient || !mImageClient->AsImageClientSingle()) { + return nullptr; + } + return new SharedRGBImage(mImageClient); +} + +void +ImageContainer::SetCurrentImageInternal(const nsTArray& 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 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()); +} + +void +ImageContainer::SetCurrentImages(const nsTArray& aImages) +{ + MOZ_ASSERT(!aImages.IsEmpty()); + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + if (mImageClient) { + if (RefPtr 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 imageBridge = ImageBridgeChild::GetSingleton()) { + imageBridge->FlushAllImages(mImageClient, this); + } + return; + } + + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + SetCurrentImageInternal(nsTArray()); +} + +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 images; + images.AppendElement(NonOwningImage(aImage)); + SetCurrentImagesInTransaction(images); +} + +void +ImageContainer::SetCurrentImagesInTransaction(const nsTArray& 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* 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 +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(mData.mCbCrStride) * mData.mCbCrSize.height * 2 + + CheckedInt(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 +PlanarYCbCrImage::GetAsSourceSurface() +{ + if (mSourceSurface) { + RefPtr 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 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 +NVImage::GetAsSourceSurface() +{ + if (mSourceSurface) { + RefPtr 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 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(aData.mYSize.height) * aData.mYStride + + CheckedInt(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 +NVImage::AllocateBuffer(uint32_t aSize) +{ + UniquePtr 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 = mTextureClients.Get(aForwarder->GetSerial()); + if (textureClient) { + return textureClient; + } + + RefPtr 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 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 // for uint32_t, uint8_t, uint64_t +#include // 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 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 { +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 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 { +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(¤t); + if (current) { + aRawRef->Release(); + return; + } + nsCOMPtr 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 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 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> + mBackendData; + + void* mImplData; + int32_t mSerial; + ImageFormat mFormat; + + static mozilla::Atomic 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 aBuffer, uint32_t aSize); + // Returns a recycled buffer of the right size, or allocates a new buffer. + mozilla::UniquePtr 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> 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 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 +{ + 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 CreatePlanarYCbCrImage(); + + // Factory methods for shared image types. + RefPtr 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& 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& 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 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* 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& 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 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 mImageFactory; + + gfx::IntSize mScaleHint; + + RefPtr 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 mImageClient; + + uint64_t mAsyncContainerID; + + nsTArray 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 mIPDLChild; + + static mozilla::Atomic 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 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 GetAsSourceSurface(); + + void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } + gfxImageFormat GetOffscreenFormat(); + + Data mData; + gfx::IntPoint mOrigin; + gfx::IntSize mSize; + gfxImageFormat mOffscreenFormat; + nsCountedRef 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 AllocateBuffer(uint32_t aSize); + + RefPtr mRecycleBin; + mozilla::UniquePtr 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 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 AllocateBuffer(uint32_t aSize); + + mozilla::UniquePtr mBuffer; + uint32_t mBufferSize; + gfx::IntSize mSize; + Data mData; + nsCountedRef 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 GetAsSourceSurface() override + { + RefPtr 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 mSourceSurface; + nsDataHashtable > 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 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 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 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 +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 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 // 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 CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor); + +Maybe YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor); + +Maybe 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 +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 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(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 GetClipRect() const + { + MOZ_ASSERT(IsValid()); + + Maybe 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 +#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 + +// 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 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& aProtocolString); + bool WebSocketHandshake(nsTArray& 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 mOutputStream; + nsCOMPtr mInputStream; + nsCOMPtr mTransport; + bool mConnected; + }; + + nsTArray > mHandlers; + nsCOMPtr mDebugSenderThread; + RefPtr mCurrentSender; + nsCOMPtr 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 mTexIDs; +}; + +class ContentMonitor { +public: + using THArray = nsTArray; + + // 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(); + } else { + // Dispatch creation to main thread, and make sure we + // dispatch this only once after booting + static bool dispatched = false; + if (dispatched) { + return; + } + + DebugOnly 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(); + } + + return mContentMonitor.get(); + } + + void NewDrawSession() { + mSession = mozilla::MakeUnique(); + } + + 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(); + return NS_OK; + } + private: + LayerScopeManager* mLayerScopeManager; + }; + + mozilla::UniquePtr mWebSocketManager; + mozilla::UniquePtr mSession; + mozilla::UniquePtr mContentMonitor; + double mScale; +}; + +LayerScopeManager gLayerScopeManager; + +/* + * The static helper functions that set data into the packet + * 1. DumpRect + * 2. DumpFilter + */ +template +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 { +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(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(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 aPacket) + : DebugGLData(Packet::TEXTURE), + mLayerRef(reinterpret_cast(layerRef)), + mTarget(target), + mName(name), + mContextAddress(reinterpret_cast(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(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(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 mPacket; +}; + +class DebugGLColorData final: public DebugGLData { +public: + DebugGLColorData(void* layerRef, + const Color& color, + int width, + int height) + : DebugGLData(Packet::COLOR), + mLayerRef(reinterpret_cast(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 aPacket) + : DebugGLData(Packet::LAYERS), + mPacket(Move(aPacket)) + { } + + virtual bool Write() override { + mPacket->set_type(mDataType); + return WriteToStream(*mPacket); + } + +protected: + UniquePtr 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 aTexIDs, + void* aLayerRef) + : DebugGLData(Packet::DRAW), + mOffsetX(aOffsetX), + mOffsetY(aOffsetY), + mMVMatrix(aMVMatrix), + mRects(aRects), + mTexIDs(aTexIDs), + mLayerRef(reinterpret_cast(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(&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 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 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 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 cleaner(d); + if (!d->Write()) { + gLayerScopeManager.DestroyServerSocket(); + break; + } + } + + // Cleanup. + mHost->RemoveData(); + return NS_OK; + } + private: + virtual ~SendTask() { } + + RefPtr 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 mList; + nsCOMPtr 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 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 sSentTextureIds; +}; + +bool SenderHelper::sLayersTreeSendable = true; +bool SenderHelper::sLayersBufferSendable = true; +std::vector 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 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 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::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(); + 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(&(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(primaryEffect); + SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect); + break; + } + case EffectTypes::YCBCR: { + const EffectYCbCr* yCbCrEffect = + static_cast(primaryEffect); + SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect); + break; + } + case EffectTypes::SOLID_COLOR: { + const EffectSolidColor* solidColorEffect = + static_cast(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(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 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(wsHeader), + wsHeaderSize, &cnt); + if (NS_FAILED(rv)) + return false; + + uint32_t written = 0; + while (written < aSize) { + uint32_t cnt; + rv = mOutputStream->Write(reinterpret_cast(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 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& aProtocolString) +{ + nsLineBuffer 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& 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(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(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(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(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(aData) & 3)) { + *aData ^= aMask >> 24; + aMask = RotateLeft(aMask, 8); + aData++; + aLen--; + } + + // perform mask on full words of data + uint32_t *iData = reinterpret_cast(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(); + p->ParseFromArray(static_cast(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 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 +#include +#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 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 // for fabs +#include // for uint32_t +#include // for fprintf, stderr, FILE +#include // 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 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& 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& aGraph) +{ + const nsTArray::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& aLayers) +{ + uint32_t nodeCount = aLayers.Length(); + if (nodeCount > MAX_SORTABLE_LAYERS) { + return; + } + DirectedGraph 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 noIncoming; + nsTArray sortedList; + + // Make a list of all layers with no incoming edges. + noIncoming.AppendElements(aLayers); + const nsTArray::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::Edge> outgoing; + graph.GetEdgesFrom(layer, outgoing); + for (uint32_t i = 0; i < outgoing.Length(); i++) { + DirectedGraph::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& 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 // 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 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( + 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& 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 mLayer; + UniquePtr mMaskLayer; + nsTArray> 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, 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& child : mChildren) { + result = result.Union(child->NewTransformedBounds()); + } + return result; + } + + return LayerPropertiesBase::NewTransformedBounds(); + } + + IntRect OldTransformedBounds() override + { + if (mLayer->Extend3DContext()) { + IntRect result; + for (UniquePtr& child : mChildren) { + result = result.Union(child->OldTransformedBounds()); + } + return result; + } + return LayerPropertiesBase::OldTransformedBounds(); + } + + // The old list of children: + mozilla::CorruptionCanary mSubtypeCanary; + nsTArray> 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(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(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(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 mContainer; + RefPtr 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(mLayer.get()); + + ImageHost* host = GetImageHost(canvasLayer); + if (host && host->GetFrameID() != mFrameID) { + aGeometryChanged = true; + + return NewTransformedBounds(); + } + + return IntRect(); + } + + RefPtr mImageHost; + int32_t mFrameID; +}; + +UniquePtr +CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */) +{ + if (!aRoot) { + return MakeUnique(); + } + + MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE); + + aRoot->CheckCanary(); + + switch (aRoot->GetType()) { + case Layer::TYPE_CONTAINER: + case Layer::TYPE_REF: + return MakeUnique(aRoot->AsContainerLayer()); + case Layer::TYPE_COLOR: + return MakeUnique(static_cast(aRoot)); + case Layer::TYPE_IMAGE: + return MakeUnique(static_cast(aRoot), aIsMask); + case Layer::TYPE_CANVAS: + return MakeUnique(static_cast(aRoot)); + case Layer::TYPE_READBACK: + case Layer::TYPE_SHADOW: + case Layer::TYPE_PAINTED: + return MakeUnique(aRoot); + } + + MOZ_ASSERT_UNREACHABLE("Unexpected root layer type"); + return MakeUnique(aRoot); +} + +/* static */ UniquePtr +LayerProperties::CloneFrom(Layer* aRoot) +{ + return CloneLayerTreePropertiesInternal(aRoot); +} + +/* static */ void +LayerProperties::ClearInvalidations(Layer *aLayer) +{ + ForEachNode( + 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 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 // 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( + 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(root, + [](LayerMetricsWrapper aLayerMetrics) + { + return aLayerMetrics.Metrics().IsRootContent(); + } + ); +} + +already_AddRefed +LayerManager::CreateOptimalDrawTarget(const gfx::IntSize &aSize, + SurfaceFormat aFormat) +{ + return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSize, + aFormat); +} + +already_AddRefed +LayerManager::CreateOptimalMaskDrawTarget(const gfx::IntSize &aSize) +{ + return CreateOptimalDrawTarget(aSize, SurfaceFormat::A8); +} + +already_AddRefed +LayerManager::CreateDrawTarget(const IntSize &aSize, + SurfaceFormat aFormat) +{ + return gfxPlatform::GetPlatform()-> + CreateOffscreenCanvasDrawTarget(aSize, aFormat); +} + +already_AddRefed +LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize, + mozilla::gfx::SurfaceFormat aFormat) +{ + RefPtr 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 +LayerManager::CreateImageContainer(ImageContainer::Mode flag) +{ + RefPtr container = new ImageContainer(flag); + return container.forget(); +} + +bool +LayerManager::AreComponentAlphaLayersEnabled() +{ + return gfxPrefs::ComponentAlphaEnabled(); +} + +/*static*/ void +LayerManager::LayerUserDataDestroy(void* data) +{ + delete static_cast(data); +} + +UniquePtr +LayerManager::RemoveUserData(void* aKey) +{ + UniquePtr d(static_cast(mUserData.Remove(static_cast(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& aFunctions) +{ + nsAutoPtr result; + nsCSSValueList** resultTail = getter_Transfers(result); + for (uint32_t i = 0; i < aFunctions.Length(); i++) { + RefPtr 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(animation.fillMode())) { + case dom::FillMode::None: + animation.fillMode() = static_cast(dom::FillMode::Forwards); + break; + case dom::FillMode::Backwards: + animation.fillMode() = static_cast(dom::FillMode::Both); + break; + default: + break; + } + + AnimData* data = mAnimationData.AppendElement(); + InfallibleTArray>& functions = + data->mFunctions; + const InfallibleTArray& segments = animation.segments(); + for (uint32_t j = 0; j < segments.Length(); j++) { + TimingFunction tf = segments.ElementAt(j).sampleFn(); + + Maybe ctf = + AnimationUtils::TimingFunctionToComputedTimingFunction(tf); + functions.AppendElement(ctf); + } + + // Precompute the StyleAnimationValues that we need if this is a transform + // animation. + InfallibleTArray& startValues = data->mStartValues; + InfallibleTArray& 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& startFunctions = + segment.startState().get_ArrayOfTransformFunction(); + startValue->SetTransformValue(CreateCSSValueList(startFunctions)); + + const InfallibleTArray& 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( + 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( + 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& +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(*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 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(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 +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(GetTransform()); +} + +Matrix4x4 +Layer::GetLocalTransform() +{ + if (LayerComposite* shadow = AsLayerComposite()) + return shadow->GetShadowTransform(); + else + return GetTransform(); +} + +const LayerToParentLayerMatrix4x4 +Layer::GetLocalTransformTyped() +{ + return ViewAs(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 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 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 +Layer::GetCombinedClipRect() const +{ + Maybe 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& 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& aToSort) +{ + ForEachNode( + (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& aArray) +{ + AutoTArray 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& 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(l)->NotifyPaintedLayerRemoved(tl); + } + } + } + if (aLayer->GetType() == TYPE_READBACK) { + static_cast(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((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& 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 +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 surf = aTarget->Snapshot(); + RefPtr 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("
  • "; + } + DumpSelf(aStream, aPrefix); + +#ifdef MOZ_DUMP_PAINTING + if (dumpCompositorTexture) { + AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml); + } else if (dumpClientTexture) { + if (aDumpHtml) { + aStream << nsPrintfCString(""; + } + } +#endif + + if (aDumpHtml) { + aStream << ""; +#ifdef MOZ_DUMP_PAINTING + if (dumpClientTexture) { + aStream << nsPrintfCString("
    \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 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 << "
      "; + } + + for (Layer* child : children) { + child->Dump(aStream, pfx.get(), aDumpHtml, aSorted); + } + + if (aDumpHtml) { + aStream << "
    "; + } + } + + if (aDumpHtml) { + aStream << "
  • "; + } +} + +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 +static void +DumpRect(layerscope::LayersPacket::Layer::Rect* aLayerRect, + const BaseRect& 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(this)); + layer->set_parentptr(reinterpret_cast(aParent)); + // Shadow + if (LayerComposite* lc = AsLayerComposite()) { + LayersPacket::Layer::Shadow* s = layer->mutable_shadow(); + if (const Maybe& 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(GetContentFlags() & CONTENT_OPAQUE)); + // Component alpha + layer->set_calpha(static_cast(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(mMaskLayer.get())); + } + + // DisplayList log. + if (mDisplayListLog.Length() > 0) { + layer->set_displaylistloglength(mDisplayListLog.Length()); + auto compressedData = + MakeUnique(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 +Layer::RemoveUserData(void* aKey) +{ + UniquePtr d(static_cast(mUserData.Remove(static_cast(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 << "
    • "; + } +#endif + DumpSelf(aStream, aPrefix, aSorted); + + nsAutoCString pfx(aPrefix); + pfx += " "; + if (!GetRoot()) { + aStream << nsPrintfCString("%s(null)", pfx.get()).get(); + if (aDumpHtml) { + aStream << "
    "; + } + return; + } + + if (aDumpHtml) { + aStream << "
      "; + } + GetRoot()->Dump(aStream, pfx.get(), aDumpHtml); + if (aDumpHtml) { + aStream << "
    "; + } + 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(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 +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& 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 +#include // for uint32_t, uint64_t, uint8_t +#include // for FILE +#include // 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(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& 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 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 CreatePaintedLayerWithHint(PaintedLayerCreationHint) { + return CreatePaintedLayer(); + } + /** + * CONSTRUCTION PHASE ONLY + * Create a ContainerLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateContainerLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create an ImageLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateImageLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a ColorLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateColorLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a CanvasLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateCanvasLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a ReadbackLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateReadbackLayer() { return nullptr; } + /** + * CONSTRUCTION PHASE ONLY + * Create a RefLayer for this manager's layer tree. + */ + virtual already_AddRefed 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 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 + 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 + CreateOptimalMaskDrawTarget(const IntSize &aSize); + + /** + * Creates a DrawTarget for use with canvas which is optimized for + * inter-operating with this layermanager. + */ + virtual already_AddRefed + 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 + 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(aKey), aData, LayerUserDataDestroy); + } + /** + * This can be used anytime. Ownership passes to the caller! + */ + UniquePtr RemoveUserData(void* aKey); + + /** + * This getter can be used anytime. + */ + bool HasUserData(void* aKey) + { + return mUserData.Has(static_cast(aKey)); + } + /** + * This getter can be used anytime. Ownership is retained by the layer + * manager. + */ + LayerUserData* GetUserData(void* aKey) const + { + return static_cast(mUserData.Get(static_cast(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& 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 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 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 GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId); + void ClearPendingScrollInfoUpdate(); +private: + std::map mPendingScrollUpdates; +}; + +typedef InfallibleTArray AnimationArray; + +struct AnimData { + InfallibleTArray mStartValues; + InfallibleTArray mEndValues; + InfallibleTArray> 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&) 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& 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& aRect) + { + if (mClipRect) { + if (!aRect) { + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClipRect was %d,%d,%d,%d is ", 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 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& 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>& 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& 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(); + } + 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& GetClipRect() const { return mClipRect; } + const Maybe& GetScrolledClip() const { return mScrolledClip; } + Maybe 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& 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>& 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 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& 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(aKey), aData, LayerManager::LayerUserDataDestroy); + } + /** + * This can be used anytime. Ownership passes to the caller! + */ + UniquePtr RemoveUserData(void* aKey); + /** + * This getter can be used anytime. + */ + bool HasUserData(void* aKey) + { + return mUserData.Has(static_cast(aKey)); + } + /** + * This getter can be used anytime. Ownership is retained by the layer + * manager. + */ + LayerUserData* GetUserData(void* aKey) const + { + return static_cast(mUserData.Get(static_cast(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& GetLocalClipRect(); + const LayerIntRegion& GetLocalVisibleRegion(); + + bool Extend3DContext() { + return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT; + } + bool Combines3DTransformWithAncestors() { + return GetParent() && + reinterpret_cast(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 mMaskLayer; + nsTArray> mAncestorMaskLayers; + // Look for out-of-bound in the middle of the structure + mozilla::CorruptionCanary mCanary; + gfx::UserData mUserData; + gfx::IntRect mLayerBounds; + LayerIntRegion mVisibleRegion; + nsTArray 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 mPendingTransform; + float mPostXScale; + float mPostYScale; + gfx::Matrix4x4 mEffectiveTransform; + AnimationArray mAnimations; + // See mPendingTransform above. + nsAutoPtr mPendingAnimations; + InfallibleTArray mAnimationData; + float mOpacity; + gfx::CompositionOp mMixBlendMode; + bool mForceIsolatedGroup; + Maybe mClipRect; + Maybe mScrolledClip; + gfx::IntRect mTileSourceRect; + gfx::TiledIntRegion mInvalidRegion; + nsTArray > mApzcs; + uint32_t mContentFlags; + bool mUseTileSourceRect; + bool mIsFixedPosition; + bool mTransformIsPerspective; + struct FixedPositionData { + FrameMetrics::ViewID mScrollId; + LayerPoint mAnchor; + int32_t mSides; + }; + UniquePtr mFixedPositionData; + struct StickyPositionData { + FrameMetrics::ViewID mScrollId; + LayerRect mOuter; + LayerRect mInner; + }; + nsAutoPtr 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 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 transformedOrig = + idealTransform.TransformPoint(mozilla::gfx::Point()); +#ifdef DEBUG + DebugOnly 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& 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& 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 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 // 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 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::PointTyped& p, + const char* pfx="", const char* sfx="") +{ + aStream << pfx << p << sfx; +} + +template +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::IntPointTyped& p, + const char* pfx="", const char* sfx="") +{ + aStream << pfx << p << sfx; +} + +template +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::RectTyped& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRectTyped& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::IntRegionTyped& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::TiledRegion& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::SizeTyped& sz, + const char* pfx="", const char* sfx="") +{ + aStream << pfx; + aStream << nsPrintfCString( + "(w=%f, h=%f)", + sz.width, sz.height).get(); + aStream << sfx; +} + +template +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::IntSizeTyped& sz, + const char* pfx="", const char* sfx="") +{ + aStream << pfx; + aStream << nsPrintfCString( + "(w=%d, h=%d)", + sz.width, sz.height).get(); + aStream << sfx; +} + +template +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::ScaleFactors2D& 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 +void +AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4Typed& 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 +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 // for uint32_t + +#include "Units.h" +#include "mozilla/gfx/Point.h" // for IntPoint +#include "mozilla/TypedEnumBits.h" +#include "nsRegion.h" + +#include // 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 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 AsyncTransformComponentMatrix; +typedef gfx::Matrix4x4Typed 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((reinterpret_cast(x)+31)&~31) + +static already_AddRefed +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 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(cbCrWidth * cbCrHeight); + auto crPlane = MakeUnique(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(cbCrStride * 2 * ioHeight + 31); + auto cbPlane = MakeUnique(cbCrStride * cbCrHeight + 31); + auto crPlane = MakeUnique(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 +CreateSourceSurfaceFromMacIOSurface(MacIOSurface* aSurface) +{ + aSurface->Lock(); + RefPtr 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 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 +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 +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 GetAsSourceSurface() override; + + virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + + virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override { + return this; + } + +private: + RefPtr mSurface; + RefPtr 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 +PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect) +{ + MOZ_ASSERT(!mSnapshot); + RefPtr dt(mDrawTarget); + return dt.forget(); +} + +bool +PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed aDT) +{ + RefPtr 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 +PersistentBufferProviderBasic::BorrowSnapshot() +{ + mSnapshot = mDrawTarget->Snapshot(); + RefPtr snapshot = mSnapshot; + return snapshot.forget(); +} + +void +PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed aSnapshot) +{ + RefPtr snapshot = aSnapshot; + MOZ_ASSERT(!snapshot || snapshot == mSnapshot); + mSnapshot = nullptr; +} + +//static +already_AddRefed +PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + gfx::BackendType aBackend) +{ + RefPtr dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat); + + if (!dt) { + return nullptr; + } + + RefPtr provider = + new PersistentBufferProviderBasic(dt); + + return provider.forget(); +} + + +//static +already_AddRefed +PersistentBufferProviderShared::Create(gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + ShadowLayerForwarder* aFwd) +{ + if (!aFwd || !aFwd->GetTextureForwarder()->IPCOpen()) { + return nullptr; + } + + RefPtr texture = TextureClient::CreateForDrawing( + aFwd, aFormat, aSize, + BackendSelector::Canvas, + TextureFlags::DEFAULT, + TextureAllocationFlags::ALLOC_DEFAULT + ); + + if (!texture) { + return nullptr; + } + + RefPtr provider = + new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture); + return provider.forget(); +} + +PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + ShadowLayerForwarder* aFwd, + RefPtr& aTexture) + +: mSize(aSize) +, mFormat(aFormat) +, mFwd(aFwd) +, mFront(Nothing()) +{ + if (mTextures.append(aTexture)) { + mBack = Some(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 prevTexture = GetTexture(mFront); + + // Get rid of everything else + Destroy(); + + if (prevTexture) { + RefPtr 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(mTextures.length() - 1); + mBack = mFront; + } + } + + mFwd = aFwd; + + return true; +} + +TextureClient* +PersistentBufferProviderShared::GetTexture(Maybe aIndex) +{ + if (aIndex.isNothing() || !CheckIndex(aIndex.value())) { + return nullptr; + } + return mTextures[aIndex.value()]; +} + +already_AddRefed +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 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 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(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 success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr); + MOZ_ASSERT(success); + + previous->Unlock(); + } + } + + mDrawTarget = tex->BorrowDrawTarget(); + + RefPtr dt(mDrawTarget); + return dt.forget(); +} + +bool +PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed aDT) +{ + RefPtr 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 +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 dt = front->BorrowDrawTarget(); + + if (!dt) { + front->Unlock(); + return nullptr; + } + + mSnapshot = dt->Snapshot(); + + RefPtr snapshot = mSnapshot; + return snapshot.forget(); +} + +void +PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed aSnapshot) +{ + RefPtr snapshot = aSnapshot; + MOZ_ASSERT(!snapshot || snapshot == mSnapshot); + + mSnapshot = nullptr; + snapshot = nullptr; + + auto front = GetTexture(mFront); + if (front) { + front->Unlock(); + } +} + +void +PersistentBufferProviderShared::NotifyInactive() +{ + RefPtr front = GetTexture(mFront); + RefPtr 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(0); + } + if (front == back) { + mFront = mBack; + } + } + + if (front && front != back) { + if (mTextures.append(front)) { + mFront = Some(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 +{ +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 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 aDT) = 0; + + virtual already_AddRefed BorrowSnapshot() = 0; + + virtual void ReturnSnapshot(already_AddRefed 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 + 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 BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override; + + virtual bool ReturnDrawTarget(already_AddRefed aDT) override; + + virtual already_AddRefed BorrowSnapshot() override; + + virtual void ReturnSnapshot(already_AddRefed aSnapshot) override; + + virtual bool PreservesDrawingState() const override { return true; } +private: + ~PersistentBufferProviderBasic(); + + RefPtr mDrawTarget; + RefPtr 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 + Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + ShadowLayerForwarder* aFwd); + + virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; } + + virtual already_AddRefed BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override; + + virtual bool ReturnDrawTarget(already_AddRefed aDT) override; + + virtual already_AddRefed BorrowSnapshot() override; + + virtual void ReturnSnapshot(already_AddRefed 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& aTexture); + + ~PersistentBufferProviderShared(); + + TextureClient* GetTexture(Maybe aIndex); + bool CheckIndex(uint32_t aIndex) { return aIndex < mTextures.length(); } + + void Destroy(); + + gfx::IntSize mSize; + gfx::SurfaceFormat mFormat; + RefPtr mFwd; + Vector, 4> mTextures; + // Offset of the texture in mTextures that the canvas uses. + Maybe mBack; + // Offset of the texture in mTextures that is presented to the compositor. + Maybe mFront; + + RefPtr mDrawTarget; + RefPtr mSnapshot; +}; + +struct AutoReturnSnapshot +{ + PersistentBufferProvider* mBufferProvider; + RefPtr* 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 // 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 + 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 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 // 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(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& clipRect = l->GetLocalClipRect(); + if (clipRect && !clipRect->Contains(ViewAs(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(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 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(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* 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 // 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* aUpdates, + nsIntRegion* aUpdateRegion = nullptr); + + ~ReadbackProcessor(); + +protected: + void BuildUpdatesForLayer(ReadbackLayer* aLayer); + + nsTArray 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( + 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 // for int32_t +#include // 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 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 +SourceRotatedBuffer::GetSourceSurface(ContextSource aSource) const +{ + RefPtr 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 destDTBuffer; + RefPtr 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 +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 // 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 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 GetSourceSurface(ContextSource aSource) const; + + virtual bool HaveBuffer() const { return !!mSource; } + virtual bool HaveBufferOnWhite() const { return !!mSourceOnWhite; } + +private: + RefPtr mSource; + RefPtr 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 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* aBlackDT, RefPtr* 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 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 mDTBuffer; + RefPtr 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 +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(mSurface.get())); + mSurface->AddRef(); + return true; +} + +DIBTextureData* +MemoryDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) +{ + RefPtr 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 imgSurf = mSurface->GetAsImageSurface(); + + RefPtr 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 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 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(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 imgSurf = mSurface->GetAsImageSurface(); + + RefPtr surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat); + + if (!surf || !mTextureSource->Update(surf, const_cast(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 surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat); + if (surf) { + surf->AddUserData(&kFileMappingKey, data, UnmapFileData); + if (!mTextureSource->Update(surf, const_cast(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 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 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 mTextureSource; + RefPtr mCompositor; + gfx::SurfaceFormat mFormat; + gfx::IntSize mSize; + bool mIsLocked; +}; + +class DIBTextureHost : public TextureHostDirectUpload +{ +public: + DIBTextureHost(TextureFlags aFlags, + const SurfaceDescriptorDIB& aDescriptor); + + virtual already_AddRefed GetAsSurface() override + { + return nullptr; // TODO: cf bug 872568 + } + +protected: + virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override; + + RefPtr mSurface; +}; + +class TextureHostFileMapping : public TextureHostDirectUpload +{ +public: + TextureHostFileMapping(TextureFlags aFlags, + const SurfaceDescriptorFileMapping& aDescriptor); + ~TextureHostFileMapping(); + + virtual already_AddRefed 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 +TextureWrapperImage::GetAsSourceSurface() +{ + TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_READ); + if (!autoLock.Succeeded()) { + return nullptr; + } + + RefPtr 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 GetAsSourceSurface() override; + TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + +private: + gfx::IntRect mPictureRect; + RefPtr 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 // for uint16_t, uint32_t +#include // 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 : 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 TileIntSize; +typedef gfx::IntPointTyped 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 +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 mRetainedTiles; + TilesPlacement mTiles; + float mResolution; + gfx::IntSize mTileSize; + gfx::IntPoint mTileOrigin; +}; + +template void +TiledLayerBuffer::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 + +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 + static Node* FirstChild(Node* n) { + return n->GetFirstChild(); + } + template + static Node* NextSibling(Node* n) { + return n->GetNextSibling(); + } + template + static Node FirstChild(Node n) { + return n.GetFirstChild(); + } + template + static Node NextSibling(Node n) { + return n.GetNextSibling(); + } +}; +class ReverseIterator +{ + public: + template + static Node* FirstChild(Node* n) { + return n->GetLastChild(); + } + template + static Node* NextSibling(Node* n) { + return n->GetPrevSibling(); + } + template + static Node FirstChild(Node n) { + return n.GetLastChild(); + } + template + 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 +static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) -> +typename EnableIf::value && + IsSame::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(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 +static auto ForEachNode(Node aRoot, const PreAction& aPreAction, const PostAction& aPostAction) -> +typename EnableIf::value && + IsSame::value, void>::Type +{ + if (!aRoot) { + return; + } + + aPreAction(aRoot); + + for (Node child = Iterator::FirstChild(aRoot); + child; + child = Iterator::NextSibling(child)) { + ForEachNode(child, aPreAction, aPostAction); + } + + aPostAction(aRoot); +} + +/* + * ForEachNode pre-order traversal, using TraversalFlag. + */ +template +auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> +typename EnableIf::value, bool>::Type +{ + return ForEachNode(aRoot, aPreAction, [](Node aNode){ return TraversalFlag::Continue; }); +} + +/* + * ForEachNode pre-order, not using TraversalFlag. + */ +template +auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> +typename EnableIf::value, void>::Type +{ + ForEachNode(aRoot, aPreAction, [](Node aNode){}); +} + +/* + * ForEachNode post-order traversal, using TraversalFlag. + */ +template +auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) -> +typename EnableIf::value, bool>::Type +{ + return ForEachNode(aRoot, [](Node aNode){ return TraversalFlag::Continue; }, aPostAction); +} + +/* + * ForEachNode post-order, not using TraversalFlag. + */ +template +auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) -> +typename EnableIf::value, void>::Type +{ + ForEachNode(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 +Node BreadthFirstSearch(Node aRoot, const Condition& aCondition) +{ + if (!aRoot) { + return Node(); + } + + std::queue 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 +Node DepthFirstSearch(Node aRoot, const Condition& aCondition) +{ + Node result = Node(); + + ForEachNode(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 +Node DepthFirstSearchPostOrder(Node aRoot, const Condition& aCondition) +{ + Node result = Node(); + + ForEachNodePostOrder(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 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 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 // 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& aTargets) = 0; + + /** + * Updates any zoom constraints contained in the tag. + * If the |aConstraints| is Nothing() then previously-provided constraints for + * the given |aGuid| are cleared. + */ + virtual void UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe& 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& 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 +#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> 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 mApzcMap; +}; + +class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager) + : mTreeManager(aTreeManager) + { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false); + } + } + + void Unregister() + { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard"); + } + mTreeManager = nullptr; + } + +protected: + virtual ~CheckerboardFlushObserver() {} + +private: + RefPtr 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(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 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 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 &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(mRootNode.get(), + [&state] (HitTestingTreeNode* aNode) + { + state.mNodesToDestroy.AppendElement(aNode); + }); + mRootNode = nullptr; + + if (aRoot) { + std::stack indents; + std::stack ancestorTransforms; + HitTestingTreeNode* parent = nullptr; + HitTestingTreeNode* next = nullptr; + uint64_t layersId = aRootLayerTreeId; + ancestorTransforms.push(Matrix4x4()); + + mApzcTreeLog << "[start]\n"; + LayerMetricsWrapper root(aRoot); + mTreeLock.AssertCurrentThreadOwns(); + + ForEachNode(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 +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 node = aState.mNodesToDestroy[i]; + if (!node->IsPrimaryHolder()) { + aState.mNodesToDestroy.RemoveElement(node); + node->RecycleWith(aApzc, aLayersId); + return node.forget(); + } + } + RefPtr 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 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 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(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 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 +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 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 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 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 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 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 untransformedStartPoint = UntransformBy( + transformToGecko, panInput.mPanStartPoint); + Maybe 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 apzc = GetTargetAPZC(pinchInput.mFocusPoint, + &hitResult); + if (apzc) { + MOZ_ASSERT(hitResult != HitNothing); + + ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc) + * GetApzcToGeckoTransform(apzc); + Maybe 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 apzc = GetTargetAPZC(tapInput.mPoint, + &hitResult); + if (apzc) { + MOZ_ASSERT(hitResult != HitNothing); + + ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc) + * GetApzcToGeckoTransform(apzc); + Maybe 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 +APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent, + nsTArray* aOutTouchBehaviors, + HitTestResult* aOutHitResult) +{ + RefPtr 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 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 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 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(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(*aRefPoint, LDIsScreen); + RefPtr 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 untransformedRefPoint = + UntransformBy(outTransform, refPointAsScreen); + if (untransformedRefPoint) { + *aRefPoint = + ViewAs(*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 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& aTargets) +{ + APZThreadUtils::AssertOnControllerThread(); + + RefPtr target = nullptr; + if (aTargets.Length() > 0) { + target = GetTargetAPZC(aTargets[0]); + } + for (size_t i = 1; i < aTargets.Length(); i++) { + RefPtr apzc = GetTargetAPZC(aTargets[i]); + target = GetMultitouchTarget(target, apzc); + } + mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target); +} + +void +APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget) +{ + APZThreadUtils::AssertOnControllerThread(); + + RefPtr apzc = GetTargetAPZC(aTarget); + mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc); +} + +void +APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, + const Maybe& aConstraints) +{ + MutexAutoLock lock(mTreeLock); + RefPtr 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(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(mRootNode.get(), + [](HitTestingTreeNode* aNode) + { + if (aNode->IsPrimaryHolder()) { + MOZ_ASSERT(aNode->GetApzc()); + aNode->GetApzc()->FlushRepaintForNewInputBlock(); + } + }); +} + +void +APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid) +{ + RefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->CancelAnimation(); + } +} + +void +APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) +{ + MutexAutoLock lock(mTreeLock); + RefPtr 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> nodesToDestroy; + ForEachNode(mRootNode.get(), + [&nodesToDestroy](HitTestingTreeNode* aNode) + { + nodesToDestroy.AppendElement(aNode); + }); + + for (size_t i = 0; i < nodesToDestroy.Length(); i++) { + nodesToDestroy[i]->Destroy(); + } + mRootNode = nullptr; + + RefPtr self(this); + NS_DispatchToMainThread(NS_NewRunnableFunction([self] { + self->mFlushObserver->Unregister(); + self->mFlushObserver = nullptr; + })); +} + +RefPtr +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 startPoint = UntransformBy(transformToApzc, screenStart); + Maybe 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 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 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 target = GetTargetAPZC(aPoint, nullptr); + return target != nullptr; +} + +already_AddRefed +APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid) +{ + MutexAutoLock lock(mTreeLock); + RefPtr node = GetTargetNode(aGuid, nullptr); + MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC + RefPtr apzc = node ? node->GetApzc() : nullptr; + return apzc.forget(); +} + +already_AddRefed +APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid, + GuidComparator aComparator) +{ + mTreeLock.AssertCurrentThreadOwns(); + RefPtr target = DepthFirstSearchPostOrder(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 +APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, + HitTestResult* aOutHitResult, + bool* aOutHitScrollbar) +{ + MutexAutoLock lock(mTreeLock); + HitTestResult hitResult = HitNothing; + ParentLayerPoint point = ViewAs(aPoint, + PixelCastJustification::ScreenIsParentLayerForRoot); + RefPtr 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 +APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr& 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 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 +APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics) +{ + MutexAutoLock lock(mTreeLock); + + return DepthFirstSearch(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 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 hitTestPoints; + hitTestPoints.push(aHitTestPoint); + + ForEachNode(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 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(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(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(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(mRootNode.get(), + [](HitTestingTreeNode* aNode) { + AsyncPanZoomController* apzc = aNode->GetApzc(); + return apzc && apzc->IsRootContent(); + }); + if (resultNode) { + return resultNode->GetApzc(); + } + resultNode = BreadthFirstSearch(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(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(result); +} + +already_AddRefed +APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const +{ + MutexAutoLock lock(mTreeLock); + RefPtr 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 +APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const +{ + mTreeLock.AssertCurrentThreadOwns(); + RefPtr 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 // 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& 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 tag. + * If the |aConstraints| is Nothing() then previously-provided constraints for + * the given |aGuid| are cleared. + */ + void UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe& 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 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& 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 BuildOverscrollHandoffChain(const RefPtr& 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 GetRootNode() const; + already_AddRefed 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 GetTargetAPZC(const ScrollableLayerGuid& aGuid); + already_AddRefed 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 GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; + already_AddRefed CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const; + already_AddRefed GetTouchInputBlockAPZC(const MultiTouchInput& aEvent, + nsTArray* aOutTouchBehaviors, + HitTestResult* aOutHitResult); + nsEventStatus ProcessTouchInput(MultiTouchInput& aInput, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId); + void FlushRepaintsToClearScreenToGeckoTransform(); + + already_AddRefed 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 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 mRootNode; + /* Holds the zoom constraints for scrollable layers, as determined by the + * the main-thread gecko code. */ + std::map 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 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 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 // 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(static_cast(a) + | static_cast(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 +static bool IsZero(const gfx::PointTyped& 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(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& aOverscrollHandoffChain, + bool aFlingIsHandoff, + const RefPtr& 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(¤tX); + mOverScroller->GetCurrY(¤tY); + 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, + RefPtr>(&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& aOverscrollHandoffChain, + bool aFlingIsHandoff /* ignored */, + const RefPtr& 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 mOverscrollHandoffChain; + RefPtr 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 struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace layers { + +class AsyncDragMetrics { + friend struct IPC::ParamTraits; + +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> 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> 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 // for fabsf, fabs, atan2 +#include // for uint32_t, uint64_t +#include // for int32_t +#include // 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 gZoomAnimationFunction; + +/** + * Computed time function used for curving up velocity when it gets high. + */ +StaticAutoPtr 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(&mApzc, + &AsyncPanZoomController::HandleSmoothScrollOverscroll, + velocity)); + return false; + } + + return true; + } + + void SetDestination(const nsPoint& aNewDestination) { + mXAxisModel.SetDestination(static_cast(aNewDestination.x)); + mYAxisModel.SetDestination(static_cast(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& 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(*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(); + } + return mPlatformSpecificState.get(); +} + +already_AddRefed +AsyncPanZoomController::GetGeckoContentController() const { + MonitorAutoLock lock(mRefPtrMonitor); + RefPtr controller = mGeckoContentController; + return controller.forget(); +} + +already_AddRefed +AsyncPanZoomController::GetGestureEventListener() const { + MonitorAutoLock lock(mRefPtrMonitor); + RefPtr listener = mGestureEventListener; + return listener.forget(); +} + +const RefPtr& +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(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 +static CoordTyped GetAxisStart(AsyncDragMetrics::DragDirection aDir, const PointTyped& aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x; + } else { + return aValue.y; + } +} + +template +static CoordTyped GetAxisStart(AsyncDragMetrics::DragDirection aDir, const RectTyped& aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x; + } else { + return aValue.y; + } +} + +template +static IntCoordTyped GetAxisStart(AsyncDragMetrics::DragDirection aDir, const IntRectTyped& aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x; + } else { + return aValue.y; + } +} + +template +static IntCoordTyped GetAxisEnd(AsyncDragMetrics::DragDirection aDir, const IntRectTyped& aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x + aValue.width; + } else { + return aValue.y + aValue.height; + } +} + +template +static CoordTyped GetAxisSize(AsyncDragMetrics::DragDirection aDir, const RectTyped& aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.width; + } else { + return aValue.height; + } +} + +template +static float GetAxisScale(AsyncDragMetrics::DragDirection aDir, const ScaleFactors2D& 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 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 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 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 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 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 controller = GetGeckoContentController()) { + controller->NotifyPinchGesture(aEvent.mType, GetGuid(), + ViewAs(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 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 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 layoutPoint = UntransformBy( + transformScreenToGecko, aPoint); + if (!layoutPoint) { + return false; + } + + *aOut = LayoutDevicePoint(ViewAs(*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 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 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 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 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 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 = + NewRunnableMethod(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 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 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 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 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 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 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 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& aOverscrollHandoffChain, + const RefPtr& 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 animation( + static_cast(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(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 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 controller = GetGeckoContentController(); + if (!controller) { + return; + } + if (!controller->IsRepaintThread()) { + // use the local variable to resolve the function overload. + auto func = static_cast + (&AsyncPanZoomController::RequestContentRepaint); + controller->DispatchToRepaintThread(NewRunnableMethod(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 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>* 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> 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(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 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 + (&AsyncPanZoomController::RequestContentRepaint); + controller->DispatchToRepaintThread( + NewRunnableMethod( + 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 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 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 aTask, int aDelayMs) { + APZThreadUtils::AssertOnControllerThread(); + RefPtr task = aTask; + RefPtr 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(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(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 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 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 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 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& 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 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 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>* 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 mCompositorController; + RefPtr 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 mGeckoContentController; + RefPtr 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 mTreeManager; + + /* Utility functions that return a addrefed pointer to the corresponding fields. */ + already_AddRefed GetGeckoContentController() const; + already_AddRefed 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 mAnimation; + + UniquePtr mOverscrollEffect; + + // Groups state variables that are specific to a platform. + // Initialized on first use. + UniquePtr 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& GetInputQueue() const; + +private: + void CancelAnimationAndGestureState(); + + RefPtr 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& aOverscrollHandoffChain, + const RefPtr& 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 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|). 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&|. This allows the + * function to copy it into the |RefPtr| + * that will store it, while avoiding an unnecessary copy (and thus + * AddRef() and Release()) when passing it. + */ + RefPtr 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 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 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 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 // for fabsf, pow, powf +#include // 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 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 // 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 > 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 // 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 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& 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 +#include "Units.h" +#include + +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& 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 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& aOverscrollHandoffChain, + bool aFlingIsHandedOff, + const RefPtr& 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(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, + RefPtr>(&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 mOverscrollHandoffChain; + RefPtr 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 // for fabsf +#include // 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 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 task = + NewCancelableRunnableMethod(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 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 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 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 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 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& 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 +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 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& 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 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 mLastChild; + RefPtr mPrevSibling; + RefPtr mParent; + + RefPtr 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 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& 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& 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& aTargetApzc) +{ + // note that aTargetApzc MAY be null here. + mTargetApzc = aTargetApzc; + mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : ScreenToParentLayerMatrix4x4(); + mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr); +} + +const RefPtr& +InputBlockState::GetTargetApzc() const +{ + return mTargetApzc; +} + +const RefPtr& +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& 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& 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& 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 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& 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 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 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 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 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& 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& 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 apzc = + mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent); + + if (apzc && apzc != GetTargetApzc()) { + UpdateTargetApzc(apzc); + } + } +} + +bool +PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr& 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 apzc = aTargetApzc; + if (apzc && aFirstInput) { + RefPtr 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& 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& 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& 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 +{ +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& aTargetApzc, + bool aTargetConfirmed); + virtual ~InputBlockState() + {} + + virtual bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState, + InputData* aFirstInput); + const RefPtr& GetTargetApzc() const; + const RefPtr& 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& 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 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 mScrolledApzc; +protected: + RefPtr 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& 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& aTargetApzc, + bool aTargetConfirmed, + const ScrollWheelInput& aEvent); + + bool SetContentResponse(bool aPreventDefault) override; + bool MustStayActive() override; + const char* Type() override; + bool SetConfirmedTargetApzc(const RefPtr& 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& 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& 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& 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& 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& 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& aBehaviors); + /** + * If the allowed touch behaviors have been set, populate them into + * |aOutBehaviors| and return true. Else, return false. + */ + bool GetAllowedTouchBehaviors(nsTArray& 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 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& 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& aTarget, + bool aTargetConfirmed, + const MultiTouchInput& aEvent, + uint64_t* aOutInputBlockId) { + TouchBlockState* block = nullptr; + if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) { + nsTArray 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 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(aEvent, *block)); + ProcessQueue(); + return result; +} + +nsEventStatus +InputQueue::ReceiveMouseInput(const RefPtr& 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(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& 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(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 horizontallyScrollableAPZC = + aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent); + return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc(); +} + +nsEventStatus +InputQueue::ReceivePanGestureInput(const RefPtr& 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(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& 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& 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& aTarget, + CancelableBlockState* aBlock) { + INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get()); + aBlock->StartContentResponseTimer(); + aTarget->PostDelayedTask(NewRunnableMethod(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& 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& 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& 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 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& 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& 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& aTargetApzc); + /** + * This function is invoked to confirm that the drag block should be handled + * by the APZ. + */ + void ConfirmDragBlock(uint64_t aInputBlockId, + const RefPtr& 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& 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& 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& aTarget, + CancelableBlockState* aBlock); + + nsEventStatus ReceiveTouchInput(const RefPtr& aTarget, + bool aTargetConfirmed, + const MultiTouchInput& aEvent, + uint64_t* aOutInputBlockId); + nsEventStatus ReceiveMouseInput(const RefPtr& aTarget, + bool aTargetConfirmed, + const MouseInput& aEvent, + uint64_t* aOutInputBlockId); + nsEventStatus ReceiveScrollWheelInput(const RefPtr& aTarget, + bool aTargetConfirmed, + const ScrollWheelInput& aEvent, + uint64_t* aOutInputBlockId); + nsEventStatus ReceivePanGestureInput(const RefPtr& 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& aTarget, + CancelableBlockState* aBlock); + void MainThreadTimeout(uint64_t aInputBlockId); + void ProcessQueue(); + bool CanDiscardBlock(CancelableBlockState* aBlock); + void UpdateActiveApzc(const RefPtr& 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> 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 mActiveTouchBlock; + RefPtr mActiveWheelBlock; + RefPtr mActiveDragBlock; + RefPtr mActivePanGestureBlock; + + // The APZC to which the last event was delivered + RefPtr 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 controller = mApzc.GetGeckoContentController(); + if (controller && (aShouldOverscrollX || aShouldOverscrollY)) { + controller->UpdateOverscrollOffset(aOverscroll.x, aOverscroll.y, mApzc.IsRootContent()); + aOverscroll = ParentLayerPoint(); + } + } + + void HandleFlingOverscroll(const ParentLayerPoint& aVelocity) override { + RefPtr 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 // 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& a, + const RefPtr& 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& +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 +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 +#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 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& 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 FindFirstScrollable(const InputData& aInput) const; + +private: + std::vector> 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 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 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(aInput)) + , mBlock(&aBlock) +{ +} + +QueuedInput::QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock) + : mInput(MakeUnique(aInput)) + , mBlock(&aBlock) +{ +} + +QueuedInput::QueuedInput(const MouseInput& aInput, DragBlockState& aBlock) + : mInput(MakeUnique(aInput)) + , mBlock(&aBlock) +{ +} + +QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock) + : mInput(MakeUnique(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 mInput; + // A pointer to the block that the input event is associated with. This must + // be non-null. + RefPtr 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 tm; + RefPtr 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 : layers) { + if (TestAsyncPanZoomController* apzc = ApzcOf(layer)) { + apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); + } + } + } + + nsTArray > layers; + RefPtr lm; + RefPtr root; + + RefPtr 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( + 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 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 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 aTask, int aDelayMs) { + RefPtr task = aTask; + } + bool IsRepaintThread() { + return NS_IsMainThread(); + } + void DispatchToRepaintThread(already_AddRefed 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 aTask, int aDelayMs) { + RefPtr 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, 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, 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, TimeStamp>> mTaskQueue; + TimeStamp mTime; +}; + +class TestAPZCTreeManager : public APZCTreeManager { +public: + explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc) : mcc(aMcc) {} + + RefPtr 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 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 target = this; + GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target); + } + + void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray& 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(); + } + + template + void Tap(const RefPtr& aTarget, const ScreenIntPoint& aPoint, + TimeDuration aTapLength, + nsEventStatus (*aOutEventStatuses)[2] = nullptr, + uint64_t* aOutInputBlockId = nullptr); + + template + void TapAndCheckStatus(const RefPtr& aTarget, + const ScreenIntPoint& aPoint, TimeDuration aTapLength); + + template + void Pan(const RefPtr& aTarget, + const ScreenIntPoint& aTouchStart, + const ScreenIntPoint& aTouchEnd, + bool aKeepFingerDown = false, + nsTArray* 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 + void Pan(const RefPtr& aTarget, int aTouchStartY, + int aTouchEndY, bool aKeepFingerDown = false, + nsTArray* 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 + void PanAndCheckStatus(const RefPtr& aTarget, int aTouchStartY, + int aTouchEndY, + bool aExpectConsumed, + nsTArray* aAllowedTouchBehaviors, + uint64_t* aOutInputBlockId = nullptr); + + void ApzcPanNoFling(const RefPtr& aApzc, + int aTouchStartY, + int aTouchEndY, + uint64_t* aOutInputBlockId = nullptr); + + template + void DoubleTap(const RefPtr& aTarget, + const ScreenIntPoint& aPoint, + nsEventStatus (*aOutEventStatuses)[4] = nullptr, + uint64_t (*aOutInputBlockIds)[2] = nullptr); + + template + void DoubleTapAndCheckStatus(const RefPtr& aTarget, + const ScreenIntPoint& aPoint, + uint64_t (*aOutInputBlockIds)[2] = nullptr); + +protected: + RefPtr mcc; +}; + +template +void +APZCTesterBase::Tap(const RefPtr& 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 +void +APZCTesterBase::TapAndCheckStatus(const RefPtr& 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 +void +APZCTesterBase::Pan(const RefPtr& aTarget, + const ScreenIntPoint& aTouchStart, + const ScreenIntPoint& aTouchEnd, + bool aKeepFingerDown, + nsTArray* 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 +void +APZCTesterBase::Pan(const RefPtr& aTarget, + int aTouchStartY, int aTouchEndY, bool aKeepFingerDown, + nsTArray* aAllowedTouchBehaviors, + nsEventStatus (*aOutEventStatuses)[4], + uint64_t* aOutInputBlockId) +{ + Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY), + aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId); +} + +template +void +APZCTesterBase::PanAndCheckStatus(const RefPtr& aTarget, + int aTouchStartY, + int aTouchEndY, + bool aExpectConsumed, + nsTArray* 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& aApzc, + int aTouchStartY, int aTouchEndY, + uint64_t* aOutInputBlockId) +{ + Pan(aApzc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId); + aApzc->CancelAnimation(); +} + +template +void +APZCTesterBase::DoubleTap(const RefPtr& 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 +void +APZCTesterBase::DoubleTapAndCheckStatus(const RefPtr& 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(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& 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 +void +SetDefaultAllowedTouchBehavior(const RefPtr& aTarget, + uint64_t aInputBlockId, + int touchPoints = 1) +{ + nsTArray 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 +nsEventStatus +TouchDown(const RefPtr& 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 +nsEventStatus +TouchMove(const RefPtr& 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 +nsEventStatus +TouchUp(const RefPtr& 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 +void +PinchWithPinchInput(const RefPtr& 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 +void +PinchWithPinchInputAndCheckStatus(const RefPtr& 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 +void +PinchWithTouchInput(const RefPtr& aTarget, + const ScreenIntPoint& aFocus, float aScale, + int& inputId, + nsTArray* 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 +void +PinchWithTouchInputAndCheckStatus(const RefPtr& aTarget, + const ScreenIntPoint& aFocus, float aScale, + int& inputId, bool aShouldTriggerPinch, + nsTArray* 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 +nsEventStatus +Wheel(const RefPtr& 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 +nsEventStatus +SmoothWheel(const RefPtr& 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 +nsEventStatus +MouseDown(const RefPtr& 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 +nsEventStatus +MouseMove(const RefPtr& 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 +nsEventStatus +MouseUp(const RefPtr& 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 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 > layers; + RefPtr lm; + RefPtr 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 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 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(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(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(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(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(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 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 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 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 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 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 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 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 allowedTouchBehaviors; + allowedTouchBehaviors.AppendElement(aBehavior); + apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors); + } + // Have content "respond" to the touchstart + apzc->ContentReceivedInputBlock(blockId, false); + + MockFunction 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 allowedTouchBehaviors; + allowedTouchBehaviors.AppendElement(aBehavior); + apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors); + } + // Have content "respond" to the touchstart + apzc->ContentReceivedInputBlock(blockId, false); + + MockFunction 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 GetTargetAPZC(const ScreenPoint& aPoint) { + RefPtr 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 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 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 root = manager->GetRootNode(); + RefPtr node5 = root->GetLastChild(); + RefPtr node4 = node5->GetPrevSibling(); + RefPtr node2 = node4->GetPrevSibling(); + RefPtr node1 = node2->GetPrevSibling(); + RefPtr node3 = node2->GetLastChild(); + RefPtr node9 = node5->GetLastChild(); + RefPtr node8 = node9->GetPrevSibling(); + RefPtr node6 = node8->GetPrevSibling(); + RefPtr 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 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 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 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 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 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 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 *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 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 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 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 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 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(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(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(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 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 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 parent = ApzcOf(root); + RefPtr 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 parent1 = ApzcOf(layers[1]); + RefPtr child1 = ApzcOf(layers[2]); + RefPtr parent2 = ApzcOf(layers[3]); + RefPtr 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 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 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 parentApzc = ApzcOf(root); + RefPtr 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 parentApzc = ApzcOf(root); + RefPtr 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 registration = MakeUnique(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 root = manager->GetRootNode(); + RefPtr node2 = root->GetFirstChild()->GetFirstChild(); + RefPtr 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 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. +// 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 @@ + + + + + + Sanity panning test + + + + + + +
    + This div makes the page scrollable. +
    + + 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 @@ + + + + + + Test for Bug 1151663, helper page + + + + + Mozilla Bug 1151663 +
    + +
    +
    + +
    + + 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 @@ + + + + + + Test for touchend on media elements + + + + + + + +

    Tap on the colored boxes to hide them.

    + + +
    + + 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 @@ + + Ensure that the hit region doesn't get unexpectedly expanded + + + + + + + +Some text + +
    +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.
    +0
    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +202
    +203
    +204
    +205
    +206
    +207
    +208
    +209
    +210
    +211
    +212
    +213
    +214
    +215
    +216
    +217
    +218
    +219
    +220
    +221
    +222
    +223
    +224
    +225
    +226
    +227
    +228
    +229
    +230
    +231
    +232
    +233
    +234
    +235
    +236
    +237
    +238
    +239
    +240
    +241
    +242
    +243
    +244
    +245
    +246
    +247
    +248
    +249
    +250
    +251
    +252
    +253
    +254
    +255
    +256
    +257
    +258
    +259
    +260
    +261
    +262
    +263
    +264
    +265
    +266
    +267
    +268
    +269
    +270
    +271
    +272
    +273
    +274
    +275
    +276
    +277
    +278
    +279
    +280
    +281
    +282
    +283
    +284
    +285
    +286
    +287
    +288
    +289
    +290
    +291
    +292
    +293
    +294
    +295
    +296
    +297
    +298
    +299
    +300
    +301
    +302
    +303
    +304
    +305
    +306
    +307
    +308
    +309
    +310
    +311
    +312
    +313
    +314
    +315
    +316
    +317
    +318
    +319
    +320
    +321
    +322
    +323
    +324
    +325
    +326
    +327
    +328
    +329
    +330
    +331
    +332
    +333
    +334
    +335
    +336
    +337
    +338
    +339
    +340
    +341
    +342
    +343
    +344
    +345
    +346
    +347
    +348
    +349
    +350
    +351
    +352
    +353
    +354
    +355
    +356
    +357
    +358
    +359
    +360
    +361
    +362
    +363
    +364
    +365
    +366
    +367
    +368
    +369
    +370
    +371
    +372
    +373
    +374
    +375
    +376
    +377
    +378
    +379
    +380
    +381
    +382
    +383
    +384
    +385
    +386
    +387
    +388
    +389
    +390
    +391
    +392
    +393
    +394
    +395
    +396
    +397
    +398
    +399
    +400
    +401
    +402
    +403
    +404
    +405
    +406
    +407
    +408
    +409
    +410
    +411
    +412
    +413
    +414
    +415
    +416
    +417
    +418
    +419
    +420
    +421
    +422
    +423
    +424
    +425
    +426
    +427
    +428
    +429
    +430
    +431
    +432
    +433
    +434
    +435
    +436
    +437
    +438
    +439
    +440
    +441
    +442
    +443
    +444
    +445
    +446
    +447
    +448
    +449
    +450
    +451
    +452
    +453
    +454
    +455
    +456
    +457
    +458
    +459
    +460
    +461
    +462
    +463
    +464
    +465
    +466
    +467
    +468
    +469
    +470
    +471
    +472
    +473
    +474
    +475
    +476
    +477
    +478
    +479
    +480
    +481
    +482
    +483
    +484
    +485
    +486
    +487
    +488
    +489
    +490
    +491
    +492
    +493
    +494
    +495
    +496
    +497
    +498
    +499
    +
    +
    this div makes the page scrollable
    + 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 @@ + + + + + + + Test for bug 1280013 + + + + + + + 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. + + + 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 @@ + + + + + + Test pointer events are dispatched once for touch tap + + + + + + +
    + + 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 @@ + + + + + + Test pointer events are dispatched once for touch tap + + + + + + +
    + + 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 @@ + + + + + + + Test for Bug 982141, helper page + + + + + Mozilla Bug 982141 + +
    +
    + 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. +
    + Line 1
    + Line 2
    + Line 3
    + Line 4
    + Line 5
    + Line 6
    + Line 7
    + Line 8
    + Line 9
    + Line 10
    + Line 11
    + Line 12
    + Line 13
    + Line 14
    + Line 15
    + Line 16
    + Line 17
    + Line 18
    + Line 19
    + Line 20
    + Line 21
    + Line 22
    + Line 23
    + Line 24
    + Line 25
    + Line 26
    + Line 27
    + Line 28
    + Line 29
    + Line 30
    + Line 31
    + Line 32
    + Line 33
    + Line 34
    + Line 35
    + Line 36
    + Line 37
    + Line 38
    + Line 39
    + Line 40
    + Line 41
    + Line 42
    + Line 43
    + Line 44
    + Line 45
    + Line 46
    + Line 40
    + Line 48
    + Line 49
    + Line 50
    +
    + + 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 @@ + + + + + + Sanity mouse-clicking test + + + + + + + + + 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 @@ + + + + + + Sanity panning test for scrollable div + + + + + + +
    +
    + This div makes the |outer| div scrollable. +
    +
    +
    + This div makes the top-level page scrollable. +
    + + 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 @@ + + + + + + Sanity mouse-drag click test + + + + + + + + + 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 @@ + + + + + + Dragging the mouse on a content-implemented scrollbar + + + + + + + + +
    Drag up and down on this bar. The background/scrollbar shouldn't glitch
    +This is a tall page
    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +202
    +203
    +204
    +205
    +206
    +207
    +208
    +209
    +210
    +211
    +212
    +213
    +214
    +215
    +216
    +217
    +218
    +219
    +220
    +221
    +222
    +223
    +224
    +225
    +226
    +227
    +228
    +229
    +230
    +231
    +232
    +233
    +234
    +235
    +236
    +237
    +238
    +239
    +240
    +241
    +242
    +243
    +244
    +245
    +246
    +247
    +248
    +249
    +250
    +251
    +252
    +253
    +254
    +255
    +256
    +257
    +258
    +259
    +260
    +261
    +262
    +263
    +264
    +265
    +266
    +267
    +268
    +269
    +270
    +271
    +272
    +273
    +274
    +275
    +276
    +277
    +278
    +279
    +280
    +281
    +282
    +283
    +284
    +285
    +286
    +287
    +288
    +289
    +290
    +291
    +292
    +293
    +294
    +295
    +296
    +297
    +298
    +299
    +300
    +301
    +302
    +303
    +304
    +305
    +306
    +307
    +308
    +309
    +310
    +311
    +312
    +313
    +314
    +315
    +316
    +317
    +318
    +319
    +320
    +321
    +322
    +323
    +324
    +325
    +326
    +327
    +328
    +329
    +330
    +331
    +332
    +333
    +334
    +335
    +336
    +337
    +338
    +339
    +340
    +341
    +342
    +343
    +344
    +345
    +346
    +347
    +348
    +349
    +350
    +351
    +352
    +353
    +354
    +355
    +356
    +357
    +358
    +359
    +360
    +361
    +362
    +363
    +364
    +365
    +366
    +367
    +368
    +369
    +370
    +371
    +372
    +373
    +374
    +375
    +376
    +377
    +378
    +379
    +380
    +381
    +382
    +383
    +384
    +385
    +386
    +387
    +388
    +389
    +390
    +391
    +392
    +393
    +394
    +395
    +396
    +397
    +398
    +399
    +400
    +401
    +402
    +403
    +404
    +405
    +406
    +407
    +408
    +409
    +410
    +411
    +412
    +413
    +414
    +415
    +416
    +417
    +418
    +419
    +420
    +421
    +422
    +423
    +424
    +425
    +426
    +427
    +428
    +429
    +430
    +431
    +432
    +433
    +434
    +435
    +436
    +437
    +438
    +439
    +440
    +441
    +442
    +443
    +444
    +445
    +446
    +447
    +448
    +449
    +450
    +451
    +452
    +453
    +454
    +455
    +456
    +457
    +458
    +459
    +460
    +461
    +462
    +463
    +464
    +465
    +466
    +467
    +468
    +469
    +470
    +471
    +472
    +473
    +474
    +475
    +476
    +477
    +478
    +479
    +480
    +481
    +482
    +483
    +484
    +485
    +486
    +487
    +488
    +489
    +490
    +491
    +492
    +493
    +494
    +495
    +496
    +497
    +498
    +499
    + + + 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 @@ + + + + + + + +
    +
    +
    + + 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 @@ + + + + + + + +
    +
    +
    + + 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 @@ + + + + + + Sanity panning test for scrollable div + + + + + + + +
    + This div makes the top-level page scrollable. +
    + + 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 @@ + + + + + + Ensure we get a touch-cancel after a contextmenu comes up + + + + + + + Link to nowhere + + 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 @@ + + + Wheel-scrolling over inactive subframe with perspective + + + + + + + +
    +
    +
    +
    + + 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 @@ + + + Wheel-scrolling over inactive subframe with z-index + + + + + + + +
    +
    +
    +
    + + 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 @@ + + + Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes + + + + + + +
    sticky
    +
    fixed
    + + +
    +
    scrollable content inside a fixed-pos item
    +
    + + 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 @@ + + + + + + Sanity touch-tapping test + + + + + + +
    spacer
    + + + 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 @@ + + +This is a tall page
    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +202
    +203
    +204
    +205
    +206
    +207
    +208
    +209
    +210
    +211
    +212
    +213
    +214
    +215
    +216
    +217
    +218
    +219
    +220
    +221
    +222
    +223
    +224
    +225
    +226
    +227
    +228
    +229
    +230
    +231
    +232
    +233
    +234
    +235
    +236
    +237
    +238
    +239
    +240
    +241
    +242
    +243
    +244
    +245
    +246
    +247
    +248
    +249
    +250
    +251
    +252
    +253
    +254
    +255
    +256
    +257
    +258
    +259
    +260
    +261
    +262
    +263
    +264
    +265
    +266
    +267
    +268
    +269
    +270
    +271
    +272
    +273
    +274
    +275
    +276
    +277
    +278
    +279
    +280
    +281
    +282
    +283
    +284
    +285
    +286
    +287
    +288
    +289
    +290
    +291
    +292
    +293
    +294
    +295
    +296
    +297
    +298
    +299
    +300
    +301
    +302
    +303
    +304
    +305
    +306
    +307
    +308
    +309
    +310
    +311
    +312
    +313
    +314
    +315
    +316
    +317
    +318
    +319
    +320
    +321
    +322
    +323
    +324
    +325
    +326
    +327
    +328
    +329
    +330
    +331
    +332
    +333
    +334
    +335
    +336
    +337
    +338
    +339
    +340
    +341
    +342
    +343
    +344
    +345
    +346
    +347
    +348
    +349
    +350
    +351
    +352
    +353
    +354
    +355
    +356
    +357
    +358
    +359
    +360
    +361
    +362
    +363
    +364
    +365
    +366
    +367
    +368
    +369
    +370
    +371
    +372
    +373
    +374
    +375
    +376
    +377
    +378
    +379
    +380
    +381
    +382
    +383
    +384
    +385
    +386
    +387
    +388
    +389
    +390
    +391
    +392
    +393
    +394
    +395
    +396
    +397
    +398
    +399
    +400
    +401
    +402
    +403
    +404
    +405
    +406
    +407
    +408
    +409
    +410
    +411
    +412
    +413
    +414
    +415
    +416
    +417
    +418
    +419
    +420
    +421
    +422
    +423
    +424
    +425
    +426
    +427
    +428
    +429
    +430
    +431
    +432
    +433
    +434
    +435
    +436
    +437
    +438
    +439
    +440
    +441
    +442
    +443
    +444
    +445
    +446
    +447
    +448
    +449
    +450
    +451
    +452
    +453
    +454
    +455
    +456
    +457
    +458
    +459
    +460
    +461
    +462
    +463
    +464
    +465
    +466
    +467
    +468
    +469
    +470
    +471
    +472
    +473
    +474
    +475
    +476
    +477
    +478
    +479
    +480
    +481
    +482
    +483
    +484
    +485
    +486
    +487
    +488
    +489
    +490
    +491
    +492
    +493
    +494
    +495
    +496
    +497
    +498
    +499
    + + 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 @@ + + + + + + Sanity touch-tapping test + + + + + + + + + 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 @@ + + + + + + Sanity touch-tapping test with fullzoom + + + + + + + + + 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 @@ + + + + + + Ensure APZ doesn't wait for passive listeners + + + + + + + Link to nowhere + + 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 @@ + + + + + + Sanity touch-action test + + + + + + +
    + This div makes the page scrollable on both axes.
    + This is the second line of text.
    + This is the third line of text.
    + This is the fourth line of text. +
    + +
    + + 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 @@ + + + + + + Complex touch-action test + + + + + + +
    +
    +
    + + + + +
    + + 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 @@ + + + + + + Test to ensure APZ doesn't always wait for touch-action + + + + + + +
    +
    + This is a colored div that will move on the screen as the scroller scrolls. +
    +
    + This is a large div to make the scroller scrollable. +
    + + 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 @@ + + + + + + Test for Bug 1151663 + + + + + + + Mozilla Bug 1151663 + + 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 @@ + + + + + Test for Bug 1151667 + + + + + + + + + +Mozilla Bug 1151667 +

    +
    + +
    +
    + +
    +
    +
    +
    + + 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 @@ + + + + + Test to ensure non-scrollable frames don't get layerized + + + + + + + + +

    +
    +
    sample code here
    +
    spacer to make the 'container' div the root scrollable element
    +
    +
    +
    +
    + + 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 @@ + + + + + + Test for Bug 1277814 + + + + + + + + + + +
    + CoolCmd
    CoolCmd
    CoolCmd
    CoolCmd
    + CoolCmd
    CoolCmd
    CoolCmd
    CoolCmd
    + CoolCmd
    CoolCmd
    CoolCmd
    CoolCmd
    + CoolCmd
    CoolCmd
    CoolCmd
    CoolCmd
    + CoolCmd
    CoolCmd
    CoolCmd
    CoolCmd
    + + + 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 @@ + + + + + + Test for Bug 1285070 + + + + + + + + +
    +
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    +
    +
    + + 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 @@ + + + + + + Test for Bug 1285070 + + + + + + + + +
    +
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    + this is some scrollable text.
    + this is a second line to make the scrolling more obvious.
    + and a third for good measure.
    +
    +
    + + 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 @@ + + + + + + Test for Bug 982141 + + + + + + + Mozilla Bug 982141 + + 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 @@ + + + + + Test for bug 1235899 + + + + + + + + + +Mozilla Bug 1235899 +

    +
    +

    You should be able to fling this list without it stopping abruptly

    +
    +
    +
      +
    1. Some text
    2. +
    3. Some text
    4. +
    5. Some text
    6. +
    7. Some text
    8. +
    9. Some text
    10. +
    11. Some text
    12. +
    13. Some text
    14. +
    15. Some text
    16. +
    17. Some text
    18. +
    19. Some text
    20. +
    21. Some text
    22. +
    23. Some text
    24. +
    25. Some text
    26. +
    27. Some text
    28. +
    29. Some text
    30. +
    31. Some text
    32. +
    33. Some text
    34. +
    35. Some text
    36. +
    37. Some text
    38. +
    39. Some text
    40. +
    41. Some text
    42. +
    43. Some text
    44. +
    45. Some text
    46. +
    47. Some text
    48. +
    49. Some text
    50. +
    51. Some text
    52. +
    53. Some text
    54. +
    55. Some text
    56. +
    57. Some text
    58. +
    59. Some text
    60. +
    61. Some text
    62. +
    63. Some text
    64. +
    65. Some text
    66. +
    67. Some text
    68. +
    69. Some text
    70. +
    71. Some text
    72. +
    73. Some text
    74. +
    75. Some text
    76. +
    77. Some text
    78. +
    79. Some text
    80. +
    81. Some text
    82. +
    83. Some text
    84. +
    85. Some text
    86. +
    87. Some text
    88. +
    89. Some text
    90. +
    91. Some text
    92. +
    93. Some text
    94. +
    95. Some text
    96. +
    97. Some text
    98. +
    99. Some text
    100. +
    101. Some text
    102. +
    103. Some text
    104. +
    105. Some text
    106. +
    107. Some text
    108. +
    109. Some text
    110. +
    111. Some text
    112. +
    113. Some text
    114. +
    115. Some text
    116. +
    117. Some text
    118. +
    119. Some text
    120. +
    121. Some text
    122. +
    123. Some text
    124. +
    125. Some text
    126. +
    127. Some text
    128. +
    129. Some text
    130. +
    131. Some text
    132. +
    133. Some text
    134. +
    135. Some text
    136. +
    137. Some text
    138. +
    139. Some text
    140. +
    141. Some text
    142. +
    143. Some text
    144. +
    145. Some text
    146. +
    147. Some text
    148. +
    149. Some text
    150. +
    151. Some text
    152. +
    153. Some text
    154. +
    155. Some text
    156. +
    157. Some text
    158. +
    159. Some text
    160. +
    161. Some text
    162. +
    163. Some text
    164. +
    165. Some text
    166. +
    167. Some text
    168. +
    169. Some text
    170. +
    171. Some text
    172. +
    173. Some text
    174. +
    175. Some text
    176. +
    177. Some text
    178. +
    179. Some text
    180. +
    181. Some text
    182. +
    183. Some text
    184. +
    185. Some text
    186. +
    187. Some text
    188. +
    189. Some text
    190. +
    191. Some text
    192. +
    193. Some text
    194. +
    195. Some text
    196. +
    197. Some text
    198. +
    199. Some text
    200. +
    201. Some text
    202. +
    203. Some text
    204. +
    +
    +
    +
    + +
    +
    +
    +
    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 @@
    +
    +
    +
    +  
    +  Various mouse tests that spawn in new windows
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    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 @@
    +
    +
    +
    +
    +  
    +  Test for Bug 1285070
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    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 @@
    +
    +
    +
    +  
    +  Various touch tests that spawn in new windows
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    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 @@
    +
    +
    +
    +  
    +  Various wheel-scrolling tests that spawn in new windows
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    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 @@
    +
    +
    +
    +  
    +  Various zoom-related tests that spawn in new windows
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    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 @@
    +
    +
    + 
    + 
    +  Test for bug 1292781
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    + 
    + 
    +Mozilla Bug 1292781
    +

    +
    +

    The frame reconstruction should not leave this scrollframe in a bad state

    +
    +
    + this is the top of the scrollframe. +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    + this is near the top of the scrollframe. +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    + this is near the bottom of the scrollframe. +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    +
    this is a box
    + this is the bottom of the scrollframe. +
    +
    +
    + +
    +
    +
    +
    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 @@
    +
    +
    +
    +
    +  Test for layerization
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +APZ layerization tests
    +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + 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 @@ + + + + Test scrolling flattened inactive frames + + + + + + + + +
    +
    +
    +
    +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    + +

    +
    + + + 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 @@ + + + + Test scrolling flattened inactive frames + + + + + + + +
    +
    +
    +
    +
    +
    + + + 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 @@ + + + + Test scrolling subframe scrollbars + + + + + + + + +

    +1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +

    + + + 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 @@ + + + Test Frame Uniformity While Scrolling + + + + + + + + + +
    +
    + + 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 @@ + + + + + Test for Bug 1203140 + + + + + + + + + +Mozilla Bug 1203140 +

    +
    +

    The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.

    +
    +
    Div to make 'content' scrollable
    +
    +
    +
    +
    + + + 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 @@ + + + + + Test for Bug 1013412 + + + + + + + + + +Mozilla Bug 1161206 +

    +
    +

    Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.

    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + 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 @@ + + + + + Test for Bug 1175585 + + + + + + + + + +APZ wheel transactions test +

    +
    +
    +
    +
    +
    +
    +
    +
    + + + 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 @@ + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + + +
    + + + 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 @@ + + + + + + +
    + + + 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 @@ + + + + +
    + This is the top of the page. +
    + This is the bottom of the page. + 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 @@ + + + + + +
    + This is the top of the page. +
    + This is the bottom of the page. + 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 @@ + + + + + +This tests that an initial-scale of 0 (i.e. garbage) is overridden
    +with something a little more sane. + + + 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 @@ + + + + + +This tests that an initial-scale of 0 (i.e. garbage) is overridden
    +with something a little more sane. + + + 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 + static void ConvertMap(const std::map& aFrom, + dom::Sequence& 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 + +#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; + 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 InsertResultT; + DebugOnly 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 ScrollFrameDataBase; + struct ScrollFrameData : ScrollFrameDataBase {}; + typedef std::map BucketBase; + struct Bucket : BucketBase {}; + typedef std::map 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 + 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 +{ + 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 + : ParamTraits {}; + +template <> +struct ParamTraits + : ParamTraits {}; + +template <> +struct ParamTraits + : ParamTraits {}; + +} // 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); + } +} + +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 +GetPresShell(const nsIContent* aContent) +{ + nsCOMPtr 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); +} + +void +APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics) +{ + if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { + return; + } + nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); + if (!content) { + return; + } + + nsCOMPtr 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 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 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 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& 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* 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 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& 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& 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 mPresShell; + uint64_t mInputBlockId; + nsTArray 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& 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 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 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 targetContent = nsLayoutUtils::FindContentFor(aScrollId); + if (!targetContent) { + return; + } + nsCOMPtr 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 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& 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& 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 struct already_AddRefed; +template class nsCOMPtr; + +namespace mozilla { +namespace layers { + +typedef function&)> + 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& 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& 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& 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 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 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 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 timer = do_CreateInstance(NS_TIMER_CONTRACTID); + RefPtr 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& aPresShell, + const CSSPoint& aPoint, + const CSSToLayoutDeviceScale& aScale, + Modifiers aModifiers, + const nsCOMPtr& 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& 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 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& aPresShell, + const CSSPoint& aPoint, + const CSSToLayoutDeviceScale& aScale, + Modifiers aModifiers) +{ +#ifdef XP_WIN + nsCOMPtr 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 docshell(doc ? doc->GetDocShell() : nullptr); + if (docshell && sf) { + nsDocShell* nsdocshell = static_cast(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 docshell(doc ? doc->GetDocShell() : nullptr); + if (docshell && sf) { + nsDocShell* nsdocshell = static_cast(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 +APZEventState::GetWidget() const +{ + nsCOMPtr 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 + +#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 nsCOMPtr; +class nsIDocument; +class nsIPresShell; +class nsIWidget; + +namespace mozilla { +namespace layers { + +class ActiveElementManager; + +typedef function + 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& aUtils, + const CSSPoint& aPoint, + const CSSToLayoutDeviceScale& aScale, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId); + void ProcessLongTapUp(const nsCOMPtr& 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& aPresShell, + const CSSPoint& aPoint, + const CSSToLayoutDeviceScale& aScale, + Modifiers aModifiers, + const nsCOMPtr& aWidget); + already_AddRefed GetWidget() const; +private: + nsWeakPtr mWidget; + RefPtr 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 aTask) +{ + RefPtr 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 aTask); + + /** + * Returns true if currently on APZ "controller thread". + */ + static bool IsControllerThread(); +}; + +// A base class for GenericTimerCallback. +// 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 +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 +GenericTimerCallback* NewTimerCallback(const Function& aFunction) +{ + return new GenericTimerCallback(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 task = + NewCancelableRunnableMethod>(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& 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 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 mSetActiveTask; + /** + * See ActiveElementUsesStyle() documentation. + */ + bool mActiveElementUsesStyle; + + // Helpers + void TriggerElementActivation(); + void SetActive(dom::Element* aTarget); + void ResetActive(); + void ResetTouchBlockState(); + void SetActiveTask(const nsCOMPtr& 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::sInstance; + +/*static*/ already_AddRefed +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 instance = sInstance.get(); + return instance.forget(); +} + +void +CheckerboardEventStorage::Report(uint32_t aSeverity, const std::string& aLog) +{ + if (!NS_IsMainThread()) { + RefPtr 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 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& 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::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv) +{ + RefPtr ces = new CheckerboardReportService(aGlobal.GetAsSupports()); + return ces.forget(); +} + +CheckerboardReportService::CheckerboardReportService(nsISupports* aParent) + : mParent(aParent) +{ +} + +JSObject* +CheckerboardReportService::WrapObject(JSContext* aCtx, JS::Handle aGivenProto) +{ + return CheckerboardReportServiceBinding::Wrap(aCtx, this, aGivenProto); +} + +nsISupports* +CheckerboardReportService::GetParentObject() +{ + return mParent; +} + +void +CheckerboardReportService::GetReports(nsTArray& aOutReports) +{ + RefPtr 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 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 + +#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 GetInstance(); + + /** + * Get the stored checkerboard reports. + */ + void GetReports(nsTArray& 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 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 + Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv); + + explicit CheckerboardReportService(nsISupports* aSupports); + + JSObject* WrapObject(JSContext* aCtx, JS::Handle 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& aOutReports); + bool IsRecordingEnabled() const; + void SetRecordingEnabled(bool aEnabled); + void FlushActiveReports(); + +private: + virtual ~CheckerboardReportService() {} + + nsCOMPtr 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 aTask, int aDelayMs) +{ + MessageLoop::current()->PostDelayedTask(Move(aTask), aDelayMs); +} + +bool +ChromeProcessController::IsRepaintThread() +{ + return NS_IsMainThread(); +} + +void +ChromeProcessController::DispatchToRepaintThread(already_AddRefed 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 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(this, + &ChromeProcessController::HandleTap, + aType, aPoint, aModifiers, aGuid, aInputBlockId)); + return; + } + + if (!mAPZEventState) { + return; + } + + nsCOMPtr 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 + (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 + (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 + (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 aTask, int aDelayMs) override; + virtual bool IsRepaintThread() override; + virtual void DispatchToRepaintThread(already_AddRefed 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 mWidget; + RefPtr mAPZEventState; + RefPtr 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 tabChild(do_QueryInterface(aSubject)); + NS_ENSURE_TRUE(tabChild, NS_ERROR_FAILURE); + + dom::TabChild* browser = static_cast(tabChild.get()); + + if (browser->GetTabId() == mTabId) { + mController->SetBrowser(browser); + } + return NS_OK; +} + +APZChild* +ContentProcessController::Create(const dom::TabId& aTabId) +{ + RefPtr browser = dom::TabChild::FindTabChild(aTabId); + + ContentProcessController* controller = new ContentProcessController(); + + nsAutoPtr apz(new APZChild(controller)); + + if (browser) { + + controller->SetBrowser(browser); + + } else { + + RefPtr observer = + new TabChildCreatedObserver(controller, aTabId); + nsCOMPtr 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 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 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 shell; + if (nsCOMPtr doc = mBrowser->GetDocument()) { + shell = doc->GetShell(); + } + APZCCallbackHelper::NotifyFlushComplete(shell.get()); + } +} + +void +ContentProcessController::PostDelayedTask(already_AddRefed aRunnable, int aDelayMs) +{ + MOZ_ASSERT_UNREACHABLE("ContentProcessController should only be used remotely."); +} + +bool +ContentProcessController::IsRepaintThread() +{ + return NS_IsMainThread(); +} + +void +ContentProcessController::DispatchToRepaintThread(already_AddRefed 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 aRunnable, int aDelayMs) override; + + bool IsRepaintThread() override; + + void DispatchToRepaintThread(already_AddRefed aTask) override; + +private: + ContentProcessController(); + + void SetObserver(nsIObserver* aObserver); + + RefPtr mBrowser; + RefPtr 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 // 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 +ElementFromPoint(const nsCOMPtr& 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 result = content->AsElement(); + return result.forget(); + } + } + } + return nullptr; +} + +static bool +ShouldZoomToElement(const nsCOMPtr& 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& 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 shell = aRootContentDocument->GetShell(); + if (!shell) { + return zoomOut; + } + + nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable(); + if (!rootScrollFrame) { + return zoomOut; + } + + nsCOMPtr 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 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& 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 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 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 +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 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 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 +BasicLayerManager::CreateCanvasLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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(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 UpdateSurface(); + + BasicLayerManager* BasicManager() + { + return static_cast(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(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(mManager); + } +}; + +already_AddRefed +BasicLayerManager::CreateColorLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 +#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 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 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 +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 target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); + + if (!target) { + return nullptr; + } + + RefPtr rt = new BasicCompositingRenderTarget(target, aRect); + + return rt.forget(); +} + +already_AddRefed +BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect, + const CompositingRenderTarget *aSource, + const IntPoint &aSourcePoint) +{ + MOZ_CRASH("GFX: Shouldn't be called!"); + return nullptr; +} + +already_AddRefed +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 rt; + IntRect rect = aRect.ToUnknownRect(); + + if (aBufferMode != BufferMode::BUFFER_NONE) { + RefPtr 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 +BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) +{ + RefPtr result = new DataTextureSourceBasic(nullptr); + if (aFlags & TextureFlags::RGB_FROM_YCBCR) { + result->mFromYCBCR = true; + } + return result.forget(); +} + +already_AddRefed +BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface) +{ + RefPtr result = new DataTextureSourceBasic(aSurface); + return result.forget(); +} + +already_AddRefed +BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) +{ + BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost(); + MOZ_ASSERT(bufferTexture); + + if (!bufferTexture) { + return nullptr; + } + RefPtr 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& aMaskSurface, + Matrix& aMaskTransform) +{ + if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) { + EffectMask *effectMask = static_cast(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 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 buffer = mRenderTarget->mDrawTarget; + + // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, + // |dest| is a temporary surface. + RefPtr 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 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(effect)->mBlendMode; + } + + switch (aEffectChain.mPrimaryEffect->mType) { + case EffectTypes::SOLID_COLOR: { + EffectSolidColor* effectSolidColor = + static_cast(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(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 srcData = srcSurf->GetDataSurface(); + + // Yes, we re-create the premultiplied data every time. + // This might be better with a cache, eventually. + RefPtr 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(aEffectChain.mPrimaryEffect.get()); + RefPtr surface + = static_cast(effectRenderTarget->mRenderTarget.get()); + RefPtr 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 destSnapshot = dest->Snapshot(); + + SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform); + + if (sourceMask) { + RefPtr 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 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 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 self = this; + RefPtr 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 source; + if (mRenderTarget->mDrawTarget != mDrawTarget) { + source = mWidget->EndBackBufferDrawing(); + } else { + source = mRenderTarget->mDrawTarget->Snapshot(); + } + RefPtr 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 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 + CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override; + + virtual already_AddRefed + CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget *aSource, + const gfx::IntPoint &aSourcePoint) override; + + virtual already_AddRefed + CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, + const LayoutDeviceIntRect& aClearRect, + BufferMode aBufferMode); + + virtual already_AddRefed + CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + virtual already_AddRefed + CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override; + + virtual already_AddRefed + CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) override; + + virtual bool SupportsEffect(EffectTypes aEffect) override; + + virtual void SetRenderTarget(CompositingRenderTarget *aSource) override + { + mRenderTarget = static_cast(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 mDrawTarget; + // The current render target for drawing + RefPtr 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 // 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 +BasicLayerManager::CreateContainerLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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(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(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(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 GetAsSourceSurface() override; + +protected: + BasicLayerManager* BasicManager() + { + return static_cast(mManager); + } + + gfx::IntSize mSize; +}; + +void +BasicImageLayer::Paint(DrawTarget* aDT, + const gfx::Point& aDeviceOffset, + Layer* aMaskLayer) +{ + if (IsHidden() || !mContainer) { + return; + } + + RefPtr originalIF = mContainer->GetImageFactory(); + mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nullptr : BasicManager()->GetImageFactory()); + + AutoLockImage autoLock(mContainer); + Image *image = autoLock.GetImage(); + if (!image) { + mContainer->SetImageFactory(originalIF); + return; + } + RefPtr 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 +BasicImageLayer::GetAsSourceSurface() +{ + if (!mContainer) { + return nullptr; + } + + AutoLockImage lockImage(mContainer); + Image* image = lockImage.GetImage(); + if (!image) { + return nullptr; + } + return image->GetAsSourceSurface(); +} + +already_AddRefed +BasicLayerManager::CreateImageLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 // 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 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 mDecodedBuffer; + gfx::IntSize mScaleHint; + int mStride; + bool mDelayedConversion; +}; + +class BasicImageFactory : public ImageFactory +{ +public: + BasicImageFactory() {} + + virtual RefPtr + 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 +BasicPlanarYCbCrImage::GetAsSourceSurface() +{ + NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); + + if (mSourceSurface) { + RefPtr surface(mSourceSurface); + return surface.forget(); + } + + if (!mDecodedBuffer) { + return PlanarYCbCrImage::GetAsSourceSurface(); + } + + gfxImageFormat format = GetOffscreenFormat(); + + RefPtr 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 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 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 // for uint32_t +#include // for rand, RAND_MAX +#include // for int32_t +#include // 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 dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8); + + RefPtr 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 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 sourceDT = group.mGroupTarget->GetDrawTarget(); + group.mGroupTarget = nullptr; + + RefPtr 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& 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& 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(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(aPaintContext.mLayer); + AutoTArray 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 &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& clipRect = aLayer->GetLocalClipRect(); + BasicContainerLayer* container = + static_cast(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 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 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 untransformedSurf = untransformedDT->Snapshot(); + RefPtr xformDT = + untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height), + SurfaceFormat::B8G8R8A8); + RefPtr 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 +BasicLayerManager::CreateReadbackLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 // 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 CreatePaintedLayer() override; + virtual already_AddRefed CreateContainerLayer() override; + virtual already_AddRefed CreateImageLayer() override; + virtual already_AddRefed CreateCanvasLayer() override; + virtual already_AddRefed CreateColorLayer() override; + virtual already_AddRefed 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 mGroupTarget; + nsIntRegion mVisibleRegion; + bool mNeedsClipToVisibleRegion; + gfx::IntPoint mGroupOffset; + gfx::CompositionOp mOperator; + gfx::Float mOpacity; + RefPtr 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 mDefaultTarget; + // The context to draw into. + RefPtr mTarget; + // Image factory we use. + RefPtr 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 // 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 surface = + static_cast(aMaskLayer->ImplData())->GetAsSourceSurface(); + if (surface) { + Matrix transform; + Matrix4x4 effectiveTransform = aMaskLayer->GetEffectiveTransform(); + DebugOnly 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 +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 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(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 mContext; +}; + +class BasicReadbackLayer : public ReadbackLayer, + public BasicImplData +{ +public: + explicit BasicReadbackLayer(BasicLayerManager* aLayerManager) : + ReadbackLayer(aLayerManager, static_cast(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(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 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 // 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 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 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 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 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 +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 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(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(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 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 mCompositor; + RefPtr mSurface; + RefPtr 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 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 mCompositor; + RefPtr mTextureSource; + RefPtr 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 + +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 +X11TextureData::BorrowDrawTarget() +{ + MOZ_ASSERT(mSurface); + if (!mSurface) { + return nullptr; + } + + IntSize size = mSurface->GetSize(); + RefPtr dt = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size); + + return dt.forget(); +} + +bool +X11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) +{ + RefPtr 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 surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, imageFormat); + if (!surface || surface->GetType() != gfxSurfaceType::Xlib) { + NS_ERROR("creating Xlib surface failed!"); + return nullptr; + } + + gfxXlibSurface* xlibSurface = static_cast(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 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 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 +CreateTextureHostBasic(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ +#ifdef XP_MACOSX + if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorMacIOSurface) { + const SurfaceDescriptorMacIOSurface& desc = + aDesc.get_SurfaceDescriptorMacIOSurface(); + return MakeAndAddRef(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 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 surface; + if (mBufferDrawTarget) { + surface = mBufferDrawTarget->Snapshot(); + return surface.get(); + } else { + return nullptr; + } +} + +void +X11DataTextureSourceBasic::DeallocateDeviceData() +{ + mBufferDrawTarget = nullptr; +} + +already_AddRefed +X11BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) +{ + RefPtr 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 mBufferDrawTarget; +}; + +class X11BasicCompositor : public BasicCompositor +{ +public: + explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget) + : BasicCompositor(aParent, aWidget) + {} + + virtual already_AddRefed + CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + virtual already_AddRefed + 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 mCompositor; + RefPtr mSurface; + RefPtr 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::CreateCanvasClient(CanvasClientType aType, + CompositableForwarder* aForwarder, + TextureFlags aFlags) +{ + switch (aType) { + case CanvasClientTypeShSurf: + return MakeAndAddRef(aForwarder, aFlags); + case CanvasClientAsync: + return MakeAndAddRef(aForwarder, aFlags); + default: + return MakeAndAddRef(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(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 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 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 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 +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 Create(gfx::SurfaceFormat format) { + return TextureClient::CreateForRawBufferAccess(mAllocator, format, + mSize, mBackendType, + mBaseTexFlags); + } + +public: + already_AddRefed CreateB8G8R8AX8() { + gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8; + return Create(format); + } + + already_AddRefed CreateR8G8B8AX8() { + RefPtr 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 +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 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 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 +CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory) +{ + RefPtr 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(aLayer); + UpdateRenderer(aSize, renderer); +} + +void +CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer) +{ + Renderer renderer; + renderer.construct(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()) { + layer = aRenderer.ref(); + gl = layer->mGLContext; + } else { + asyncRenderer = aRenderer.ref(); + gl = asyncRenderer->mGLContext; + } + gl->MakeCurrent(); + + RefPtr 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 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 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 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 + CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + TextureFlags aFlags, + ClientCanvasLayer* aLayer); + + RefPtr mBackBuffer; + RefPtr 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 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 mShSurfClient; + RefPtr mReadbackClient; + RefPtr mFront; + RefPtr 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 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 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(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(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 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 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 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 +ClientLayerManager::CreateCanvasLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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(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(mManager); + } + + CanvasClientType GetCanvasClientType(); + + RefPtr mCanvasClient; + + UniquePtr 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(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(mManager); + } +}; + +already_AddRefed +ClientLayerManager::CreateColorLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 +ClientLayerManager::CreateContainerLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr layer = + new ClientContainerLayer(this); + CREATE_SHADOW(Container); + return layer.forget(); +} + +already_AddRefed +ClientLayerManager::CreateRefLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 // 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(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 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(mManager); + } +}; + +class ClientRefLayer : public RefLayer, + public ClientLayer { +public: + explicit ClientRefLayer(ClientLayerManager* aManager) : + RefLayer(aManager, static_cast(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(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(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(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 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 +ClientLayerManager::CreateImageLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + RefPtr 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 observerService = + mozilla::services::GetObserverService(); + + MOZ_ASSERT(observerService); + + if (observerService) { + observerService->AddObserver(this, "memory-pressure", false); + } +} + +void +ClientLayerManager::MemoryPressureObserver::UnregisterMemoryPressureEvent() +{ + nsCOMPtr 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 allocator = mTransactionIdAllocator; + uint64_t id = mLatestTransactionId; + + RefPtr 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 +ClientLayerManager::CreateReadbackLayer() +{ + RefPtr 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(¤tConfig); + 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& 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 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& 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 replies; + if (mForwarder->EndTransaction(&replies, mRegionToClear, + mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber, + mIsRepeatTransaction, transactionStart, &sent)) { + for (nsTArray::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 compositable = + CompositableClient::FromIPDLActor(obs.compositableChild()); + ContentClientRemote* contentClient = + static_cast(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 +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 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 // 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 > 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 CreatePaintedLayer() override; + virtual already_AddRefed CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override; + virtual already_AddRefed CreateContainerLayer() override; + virtual already_AddRefed CreateImageLayer() override; + virtual already_AddRefed CreateCanvasLayer() override; + virtual already_AddRefed CreateReadbackLayer() override; + virtual already_AddRefed CreateColorLayer() override; + virtual already_AddRefed 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& 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& + 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 + 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 mShadowTarget; + + RefPtr 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 mForwarder; + AutoTArray mOverfillCallbacks; + mozilla::TimeStamp mTransactionStart; + + nsTArray mDidCompositeObservers; + + RefPtr 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(aLayer->ImplData()); + } + + template + 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 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 // 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 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(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 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 +ClientLayerManager::CreatePaintedLayer() +{ + return CreatePaintedLayerWithHint(NONE); +} + +already_AddRefed +ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + if (gfxPrefs::LayersTilesEnabled()) { + RefPtr layer = new ClientTiledPaintedLayer(this, aHint); + CREATE_SHADOW(Painted); + return layer.forget(); + } else { + RefPtr 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(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(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 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(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(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 +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(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(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 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 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(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 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(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 +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 +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 +#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 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 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 // 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(aActor); + mCompositableChild->Init(this, aAsyncID); +} + +/* static */ RefPtr +CompositableClient::FromIPDLActor(PCompositableChild* aActor) +{ + MOZ_ASSERT(aActor); + + RefPtr client = static_cast(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 +CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2DBackend, + TextureFlags aTextureFlags) +{ + return TextureClient::CreateForRawBufferAccess(GetForwarder(), + aFormat, aSize, aMoz2DBackend, + aTextureFlags | mTextureFlags); +} + +already_AddRefed +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 +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 = + 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 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 // for uint64_t +#include // for vector +#include // 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 + CreateBufferTextureClient(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE, + TextureFlags aFlags = TextureFlags::DEFAULT); + + already_AddRefed + CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT); + + already_AddRefed + 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 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 mCompositableChild; + RefPtr mForwarder; + // Some layers may want to enforce some flags to all their textures + // (like disallowing tiling) + TextureFlags mTextureFlags; + RefPtr 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 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 + +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::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(aForwarder); + } + return MakeAndAddRef(aForwarder); +} + +void +ContentClient::EndPaint(nsTArray* 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* aBlackDT, + RefPtr* 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 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* 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 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 mReadbackUpdates; + // This array is used to keep the layers alive until the callback. + vector> 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* 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 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* aBlackDT, + RefPtr* 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 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 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 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 locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } + if (mTextureClientOnWhite) { + DebugOnly 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 ", + 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 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 surf = dt->Snapshot(); + RefPtr 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 locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE); + MOZ_ASSERT(locked); + } + if (mTextureClientOnWhite) { + DebugOnly 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 // 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 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* 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* aBlackDT, RefPtr* 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* 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* aBlackDT, RefPtr* 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 mTextureClient; + RefPtr 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 > 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 mFrontClient; + RefPtr 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 +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 GetAsSourceSurface(); + + virtual GPUVideoTextureData* AsGPUVideoTextureData() override + { + return this; + } + +protected: + RefPtr 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 // 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::CreateImageClient(CompositableType aCompositableHostType, + CompositableForwarder* aForwarder, + TextureFlags aFlags) +{ + RefPtr 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 +ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder) +{ + RefPtr texture; + if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) { + PlanarYCbCrImage* ycbcr = static_cast(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 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 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 newBuffers; + AutoTArray textures; + + for (auto& img : images) { + Image* image = img.mImage; + + RefPtr 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(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 // for uint32_t, uint64_t +#include // 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 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 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 mTextureClient; + int32_t mImageSerial; + }; + nsTArray 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 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 +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 discardedFrontBuffer = nullptr; + RefPtr 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 backBufferOnWhite; + RefPtr 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 dt = backBuffer->BorrowDrawTarget(); + RefPtr 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 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 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 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 // 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 actor; + RefPtr 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 mCompositableForwarder; + RefPtr mTextureForwarder; + + TextureClient* mTextureClient; + TextureData* mTextureData; + Atomic 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 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 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 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 snapshot = mBorrowedDrawTarget->Snapshot(); + RefPtr 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 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::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(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(actor)->ReleaseIPDLReference(); + return true; +} + +// static +already_AddRefed +TextureClient::AsTextureClient(PTextureChild* actor) +{ + if (!actor) { + return nullptr; + } + + TextureChild* tc = static_cast(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 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(desc.type()) << ", " + << static_cast(aForwarder->GetCompositorBackendType()) << ", " + << static_cast(GetFlags()) + << ", " << mSerial; + return false; + } + + mActor = static_cast(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(desc.type()) << ", " + << static_cast(aForwarder->GetCompositorBackendType()) << ", " + << static_cast(GetFlags()) + << ", " << mSerial; + return false; + } + + mActor = static_cast(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::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::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(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::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(data, aTextureFlags, aAllocator->GetTextureForwarder()); + } + + // Fall back to using UpdateFromSurface + + TextureAllocationFlags allocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE); + RefPtr 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::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::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(texData, aTextureFlags, aAllocator); +} + +// static +already_AddRefed +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(data, aTextureFlags, + aAllocator->GetTextureForwarder()); +} + +// static +already_AddRefed +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(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 destinationTarget = aTarget->BorrowDrawTarget(); + if (!destinationTarget) { + gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in BorrowDrawTarget"; + return false; + } + + RefPtr sourceTarget = BorrowDrawTarget(); + if (!sourceTarget) { + gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in BorrowDrawTarget"; + return false; + } + + RefPtr 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 +TextureClient::GetAsSurface() +{ + if (!Lock(OpenMode::OPEN_READ)) { + return nullptr; + } + RefPtr data; + { // scope so that the DrawTarget is destroyed before Unlock() + RefPtr dt = BorrowDrawTarget(); + if (dt) { + RefPtr 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 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 + (mShmemSection.shmem().get() + mShmemSection.offset()); + } + + RefPtr mClientAllocator; + mozilla::layers::ShmemSection mShmemSection; + bool mAllocSuccess; +}; + +// static +already_AddRefed +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(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 lock = reinterpret_cast( + 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::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(); + } + + return MakeAndAddRef(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::CreateSyncObject(SyncHandle aHandle) +{ + if (!aHandle) { + return nullptr; + } + +#ifdef XP_WIN + return MakeAndAddRef(aHandle); +#else + MOZ_ASSERT_UNREACHABLE(); + return nullptr; +#endif +} + +already_AddRefed +TextureClient::CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator) +{ + if (!aData) { + return nullptr; + } + return MakeAndAddRef(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 // for size_t +#include // 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 +{ +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObject) + virtual ~SyncObject() { } + + static already_AddRefed 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 + Create(LayersIPCChannel* aAllocator); + + static already_AddRefed + 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 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 +{ +public: + explicit TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator); + + virtual ~TextureClient(); + + static already_AddRefed + CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator); + + // Creates and allocates a TextureClient usable with Moz2D. + static already_AddRefed + CreateForDrawing(KnowsCompositor* aAllocator, + gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags flags = ALLOC_DEFAULT); + + static already_AddRefed + CreateFromSurface(KnowsCompositor* aAllocator, + gfx::SourceSurface* aSurface, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags); + + // Creates and allocates a TextureClient supporting the YCbCr format. + static already_AddRefed + 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 + 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 + CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator, + size_t aSize, + YUVColorSpace aYUVColorSpace, + TextureFlags aTextureFlags); + + // Creates and allocates a TextureClient of the same type. + already_AddRefed + 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 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 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 + 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 + 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; +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 mAllocator; + RefPtr mActor; + RefPtr mRecycleAllocator; + RefPtr mReadLock; + + TextureData* mData; + RefPtr 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 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 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 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 +class TKeepAlive : public KeepAlive +{ +public: + explicit TKeepAlive(T* aData) : mData(aData) {} +protected: + RefPtr 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(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(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 +TextureClientPool::GetTextureClient() +{ + // Try to fetch a client from the pool + RefPtr 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 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 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 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 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 +#include + +namespace mozilla { +namespace layers { + +class ISurfaceAllocator; +class TextureForwarder; +class TextureReadLock; + +class TextureClientAllocator +{ +protected: + virtual ~TextureClientAllocator() {} +public: + NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator) + + virtual already_AddRefed 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 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 > mTextureClients; + + std::list> mTextureClientsDeferred; + RefPtr mShrinkTimer; + RefPtr 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 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 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 +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 +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 +TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper) +{ + MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE); + + RefPtr 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 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 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 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 +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 >::iterator it; + for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) { + RefPtr 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 kungFuDeathGrip(this); + aClient->SetRecycleAllocator(nullptr); + + RefPtr 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 +#include +#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 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 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 + CreateOrRecycle(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags flags = ALLOC_DEFAULT); + + already_AddRefed + CreateOrRecycle(ITextureClientAllocationHelper& aHelper); + + void ShrinkToMinimumSize(); + + void Destroy(); + +protected: + virtual already_AddRefed + Allocate(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags); + + RefPtr mSurfaceAllocator; + + friend class DefaultTextureClientAllocationHelper; + void RecycleTextureClient(TextureClient* aClient) override; + + static const uint32_t kMaxPooledSized = 2; + uint32_t mMaxPooledSize; + + std::map > 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 > 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 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::Create(UniquePtr 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(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 // for size_t +#include // 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 mSurf; + + friend class SharedSurfaceTextureClient; + + explicit SharedSurfaceTextureData(UniquePtr 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 + Create(UniquePtr surf, gl::SurfaceFactory* factory, + LayersIPCChannel* aAllocator, TextureFlags aFlags); + + gl::SharedSurface* Surf() const { + return static_cast(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 // for ceil, ceilf, floor +#include +#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 +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 +{ + public: + TileExpiry() : nsExpirationTracker(1000, "TileExpiry") {} + + static void AddTile(TileClient* aTile) + { + if (!sTileExpiry) { + sTileExpiry = MakeUnique(); + } + + 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 sTileExpiry; +}; +UniquePtr TileExpiry::sTileExpiry; + +void ShutdownTileCache() +{ + TileExpiry::Shutdown(); +} + +void +TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr 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(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 frontBuffer = mFrontBuffer; + RefPtr 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 +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 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* 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 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, nsIntRegion ®ion) +{ + struct LockedBits { + uint8_t *data; + IntSize size; + int32_t stride; + SurfaceFormat format; + static int clamp(int x, int min, int max) + { + if (x < min) + x = min; + if (x > max) + x = max; + return x; + } + + static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height) + { + if (src + n > bitmap + stride*height) { + MOZ_CRASH("GFX: long src memcpy"); + } + if (src < bitmap) { + MOZ_CRASH("GFX: short src memcpy"); + } + if (dst + n > bitmap + stride*height) { + MOZ_CRASH("GFX: long dst mempcy"); + } + if (dst < bitmap) { + MOZ_CRASH("GFX: short dst mempcy"); + } + } + + static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) { + LockedBits *lb = static_cast(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 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 = gfx::Factory::CreateTiledDrawTarget(tileset); + if (!drawTarget || !drawTarget->IsValid()) { + gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target"; + return; + } + drawTarget->SetTransform(Matrix()); + + RefPtr 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::max(), + std::numeric_limits::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 = 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 backBufferOnWhite; + RefPtr 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 dt = backBuffer->BorrowDrawTarget(); + RefPtr 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 +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 transformedCompositionBounds = + GetCompositorSideCompositionBounds(scrollAncestor, + aPaintData->mTransformToCompBounds, + viewTransform, + ViewAs(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 // for size_t +#include // for uint16_t +#include // for swap +#include +#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* 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); + void Set(TileClient * container, TextureClient*); + // Implicitly convert to TextureClient* because we can't chain + // implicit conversion that would happen on RefPtr + operator TextureClient*() const { return mBuffer; } + RefPtr operator ->() { return mBuffer; } + private: + PrivateProtector& operator=(const PrivateProtector &); + RefPtr mBuffer; + } mBackBuffer; + RefPtr mBackBufferOnWhite; + RefPtr mFrontBuffer; + RefPtr mFrontBufferOnWhite; + RefPtr 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 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 + , public ClientTiledLayerBuffer +{ + friend class TiledLayerBuffer; +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 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 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 // 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 +# 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( + 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(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& 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 +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 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(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 + (aAnimation.iterationComposite()) == + dom::IterationCompositeOperation::Accumulate && + aCurrentIteration > 0) { + // FIXME: Bug 1293492: Add a utility function to calculate both of + // below StyleAnimationValues. + DebugOnly 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 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 functions; + functions.AppendElement(TransformMatrix(transform)); + *aValue = functions; +} + +static bool +SampleAnimations(Layer* aLayer, TimeStamp aPoint) +{ + bool activeAnimations = false; + + ForEachNode( + aLayer, + [&activeAnimations, &aPoint] (Layer* layer) + { + AnimationArray& animations = layer->GetAnimations(); + InfallibleTArray& 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(animation.direction()); + timing.mFill = static_cast(animation.fillMode()); + timing.mFunction = + AnimationUtils::TimingFunctionToComputedTimingFunction( + animation.easingFunction()); + + ComputedTiming computedTiming = + dom::AnimationEffectReadOnly::GetComputedTimingAt( + Nullable(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(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( + 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& 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 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(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(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> 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( + aLayer, + [&stackDeferredClips] (Layer* layer) + { + stackDeferredClips.push(Maybe()); + }, + [this, &aOutFoundRoot, &stackDeferredClips, &appliedTransform, &clipPartsCache] (Layer* layer) + { + Maybe 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 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& 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( + 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( + 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 mFixedClip; + Maybe mScrolledClip; + + Maybe Intersect() const { + return IntersectMaybeRects(mFixedClip, mScrolledClip); + } + }; + + typedef std::map 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 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(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 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 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(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 // for _Rb_tree_iterator, map, etc +#include // 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 +{ +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(aImageContainer)); + } + } + + ~CompositableParent() + { + MOZ_COUNT_DTOR(CompositableParent); + CompositableMap::Erase(mHost->GetAsyncID()); + } + + virtual void Destroy() override + { + if (mHost) { + mHost->Detach(nullptr, CompositableHost::FORCE_DETACH); + } + } + + RefPtr 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(aActor)->mHost; +} + +void +CompositableHost::UseTextureHost(const nsTArray& 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 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 effect = new EffectMask(source, + source->GetSize(), + aTransform); + aEffects.mSecondaryEffects[EffectTypes::MASK] = effect; + return true; +} + +void +CompositableHost::RemoveMaskEffect() +{ + RefPtr host = GetAsTextureHost(); + if (host) { + host->Unlock(); + } +} + +/* static */ already_AddRefed +CompositableHost::Create(const TextureInfo& aTextureInfo) +{ + RefPtr 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 dSurf = aTexture->GetAsSurface(); + if (!dSurf) { + return; + } + aStream << gfxUtils::GetAsDataURI(dSurf).get(); +} + +void +CompositableHost::ReceivedDestroy(PCompositableParent* aActor) +{ + static_cast(aActor)->RecvDestroy(); +} + +namespace CompositableMap { + +typedef std::map 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 // for uint64_t +#include // 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 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 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& 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 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 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 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 // 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 +#include "TextRenderer.h" // for TextRenderer +#include +#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(visibleRegion.GetBounds().width, 500); + + IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft(); + aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft, + aLayer->GetEffectiveTransform(), 16, + maxWidth); +} + +template +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 mLayer; + RenderTargetIntRect mClipRect; +}; + +/* all of the prepared data that we need in RenderLayer() */ +struct PreparedData +{ + RefPtr mTmpTarget; + AutoTArray mLayers; + bool mNeedsSurfaceCopy; +}; + +// ContainerPrepare is shared between RefLayer and ContainerLayer +template void +ContainerPrepare(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect) +{ + aContainer->mPrepared = MakeUnique(); + aContainer->mPrepared->mNeedsSurfaceCopy = false; + + /** + * Determine which layers to draw. + */ + AutoTArray children; + aContainer->SortChildrenBy3DZOrder(children); + + for (uint32_t i = 0; i < children.Length(); i++) { + LayerComposite* layerToRender = static_cast(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 surface = nullptr; + + RefPtr& 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 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 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 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(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 RefPtr +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& 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 RefPtr +CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer, + LayerManagerComposite* aManager) +{ + Compositor* compositor = aManager->GetCompositor(); + gfx::IntRect visibleRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds(); + RefPtr 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 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 void +RenderIntermediate(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect, + RefPtr surface) +{ + Compositor* compositor = aManager->GetCompositor(); + RefPtr 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 void +ContainerRender(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect) +{ + MOZ_ASSERT(aContainer->mPrepared); + + if (aContainer->UseIntermediateSurface()) { + RefPtr 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 = aManager->GetCompositor(); +#ifdef MOZ_DUMP_PAINTING + if (gfxEnv::DumpCompositorTextures()) { + RefPtr surf = surface->Dump(compositor); + if (surf) { + WriteSnapshotToDumpFile(aContainer, surf); + } + } +#endif + + RefPtr 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(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(GetFirstChild()->ImplData())->Destroy(); + RemoveChild(mFirstChild); + } + mDestroyed = true; + } +} + +LayerComposite* +ContainerLayerComposite::GetFirstChildComposite() +{ + if (!mFirstChild) { + return nullptr; + } + return static_cast(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(l->ImplData()); + layerToCleanup->CleanupResources(); + } +} + +RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager) + : RefLayer(aManager, nullptr) + , LayerComposite(aManager) +{ + mImplData = static_cast(this); +} + +RefLayerComposite::~RefLayerComposite() +{ + Destroy(); +} + +void +RefLayerComposite::Destroy() +{ + MOZ_ASSERT(!mFirstChild); + mDestroyed = true; +} + +LayerComposite* +RefLayerComposite::GetFirstChildComposite() +{ + if (!mFirstChild) { + return nullptr; + } + return static_cast(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 + friend void ContainerPrepare(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + template + friend void ContainerRender(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + template + friend void RenderLayers(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + template + friend void RenderIntermediate(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect, + RefPtr surface); + template + friend RefPtr + CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + template + friend RefPtr + CreateOrRecycleTarget(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + + template + 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 mPrepared; + + RefPtr mLastIntermediateSurface; +}; + +class RefLayerComposite : public RefLayer, + public LayerComposite +{ + template + friend void ContainerPrepare(ContainerT* aContainer, + LayerManagerComposite* aManager, + const RenderTargetIntRect& aClipRect); + template + friend void ContainerRender(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect); + template + friend void RenderLayers(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect); + template + friend void RenderIntermediate(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect, + RefPtr surface); + template + friend RefPtr + CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer, + LayerManagerComposite* aManager, + const gfx::IntRect& aClipRect); + template + friend RefPtr + 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 mPrepared; + RefPtr 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 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& 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 << "
      "; + } + if (mTextureHost) { + aStream << aPrefix; + if (aDumpHtml) { + aStream << "
    • Front buffer
    • "; + } else { + aStream << "\n"; + } + } + if (mTextureHostOnWhite) { + aStream << aPrefix; + if (aDumpHtml) { + aStream << "
    • Front buffer on white
    • "; + } else { + aStream << "\n"; + } + } + if (aDumpHtml) { + aStream << "
    "; + } +#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 +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 +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 // for uint32_t +#include // 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 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& 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 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 // 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& 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 aHistogram) +{ + double average = 0.0; + double samples = 0.0; + + for (std::map::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 aHistogram) +{ + double sumOfDifferences = 0; + double average = GetMean(aHistogram); + double samples = 0.0; + + for (std::map::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 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& aHistogram) +{ + int length = 0; + const int kBufferLength = 512; + char buffer[kBufferLength]; + + for (std::map::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 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 = static_cast(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 fpsSurface = Factory::CreateWrappingDataSourceSurface( + reinterpret_cast(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 // for min +#include // for size_t +#include // 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& aHistogram); + void PrintHistogram(std::map& aHistogram); + double GetMean(std::map aHistogram); + double GetStdDev(std::map 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 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 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 + +#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 result(layer, uniformity); + aOutData->mUniformities.insert(result); + } + + Reset(); +} + +LayerTransforms* +LayerTransformRecorder::GetLayerTransforms(uintptr_t aLayer) +{ + if (!mFrameTransforms.count(aLayer)) { + LayerTransforms* newTransform = new LayerTransforms(); + std::pair 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& 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; + +public: + bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext); + // Contains the calculated frame uniformities + std::map mUniformities; +}; + +struct LayerTransforms { + LayerTransforms() {} + + gfx::Point GetAverage(); + gfx::Point GetStdDev(); + + // 60 fps * 5 seconds worth of data + AutoTArray 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 mFrameTransforms; +}; + +} // namespace layers +} // namespace mozilla + +namespace IPC { +template<> +struct ParamTraits +{ + 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>::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 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 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& aTextures) +{ + MOZ_ASSERT(!mLocked); + + CompositableHost::UseTextureHost(aTextures); + MOZ_ASSERT(aTextures.Length() >= 1); + + nsTArray 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 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 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 ? "
    • TextureHost: " + : "TextureHost: "); + DumpTextureHost(aStream, img.mTextureHost); + aStream << (aDumpHtml ? "
    " : " "); + } +} + +LayerRenderState +ImageHost::GetRenderState() +{ + if (mImageHostOverlay) { + return mImageHostOverlay->GetRenderState(); + } + + TimedImage* img = ChooseImage(); + if (img) { + SetCurrentTextureHost(img->mTextureHost); + return img->mTextureHost->GetRenderState(); + } + return LayerRenderState(); +} + +already_AddRefed +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 +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 // 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& 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 GetAsSurface() override; + + virtual bool Lock() override; + + virtual void Unlock() override; + + virtual already_AddRefed 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 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 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 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 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 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(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(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 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 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 // for size_t +#include // 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 +#include +#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(aLayer->ImplData()); +} + +static void ClearSubtree(Layer* aLayer) +{ + ForEachNode( + 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& 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 layerClip = + aLayer->AsLayerComposite()->GetShadowClipRect(); + Maybe 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 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 layerClip = composite->GetShadowClipRect(); + MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), + "The layer with a clip should not participate " + "a 3D rendering context"); + Maybe 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 insideClip; + if (outsideClip && !transform.HasPerspectiveComponent()) { + Matrix4x4 inverse = transform; + if (inverse.Invert()) { + Maybe insideClipFloat = + UntransformBy(ViewAs(inverse), + ParentLayerRect(*outsideClip), + LayerRect::MaxIntRect()); + if (insideClipFloat) { + insideClipFloat->RoundOut(); + LayerIntRect insideClipInt; + if (insideClipFloat->ToIntRect(&insideClipInt)) { + insideClip = Some(insideClipInt); + } + } + } + } + + Maybe ancestorClipForChildren; + if (insideClip) { + ancestorClipForChildren = + Some(ViewAs(*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(transform), visible); + if (const Maybe& clipRect = composite->GetShadowClipRect()) { + visibleParentSpace.AndWith(*clipRect); + } + aVisibleRegion.OrWith(ViewAs(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 +LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize) +{ + NS_RUNTIMEABORT("Should only be called on the drawing side"); + return nullptr; +} + +already_AddRefed +LayerManagerComposite::CreatePaintedLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr layer = new PaintedLayerComposite(this); + return layer.forget(); +} + +already_AddRefed +LayerManagerComposite::CreateContainerLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr layer = new ContainerLayerComposite(this); + return layer.forget(); +} + +already_AddRefed +LayerManagerComposite::CreateImageLayer() +{ + NS_RUNTIMEABORT("Should only be called on the drawing side"); + return nullptr; +} + +already_AddRefed +LayerManagerComposite::CreateColorLayer() +{ + MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest," + "this should only be called on the drawing side"); + RefPtr layer = new ColorLayerComposite(this); + return layer.forget(); +} + +already_AddRefed +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(); + } + + 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 +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 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 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( + 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::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 = 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 previousTarget; + if (haveLayerEffects) { + previousTarget = PushGroupForLayerEffects(); + } else { + mTwoPassTmpTarget = nullptr; + } + + // Render our layers. + RootLayer()->Prepare(ViewAs(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( + 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(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 +LayerManagerComposite::CreatePaintedLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(new PaintedLayerComposite(this)).forget(); +} + +already_AddRefed +LayerManagerComposite::CreateContainerLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(new ContainerLayerComposite(this)).forget(); +} + +already_AddRefed +LayerManagerComposite::CreateImageLayerComposite() +{ + if (mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(new ImageLayerComposite(this)).forget(); +} + +already_AddRefed +LayerManagerComposite::CreateColorLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(new ColorLayerComposite(this)).forget(); +} + +already_AddRefed +LayerManagerComposite::CreateCanvasLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(new CanvasLayerComposite(this)).forget(); +} + +already_AddRefed +LayerManagerComposite::CreateRefLayerComposite() +{ + if (LayerManagerComposite::mDestroyed) { + NS_WARNING("Call on destroyed layer manager"); + return nullptr; + } + return RefPtr(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 // 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 +#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 CreatePaintedLayer() override; + virtual already_AddRefed CreateContainerLayer() override; + virtual already_AddRefed CreateImageLayer() override; + virtual already_AddRefed CreateColorLayer() override; + virtual already_AddRefed CreateCanvasLayer() override; + already_AddRefed CreatePaintedLayerComposite(); + already_AddRefed CreateContainerLayerComposite(); + already_AddRefed CreateImageLayerComposite(); + already_AddRefed CreateColorLayerComposite(); + already_AddRefed CreateCanvasLayerComposite(); + already_AddRefed 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 + 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& 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& 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* 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 PushGroupForLayerEffects(); + void PopGroupForLayerEffects(RefPtr aPreviousTarget, + gfx::IntRect aClipRect, + bool aGrayscaleEffect, + bool aInvertEffect, + float aContrastEffect); + + void ChangeCompositorInternal(Compositor* aNewCompositor); + + float mWarningLevel; + mozilla::TimeStamp mWarnTime; + bool mUnusedApzTransformWarning; + bool mDisabledApzWarning; + RefPtr mCompositor; + UniquePtr mClonedLayerTreeProperties; + + nsTArray mImageCompositeNotifications; + + /** + * Context target, nullptr when drawing directly to our swap chain. + */ + RefPtr mTarget; + gfx::IntRect mTargetBounds; + + nsIntRegion mInvalidRegion; + + typedef nsClassHashtable, + CSSIntRegion> VisibleRegions; + VisibleRegions mVisibleRegions; + + UniquePtr mFPS; + + bool mInTransaction; + bool mIsCompositorReady; + bool mDebugOverlayWantsNextFrame; + + RefPtr mTwoPassTmpTarget; + RefPtr 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 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& 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& 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 mShadowClipRect; + LayerManagerComposite* mCompositeManager; + RefPtr 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 +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 originalTarget = + aCompositor->GetCurrentRenderTarget(); + + RefPtr 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 previousTarget = firstTarget; + for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) { + Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i); + RefPtr 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 // 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 mCanvas; + IntSize mSize; + int mStride; + + RefPtr mSurface; + RefPtr mTextureSource; + RefPtr 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(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(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 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 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(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 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 src = mCompositor->CreateDataTextureSource(); + + if (!src->Update(textSurf)) { + // Upload failed. + return; + } + + RefPtr 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 + +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 mCompositor; + RefPtr 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 +#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 +{ +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 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(actor)->mTextureHost; +} + +// static +uint64_t +TextureHost::GetTextureSerial(PTextureParent* actor) +{ + if (!actor) { + return UINT64_MAX; + } + return static_cast(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 CreateTextureHostOGL(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags); + +// implemented in TextureHostBasic.cpp +already_AddRefed CreateTextureHostBasic(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags); + +// implemented in TextureD3D11.cpp +already_AddRefed CreateTextureHostD3D11(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags); + +// implemented in TextureD3D9.cpp +already_AddRefed CreateTextureHostD3D9(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags); + +already_AddRefed +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(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 +CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr 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(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(mActor)->NotifyNotUsed(mFwdTransactionId); + return; + } + + compositor->NotifyNotUsedAfterComposition(this); +} + +void +TextureHost::CallNotifyNotUsed() +{ + if (!mActor) { + return; + } + static_cast(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 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 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 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 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 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 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 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 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 srcY; + RefPtr srcU; + RefPtr 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 tempY = + gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc), + desc.ySize().width, + desc.ySize(), + gfx::SurfaceFormat::A8); + RefPtr tempCb = + gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc), + desc.cbCrSize().width, + desc.cbCrSize(), + gfx::SurfaceFormat::A8); + RefPtr 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 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 +BufferTextureHost::GetAsSurface() +{ + RefPtr 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(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() : nullptr; +} + +size_t ShmemTextureHost::GetBufferSize() +{ + return mShmem ? mShmem->Size() : 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::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(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 // for size_t +#include // 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 +{ +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 mNextSibling; + int mCompositableCount; +}; + +/// Equivalent of a RefPtr, 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 +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 mRef; +}; + +typedef CompositableTextureRef CompositableTextureSourceRef; +typedef CompositableTextureRef 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 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 +{ + /** + * 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; +public: + explicit TextureHost(TextureFlags aFlags); + +protected: + virtual ~TextureHost(); + +public: + /** + * Factory method. + */ + static already_AddRefed 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 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 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 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 mCompositor; + RefPtr 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 mShmem; + RefPtr 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 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 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 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 +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 +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(®ion); + } + + aTexture->PrepareTextureSource(aTextureSource); +} + +class TextureSourceRecycler +{ +public: + explicit TextureSourceRecycler(nsTArray&& 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 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& 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 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 // for uint16_t +#include // for FILE +#include // 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 +{ + friend class TiledLayerBuffer; + +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 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 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 +X11TextureHost::GetAsSurface() +{ + if (!mTextureSource || !mTextureSource->AsSourceBasic()) { + return nullptr; + } + RefPtr tempDT = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( + GetSize(), GetFormat()); + if (!tempDT) { + return nullptr; + } + RefPtr 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 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 mCompositor; + RefPtr mTextureSource; + RefPtr 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 + +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> + VertexShaderArray; + typedef EnumeratedArray> + PixelShaderArray; + + RefPtr mInputLayout; + RefPtr mVertexBuffer; + + VertexShaderArray mVSQuadShader; + VertexShaderArray mVSQuadBlendShader; + PixelShaderArray mSolidColorShader; + PixelShaderArray mRGBAShader; + PixelShaderArray mRGBShader; + PixelShaderArray mYCbCrShader; + PixelShaderArray mComponentAlphaShader; + PixelShaderArray mBlendShader; + RefPtr mPSConstantBuffer; + RefPtr mVSConstantBuffer; + RefPtr mRasterizerState; + RefPtr mLinearSamplerState; + RefPtr mPointSamplerState; + RefPtr mPremulBlendState; + RefPtr mNonPremulBlendState; + RefPtr mComponentBlendState; + RefPtr mDisabledBlendState; + RefPtr 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 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 dxgiDevice; + RefPtr dxgiAdapter; + + mDevice->QueryInterface(dxgiDevice.StartAssignment()); + dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)); + + { + RefPtr 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 +CompositorD3D11::CreateDataTextureSource(TextureFlags aFlags) +{ + RefPtr 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 +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 texture; + HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + if (FAILED(hr) || !texture) { + gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr); + return nullptr; + } + + RefPtr 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 +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 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(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, + ©Box); + } + } + + return texture; +} + +already_AddRefed +CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget* aSource, + const gfx::IntPoint &aSourcePoint) +{ + RefPtr texture = CreateTexture(aRect, aSource, aSourcePoint); + if (!texture) { + return nullptr; + } + + RefPtr rt = + new CompositingRenderTargetD3D11(texture, aRect.TopLeft()); + rt->SetSize(aRect.Size()); + + return rt.forget(); +} + +bool +CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect, + RefPtr* aOutTexture, + RefPtr* aOutView) +{ + RefPtr 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 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(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(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(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(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(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 vertexShader = mAttachments->mVSQuadShader[maskType]; + RefPtr pixelShader = GetPSForEffect(aEffectChain.mPrimaryEffect, maskType); + + RefPtr mixBlendBackdrop; + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode *blendEffect = + static_cast(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 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(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(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(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(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 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 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 chain; + HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain)); + + if (SUCCEEDED(hr) && chain && mAllowPartialPresents) { + DXGI_PRESENT_PARAMETERS params; + PodZero(¶ms); + params.DirtyRectsCount = mInvalidRegion.GetNumRects(); + StackArray 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, ¶ms); + } 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 rtView = mDefaultRT->mRTView; + RefPtr 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 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 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 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 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 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 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 + +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 + 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 + CreateRenderTarget(const gfx::IntRect &aRect, + SurfaceInitMode aInit) override; + + virtual already_AddRefed + 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 CreateTexture(const gfx::IntRect& aRect, + const CompositingRenderTarget* aSource, + const gfx::IntPoint& aSourcePoint); + bool CopyBackdrop(const gfx::IntRect& aRect, + RefPtr* aOutTexture, + RefPtr* aOutView); + + RefPtr mContext; + RefPtr mDevice; + RefPtr mSwapChain; + RefPtr mDefaultRT; + RefPtr mCurrentRT; + + RefPtr 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 mReadbackTexture; + // The sink that we're trying to read back to. + RefPtr 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 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 mTask; +}; + +NS_IMPL_ISUPPORTS(ReadbackResultWriterD3D11, nsIRunnable) + +DWORD WINAPI ReadbackManagerD3D11::StartTaskThread(void *aManager) +{ + static_cast(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 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 +#include + +#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> 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 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 // ID3D10Texture2D or ID3D11Texture2D +static bool LockD3DTexture(T* aTexture) +{ + MOZ_ASSERT(aTexture); + RefPtr 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 +static bool HasKeyedMutex(T* aTexture) +{ + RefPtr mutex; + aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); + return !!mutex; +} + +template // ID3D10Texture2D or ID3D11Texture2D +static void UnlockD3DTexture(T* aTexture) +{ + MOZ_ASSERT(aTexture); + RefPtr 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(aSyncObject); + sync->RegisterTexture(mTexture); +} + +bool +DXGITextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + RefPtr 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 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 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 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 +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(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 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 +CreateTextureHostD3D11(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr 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 +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 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 +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 device = GetDevice(); + if (!device) { + return false; + } + + RefPtr 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 +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(¤tDesc); + + // 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 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 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 dev = DeviceManagerDx::Get()->GetContentDevice(); + if (!dev || (dev != mD3D11Device)) { + return false; + } + return true; +} + +void +SyncObjectD3D11::FinalizeFrame() +{ + HRESULT hr; + + if (!mD3D11Texture && mD3D11SyncedTextures.size()) { + RefPtr 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 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 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 dev = DeviceManagerDx::Get()->GetContentDevice(); + if (!dev) { + if (DeviceManagerDx::Get()->HasDeviceReset()) { + return; + } + MOZ_CRASH("GFX: Invalid D3D11 content device"); + } + + RefPtr 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 +#include "d3d9.h" +#include + +namespace mozilla { +namespace layers { + +class MOZ_RAII AutoTextureLock +{ +public: + AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult, + uint32_t aTimeout = 0); + ~AutoTextureLock(); + +private: + RefPtr 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 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 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 mTexture; +}; + +already_AddRefed +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 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 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 mTexture; + RefPtr 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 > mTileTextures; + std::vector< RefPtr > mTileSRVs; + RefPtr 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 +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 GetAsSurface() override + { + return nullptr; + } + +protected: + bool LockInternal(); + void UnlockInternal(); + + RefPtr GetDevice(); + + bool OpenSharedHandle(); + + RefPtr mTexture; + RefPtr mTextureSource; + RefPtr 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 GetAsSurface() override + { + return nullptr; + } + +protected: + RefPtr GetDevice(); + + bool OpenSharedHandle(); + + RefPtr mTextures[3]; + RefPtr mTextureSources[3]; + + RefPtr 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 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 mD3D11Texture; + RefPtr mD3D11Device; + std::vector 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 +CompositorD3D9::CreateDataTextureSource(TextureFlags aFlags) +{ + return MakeAndAddRef(SurfaceFormat::UNKNOWN, this, aFlags); +} + +already_AddRefed +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 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(texture, aInit, aRect); +} + +already_AddRefed +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 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 sourceSurface = + static_cast(aSource)->GetD3D9Surface(); + + RefPtr 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 +CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget *aSource, + const gfx::IntPoint &aSourcePoint) +{ + RefPtr texture = CreateTexture(aRect, aSource, aSourcePoint); + + if (!texture) { + return nullptr; + } + + return MakeAndAddRef(texture, + INIT_MODE_NONE, + aRect); +} + +void +CompositorD3D9::SetRenderTarget(CompositingRenderTarget *aRenderTarget) +{ + MOZ_ASSERT(aRenderTarget && mDeviceManager); + RefPtr oldRT = mCurrentRT; + mCurrentRT = static_cast(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 backdropTexture; + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode *blendEffect = + static_cast(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(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(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(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 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(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(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 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 backBuff; + RefPtr 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 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 +GetSurfaceOfTexture(IDirect3DTexture9* aTexture) +{ + RefPtr 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 +CreateDataSurfaceForTexture(IDirect3DDevice9* aDevice, + IDirect3DSurface9* aSource, + const D3DSURFACE_DESC& aDesc) +{ + RefPtr 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(mRect.pBits); + } + + private: + RefPtr mSurface; + D3DLOCKED_RECT mRect; +}; + +void +CompositorD3D9::FinishMixBlend(const gfx::IntRect& aBackdropRect, + const gfx::Rect& aBackdropDest, + const gfx::Matrix4x4& aBackdropTransform, + RefPtr aBackdrop, + gfx::CompositionOp aBlendMode) +{ + HRESULT hr; + + RefPtr 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 backdropSurface = GetSurfaceOfTexture(aBackdrop); + if (!backdropSurface) { + return; + } + RefPtr 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 sourceSurface = GetSurfaceOfTexture(source); + if (!sourceSurface) { + return; + } + RefPtr 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 source = Factory::CreateWrappingDataSourceSurface( + sourceLock.Bits(), sourceLock.Pitch(), + gfx::IntSize(sourceDesc.Width, sourceDesc.Height), + SurfaceFormat::B8G8R8A8); + + RefPtr 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 + CreateRenderTarget(const gfx::IntRect &aRect, + SurfaceInitMode aInit) override; + + virtual already_AddRefed + 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 + 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 + 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 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 mDeviceManager; + + /* Swap chain associated with this compositor */ + RefPtr mSwapChain; + + RefPtr mDefaultRT; + RefPtr 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 +#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 sDeviceManagerLock; +static StaticRefPtr 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 +SwapChainD3D9::GetBackBuffer() +{ + RefPtr 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 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::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 +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(¶meters))) + 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 + 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 +DeviceManagerD3D9::CreateSwapChain(HWND hWnd) +{ + RefPtr 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 +DeviceManagerD3D9::CreateTexture(const IntSize &aSize, + _D3DFORMAT aFormat, + D3DPOOL aPool, + TextureSourceD3D9* aTextureHost) +{ + if (mDeviceWasRemoved) { + return nullptr; + } + RefPtr 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 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 mSwapChain; + RefPtr 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 Get(); + static RefPtr 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 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 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 mSwapChains; + + /* The D3D device we use */ + RefPtr mDevice; + + /* The D3D9Ex device - only valid on Vista+ with WDDM */ + RefPtr mDeviceEx; + + /* An instance of the D3D9 object */ + RefPtr mD3D9; + + /* An instance of the D3D9Ex object - only valid on Vista+ with WDDM */ + RefPtr mD3D9Ex; + + /* Vertex shader used for layer quads */ + RefPtr mLayerVS; + + /* Pixel shader used for RGB textures */ + RefPtr mRGBPS; + + /* Pixel shader used for RGBA textures */ + RefPtr mRGBAPS; + + /* Pixel shader used for component alpha textures (pass 1) */ + RefPtr mComponentPass1PS; + + /* Pixel shader used for component alpha textures (pass 2) */ + RefPtr mComponentPass2PS; + + /* Pixel shader used for RGB textures */ + RefPtr mYCbCrPS; + + /* Pixel shader used for solid colors */ + RefPtr mSolidColorPS; + + /* As above, but using a mask layer */ + RefPtr mLayerVSMask; + RefPtr mRGBPSMask; + RefPtr mRGBAPSMask; + RefPtr mComponentPass1PSMask; + RefPtr mComponentPass2PSMask; + RefPtr mYCbCrPSMask; + RefPtr mSolidColorPSMask; + + /* Vertex buffer containing our basic vertex structure */ + RefPtr mVB; + + /* Our vertex declaration */ + RefPtr 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 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 +#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 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 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 +#include + +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 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(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 +CreateTextureHostD3D9(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr 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 +TextureSourceD3D9::InitTextures(DeviceManagerD3D9* aDeviceManager, + const IntSize &aSize, + _D3DFORMAT aFormat, + RefPtr& aSurface, + D3DLOCKED_RECT& aLockedRect) +{ + if (!aDeviceManager) { + return nullptr; + } + RefPtr 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 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 dstSurface; + aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDeviceManager->device()->UpdateSurface(aSurface, nullptr, dstSurface, + nullptr); +} + +already_AddRefed +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 surface; + D3DLOCKED_RECT lockedRect; + RefPtr 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 +TextureSourceD3D9::TextureToTexture(DeviceManagerD3D9* aDeviceManager, + IDirect3DTexture9* aTexture, + const IntSize& aSize, + _D3DFORMAT aFormat) +{ + if (!aDeviceManager) { + return nullptr; + } + + RefPtr 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 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, ¤tDesc); + + // 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 srcTexture; + RefPtr 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 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(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 deviceManager = DeviceManagerD3D9::Get(); + RefPtr 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(mTexture.get())); + return true; +} + +already_AddRefed +D3D9TextureData::BorrowDrawTarget() +{ + MOZ_ASSERT(mD3D9Surface); + if (!mD3D9Surface) { + return nullptr; + } + + RefPtr dt; + if (ContentForFormat(mFormat) == gfxContentType::COLOR) { + RefPtr 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 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 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 +DXGID3D9TextureData::GetD3D9Surface() const +{ + RefPtr 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(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 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 srcSurface; + RefPtr 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 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(aDescriptor.handleY()); + mHandles[1] = reinterpret_cast(aDescriptor.handleCb()); + mHandles[2] = reinterpret_cast(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 +#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 InitTextures( + DeviceManagerD3D9* aDeviceManager, + const gfx::IntSize &aSize, + _D3DFORMAT aFormat, + RefPtr& aSurface, + D3DLOCKED_RECT& aLockedRect); + + already_AddRefed 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 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 mCreatingDeviceManager; + + StereoMode mStereoMode; + RefPtr 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 > mTileTextures; + RefPtr 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 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 mTexture; + RefPtr 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 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 mDevice; + RefPtr 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 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 mTextureSource; + RefPtr mTexture; + RefPtr 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 GetAsSurface() override + { + return nullptr; // TODO: cf bug 872568 + } + +protected: + void OpenSharedHandle(); + IDirect3DDevice9* GetDevice(); + + RefPtr mTextureSource; + RefPtr 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 GetAsSurface() override + { + return nullptr; + } + + protected: + IDirect3DDevice9* GetDevice(); + + HANDLE mHandles[3]; + RefPtr mTextures[3]; + RefPtr mTextureSources[3]; + + RefPtr 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 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& aTargets) +{ + SendSetTargetAPZC(aInputBlockId, aTargets); +} + +void +APZCTreeManagerChild::UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe& 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& 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& aTargets) override; + + void + UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe& 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& 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 aAPZCTreeManager) + : mLayersId(aLayersId) + , mTreeManager(aAPZCTreeManager) +{ + MOZ_ASSERT(aAPZCTreeManager != nullptr); +} + +APZCTreeManagerParent::~APZCTreeManagerParent() +{ +} + +void +APZCTreeManagerParent::ChildAdopted(RefPtr 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(mTreeManager, + &IAPZCTreeManager::ContentReceivedInputBlock, + aInputBlockId, + aPreventDefault)); + + return true; +} + +bool +APZCTreeManagerParent::RecvSetTargetAPZC( + const uint64_t& aInputBlockId, + nsTArray&& 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 + >> + (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&& aValues) +{ + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + >> + (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( + 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 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 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&& 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&& 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 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 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 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 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 // 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& 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 > mTexturesToRemove; + nsTArray> 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(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& 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 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 tex = TextureHost::AsTextureHost(op.textureParent()); + + MOZ_ASSERT(tex.get()); + compositable->RemoveTextureHost(tex); + break; + } + case CompositableOperationDetail::TOpUseTexture: { + const OpUseTexture& op = aEdit.detail().get_OpUseTexture(); + + AutoTArray 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 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 texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent()); + RefPtr 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 // 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 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 +#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 (rand()) / static_cast (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 CreateEffect(size_t i) { + float tmp; + float red = modff(i * 0.03f, &tmp); + EffectChain effects; + return MakeAndAddRef(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 CreateEffect(size_t i) { + float tmp; + float red = modff(i * 0.03f, &tmp); + EffectChain effects; + return MakeAndAddRef(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 mSurface; + RefPtr 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(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 mSurface; + RefPtr 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(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 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 mSurface; + RefPtr 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(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 CreateEffect(size_t i) { + return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, + SamplingFilter::POINT, true); + } +}; + +static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect) +{ + std::vector 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 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 // 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 sCompositorBridge; + +Atomic 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 aCompositorBridgeParent, + RefPtr 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 selfRef = this; + + if (mLayerManager) { + mLayerManager->Destroy(); + mLayerManager = nullptr; + } + + AutoTArray transactions; + ManagedPLayerTransactionChild(transactions); + for (int i = transactions.Length() - 1; i >= 0; --i) { + RefPtr layers = + static_cast(transactions[i]); + layers->Destroy(); + } + + const ManagedContainer& textures = ManagedPTextureChild(); + for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr 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&& aEndpoint) +{ + // There's only one compositor per child process. + MOZ_ASSERT(!sCompositorBridge); + + RefPtr 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&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (RefPtr 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::CreateRemote(const uint64_t& aProcessToken, + LayerManager* aLayerManager, + Endpoint&& aEndpoint) +{ + RefPtr 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& 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(actor)->GetId(); + + for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr& data = iter.Data(); + if (data->GetLayersId() == childId) { + iter.Remove(); + } + } + static_cast(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& aPluginClipRects, + const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aParentLayerVisibleRegion, + nsTArray& 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&& 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 rv; + MOZ_ASSERT(NS_IsMainThread()); + + // Tracks visible plugins we update, so we can hide any plugins we don't. + nsTArray 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 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 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,2> texturePools = mTexturePools; + + if (mLayerManager) { + MOZ_ASSERT(aId == 0); + RefPtr m = mLayerManager->AsClientLayerManager(); + MOZ_ASSERT(m); + m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); + } else if (aId != 0) { + RefPtr 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(mBuffer->memory()); + MOZ_ASSERT(frame); + mMutex->Lock(); + *aFrame = *frame; + mMutex->Unlock(); +} + +FrameMetrics::ViewID +CompositorBridgeChild::SharedFrameMetricsData::GetViewID() +{ + FrameMetrics* frame = static_cast(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 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(iTabChildBase.get()); + TabChild* tabChild = static_cast(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(aTabChild) ); + if (!mCanSend) { + return; + } + Unused << SendRequestNotifyAfterRemotePaint(); +} + +void +CompositorBridgeChild::CancelNotifyAfterRemotePaint(TabChild* aTabChild) +{ + RefPtr iTabChildBase(do_QueryReferent(mWeakTabChild)); + if (!iTabChildBase) { + return; + } + TabChildBase* tabChildBase = static_cast(iTabChildBase.get()); + TabChild* tabChild = static_cast(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* 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&& 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 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 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 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 +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(child); + + return RefPtr(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(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 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&& aEndpoint); + static bool ReinitForContent(Endpoint&& aEndpoint); + + static RefPtr CreateRemote( + const uint64_t& aProcessToken, + LayerManager* aLayerManager, + Endpoint&& 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&& 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&& 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* 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 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& 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 mBuffer; + CrossProcessMutex* mMutex; + uint64_t mLayersId; + // Unique ID of the APZC that is sharing the FrameMetrics + uint32_t mAPZCId; + }; + + RefPtr mLayerManager; + // When not multi-process, hold a reference to the CompositorBridgeParent to keep it + // alive. This reference should be null in multi-process. + RefPtr 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 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 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 > mTexturesWaitingRecycled; + + MessageLoop* mMessageLoop; + + AutoTArray,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 // for fprintf, stdout +#include // for uint64_t +#include // for _Rb_tree_iterator, etc +#include // 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 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& 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 LayerTreeMap; +static LayerTreeMap sIndirectLayerTrees; +static StaticAutoPtr sIndirectLayerTreesLock; + +static void EnsureLayerTreeMapReady() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!sIndirectLayerTreesLock) { + sIndirectLayerTreesLock = new Monitor("IndirectLayerTree"); + mozilla::ClearOnShutdown(&sIndirectLayerTreesLock); + } +} + +template +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 CompositorMap; +static StaticAutoPtr 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 task = + NewCancelableRunnableMethod(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 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 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&& 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&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe 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 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 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 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* 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& 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 + (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 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( + 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 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(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 +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 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& 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 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& aTargets) +{ + if (!mApzcTreeManager) { + return; + } + // Need to specifically bind this since it's overloaded. + void (APZCTreeManager::*setTargetApzcFunc) + (uint64_t, const nsTArray&) = + &APZCTreeManager::SetTargetAPZC; + RefPtr task = NewRunnableMethod + >> + (mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets); + APZThreadUtils::RunOnControllerThread(task.forget()); + +} + +void +CompositorBridgeParent::InitializeLayerManager(const nsTArray& 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 +CompositorBridgeParent::NewCompositor(const nsTArray& aBackendHints) +{ + for (size_t i = 0; i < aBackendHints.Length(); ++i) { + RefPtr 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& 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(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 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(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 +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 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(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 notifications; + mLayerManager->ExtractImageCompositeNotifications(¬ifications); + 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& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe newIdentifier; + { + MonitorAutoLock lock(mResetCompositorMonitor); + + CompositorLoop()->PostTask(NewRunnableMethod + >, + Maybe*>(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& aBackendHints, + Maybe* 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 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 +CompositorBridgeParent::ResetCompositorImpl(const nsTArray& aBackendHints) +{ + if (!mLayerManager) { + return Nothing(); + } + + RefPtr 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 aCompositor, + Endpoint&& aEndpoint) +{ + aCompositor->Bind(Move(aEndpoint)); +} + +/* static */ bool +CompositorBridgeParent::CreateForContent(Endpoint&& aEndpoint) +{ + gfxPlatform::InitLayersIPC(); + + RefPtr 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 // 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, 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 mVsyncObserver; + + mozilla::Monitor mCurrentCompositeTaskMonitor; + RefPtr mCurrentCompositeTask; + + mozilla::Monitor mSetNeedsCompositeMonitor; + RefPtr 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& 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& 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& 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&& aEndpoint); + + virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override; + virtual bool RecvReset(nsTArray&& 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* 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& 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& 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& 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& 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&& aEndpoint); + + struct LayerTreeState { + LayerTreeState(); + ~LayerTreeState(); + RefPtr mRoot; + RefPtr 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 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 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 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& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool* aSuccess) override; + virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override; + virtual void ScheduleTask(already_AddRefed, int); + void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr); + + void SetEGLSurfaceSize(int width, int height); + + void InitializeLayerManager(const nsTArray& aBackendHints); + void PauseComposition(); + void ResumeComposition(); + void ResumeCompositionAndResize(int width, int height); + void ForceComposition(); + void CancelCurrentCompositeTask(); + void Invalidate(); + bool IsPendingComposite(); + void FinishPendingComposite(); + + RefPtr NewCompositor(const nsTArray& aBackendHints); + void ResetCompositorTask(const nsTArray& aBackendHints, + Maybe* aOutNewIdentifier); + Maybe ResetCompositorImpl(const nsTArray& 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 + inline void ForEachIndirectLayerTree(const Lambda& aCallback); + + RefPtr mLayerManager; + RefPtr mCompositor; + RefPtr 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 mForceCompositionTask; + + RefPtr mApzcTreeManager; + + RefPtr mCompositorThreadHolder; + RefPtr 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 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 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 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 // for fprintf, stdout +#include // for uint64_t +#include // for _Rb_tree_iterator, etc +#include // 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 LayerTreeMap; +extern LayerTreeMap sIndirectLayerTrees; +extern StaticAutoPtr 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&, + 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(aLayers); + EraseLayerState(slp->GetId()); + static_cast(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(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(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& 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& 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&& 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&& 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* 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& 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& 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& 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 mSelfRef; + + RefPtr 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 + +#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::GetAndResetNhObj() +{ + RefPtr nhObj = mNhObj; + mNhObj = new NhObj(); + return nhObj.forget(); +} + +already_AddRefed +GonkNativeHandle::GetDupNhObj() +{ + if (!IsValid()) { + return GonkNativeHandle::CreateDupNhObj(nullptr); + } + return GonkNativeHandle::CreateDupNhObj(mNhObj->mHandle); +} + +/* static */ already_AddRefed +GonkNativeHandle::CreateDupNhObj(native_handle_t* aHandle) +{ + RefPtr 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::Write(Message* aMsg, + const paramType& aParam) +{ + GonkNativeHandle handle = aParam; + MOZ_ASSERT(handle.IsValid()); + + RefPtr 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(nativeHandle->numFds); ++i) { + aMsg->WriteFileDescriptor(base::FileDescriptor(nativeHandle->data[i], true)); + } +} + +bool +ParamTraits::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 nativeHandle( + native_handle_create(numFds, numInts)); + if (!nativeHandle) { + return false; + } + + auto data = + reinterpret_cast(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 { + 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 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 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(); + 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(); + header->mTotalBlocks = 0; + header->mAllocatedBlocks = 0; + + mUsedShmems.push_back(tmp); + aShmemSection->shmem() = tmp; + } + + MOZ_ASSERT(aShmemSection->shmem().IsWritable()); + + ShmemSectionHeapHeader* header = aShmemSection->shmem().get(); + uint8_t* heap = aShmemSection->shmem().get() + 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(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(heap); + allocHeader->mSize = aSize; + } + + MOZ_ASSERT(allocHeader); + header->mAllocatedBlocks++; + allocHeader->mStatus = STATUS_ALLOCATED; + + aShmemSection->size() = aSize; + aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get(); + 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(aShmemSection.shmem().get() + + aShmemSection.offset() - + sizeof(ShmemSectionHeapAllocation)); + + MOZ_ASSERT(allocHeader->mSize == aShmemSection.size()); + + DebugOnly 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(); + 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(); + 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 // for size_t +#include // 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& aMessage) = 0; + + virtual void SendPendingAsyncMessages(); + + virtual void SetAboutToSendAsyncMessages() + { + mAboutToSendAsyncMessages = true; + } + + bool IsAboutToSendAsyncMessages() + { + return mAboutToSendAsyncMessages; + } + +protected: + std::vector 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 +GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend); + +already_AddRefed +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 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 mTotalBlocks; + Atomic mAllocatedBlocks; + }; + + struct ShmemSectionHeapAllocation + { + Atomic 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 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 // 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 OpVector; +typedef nsTArray 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& aTextures) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPDLActor()); + MOZ_ASSERT(aCompositable->IsConnected()); + + AutoTArray 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 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 client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +// Singleton +static StaticMutex sImageBridgeSingletonLock; +static StaticRefPtr 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 compositables; + ManagedPCompositableChild(compositables); + for (int i = compositables.Length() - 1; i >= 0; --i) { + auto compositable = CompositableClient::FromIPDLActor(compositables[i]); + if (compositable) { + compositable->Destroy(); + } + } + InfallibleTArray textures; + ManagedPTextureChild(textures); + for (int i = textures.Length() - 1; i >= 0; --i) { + RefPtr 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* 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* 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::GetSingleton() +{ + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + return sImageBridgeChildSingleton; +} + +void +ImageBridgeChild::ReleaseImageContainer(RefPtr aChild) +{ + if (!aChild) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr runnable = WrapRunnable( + RefPtr(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 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 = WrapRunnable( + imageBridge, + &ImageBridgeChild::ReleaseTextureClientNow, + aClient); + imageBridge->GetMessageLoop()->PostTask(runnable.forget()); +} + +void +ImageBridgeChild::UpdateImageClient(RefPtr aClient, RefPtr aContainer) +{ + if (!aClient || !aContainer) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr runnable = WrapRunnable( + RefPtr(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 = WrapRunnable( + RefPtr(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 = WrapRunnable( + RefPtr(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 cset; + cset.SetCapacity(mTxn->mOperations.size()); + if (!mTxn->mOperations.empty()) { + cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size()); + } + + if (!IsSameProcess()) { + ShadowLayerForwarder::PlatformSyncBeforeUpdate(); + } + + AutoTArray 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::size_type i = 0; i < replies.Length(); ++i) { + NS_RUNTIMEABORT("not reached"); + } +} + +void +ImageBridgeChild::SendImageBridgeThreadId() +{ +} + +bool +ImageBridgeChild::InitForContent(Endpoint&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + + gfxPlatform::GetPlatform(); + + if (!sImageBridgeChildThread) { + sImageBridgeChildThread = new ImageBridgeThread(); + if (!sImageBridgeChildThread->Start()) { + return false; + } + } + + RefPtr child = new ImageBridgeChild(); + + RefPtr runnable = NewRunnableMethod&&>( + 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&& 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&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + return; + } + + // This reference is dropped in DeallocPImageBridgeChild. + this->AddRef(); + + mCanSend = true; + SendImageBridgeThreadId(); +} + +void +ImageBridgeChild::BindSameProcess(RefPtr 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 child = GetSingleton()) { + child->WillShutdown(); + + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + sImageBridgeChildSingleton = nullptr; + } +} + +void +ImageBridgeChild::WillShutdown() +{ + { + SynchronousTask task("ImageBridge ShutdownStep1 lock"); + + RefPtr runnable = WrapRunnable( + RefPtr(this), + &ImageBridgeChild::ShutdownStep1, + &task); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + } + + { + SynchronousTask task("ImageBridge ShutdownStep2 lock"); + + RefPtr runnable = WrapRunnable( + RefPtr(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 child = new ImageBridgeChild(); + RefPtr parent = ImageBridgeParent::CreateSameProcess(); + + RefPtr 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&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sImageBridgeChildSingleton); + MOZ_ASSERT(!sImageBridgeChildThread); + + sImageBridgeChildThread = new ImageBridgeThread(); + if (!sImageBridgeChildThread->IsRunning()) { + sImageBridgeChildThread->Start(); + } + + RefPtr child = new ImageBridgeChild(); + + MessageLoop* loop = child->GetMessageLoop(); + loop->PostTask(NewRunnableMethod&&>( + 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 child = GetSingleton()) { + child->IdentifyTextureHost(aIdentifier); + } +} + +RefPtr +ImageBridgeChild::CreateImageClient(CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild) +{ + if (InImageBridgeChildThread()) { + return CreateImageClientNow(aType, aImageContainer, aContainerChild); + } + + SynchronousTask task("CreateImageClient Lock"); + + RefPtr result = nullptr; + + RefPtr runnable = WrapRunnable( + RefPtr(this), + &ImageBridgeChild::CreateImageClientSync, + &task, + &result, + aType, + aImageContainer, + aContainerChild); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + + return result; +} + +RefPtr +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 client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS); + MOZ_ASSERT(client, "failed to create ImageClient"); + if (client) { + client->Connect(aImageContainer); + } + return client; +} + +already_AddRefed +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 result = nullptr; + RefPtr runnable = WrapRunnable( + RefPtr(this), + &ImageBridgeChild::CreateCanvasClientSync, + &task, + aType, + aFlag, + &result); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + + return result.forget(); +} + +already_AddRefed +ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType, + TextureFlags aFlag) +{ + RefPtr 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 = WrapRunnable( + RefPtr(this), + &ImageBridgeChild::ProxyAllocShmemNow, + &task, + ¶ms); + 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 = WrapRunnable( + RefPtr(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(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(actor)->UnregisterFromIPDL(); + return true; +} + +bool +ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray&& 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&& aNotifications) +{ + for (auto& n : aNotifications) { + ImageContainerChild* child = + static_cast(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 = WrapRunnable( + RefPtr(this), + &ImageBridgeChild::Destroy, + RefPtr(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 // for size_t +#include // 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 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&& aEndpoint); + static bool InitForContent(Endpoint&& aEndpoint); + static bool ReinitForContent(Endpoint&& 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 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&& aMessages) override; + + virtual bool + RecvDidComposite(InfallibleTArray&& aNotifications) override; + + // Create an ImageClient from any thread. + RefPtr CreateImageClient( + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild); + + // Create an ImageClient from the ImageBridge thread. + RefPtr CreateImageClientNow( + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild); + + already_AddRefed CreateCanvasClient(CanvasClient::CanvasClientType aType, + TextureFlags aFlag); + void ReleaseImageContainer(RefPtr aChild); + void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient); + void UpdateImageClient(RefPtr aClient, RefPtr 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 CreateCanvasClientNow( + CanvasClient::CanvasClientType aType, + TextureFlags aFlags); + void CreateCanvasClientSync( + SynchronousTask* aTask, + CanvasClient::CanvasClientType aType, + TextureFlags aFlags, + RefPtr* const outResult); + + void CreateImageClientSync( + SynchronousTask* aTask, + RefPtr* 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& 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&& aEndpoint); + void BindSameProcess(RefPtr 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 > 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 // 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 ImageBridgeParent::sImageBridges; + +StaticAutoPtr 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 parents; + ManagedPImageContainerParent(parents); + for (PImageContainerParent* p : parents) { + delete p; + } +} + +static StaticRefPtr sImageBridgeParentSingleton; + +void ReleaseImageBridgeParentSingleton() { + sImageBridgeParentSingleton = nullptr; +} + +/* static */ ImageBridgeParent* +ImageBridgeParent::CreateSameProcess() +{ + RefPtr parent = + new ImageBridgeParent(CompositorThreadHolder::Loop(), base::GetCurrentProcId()); + parent->mSelfRef = parent; + + sImageBridgeParentSingleton = parent; + return parent; +} + +/* static */ bool +ImageBridgeParent::CreateForGPUProcess(Endpoint&& aEndpoint) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + + MessageLoop* loop = CompositorThreadHolder::Loop(); + RefPtr parent = new ImageBridgeParent(loop, aEndpoint.OtherPid()); + + loop->PostTask(NewRunnableMethod&&>( + 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* 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* 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 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&& aEndpoint) +{ + MessageLoop* loop = CompositorThreadHolder::Loop(); + + RefPtr bridge = new ImageBridgeParent(loop, aEndpoint.OtherPid()); + loop->PostTask(NewRunnableMethod&&>( + bridge, &ImageBridgeParent::Bind, Move(aEndpoint))); + + return true; +} + +void +ImageBridgeParent::Bind(Endpoint&& 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 textures; + ManagedPTextureParent(textures); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr 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(aActor); + return true; +} + +PImageContainerParent* +ImageBridgeParent::AllocPImageContainerParent() +{ + return new ImageContainerParent(); +} + +bool +ImageBridgeParent::DeallocPImageContainerParent(PImageContainerParent* actor) +{ + delete actor; + return true; +} + +void +ImageBridgeParent::SendAsyncMessage(const InfallibleTArray& 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& 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 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::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 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 // for size_t +#include // 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 EditArray; + typedef InfallibleTArray OpDestroyArray; + typedef InfallibleTArray EditReplyArray; + +protected: + ImageBridgeParent(MessageLoop* aLoop, ProcessId aChildProcessId); + +public: + ~ImageBridgeParent(); + + /** + * Creates the globals of ImageBridgeParent. + */ + static void Setup(); + + static ImageBridgeParent* CreateSameProcess(); + static bool CreateForGPUProcess(Endpoint&& aEndpoint); + static bool CreateForContent(Endpoint&& aEndpoint); + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + // CompositableParentManager + virtual void SendAsyncMessage(const InfallibleTArray& 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 GetInstance(ProcessId aId); + + static bool NotifyImageComposites(nsTArray& 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&& aEndpoint); + +private: + void DeferredDestroy(); + MessageLoop* mMessageLoop; + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr mSelfRef; + + bool mSetChildThreadPriority; + bool mClosed; + + /** + * Map of all living ImageBridgeParent instances + */ + static std::map sImageBridges; + + RefPtr 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 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 mSyncObject; + + const int32_t mSerial; + static mozilla::Atomic 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 +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 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 // 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 // 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 EditReplyVector; + +using mozilla::layout::RenderFrameParent; + +namespace mozilla { +namespace layers { + +//-------------------------------------------------- +// Convenience accessors +static ShadowLayerParent* +cast(const PLayerParent* in) +{ + return const_cast( + static_cast(in)); +} + +template +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& layers = ManagedPLayerParent(); + for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) { + ShadowLayerParent* slp = + static_cast(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& layers = ManagedPLayerParent(); + for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) { + ShadowLayerParent* slp = + static_cast(iter.Get()->GetKey()); + slp->Destroy(); + } + mDestroyed = true; +} + +bool +LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray&& cset, + InfallibleTArray&& 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* 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* mActorsToDestroy; +}; + +bool +LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId, + const TimeDuration& aPaintTime) +{ + mCompositorBridge->UpdatePaintTime(this, aPaintTime); + return true; +} + +bool +LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset, + InfallibleTArray&& 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* 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 layer = + layer_manager()->CreatePaintedLayerComposite(); + AsLayerComposite(edit.get_OpCreatePaintedLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateContainerLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); + + RefPtr layer = layer_manager()->CreateContainerLayerComposite(); + AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateImageLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); + + RefPtr layer = + layer_manager()->CreateImageLayerComposite(); + AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateColorLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); + + RefPtr layer = layer_manager()->CreateColorLayerComposite(); + AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateCanvasLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); + + RefPtr layer = + layer_manager()->CreateCanvasLayerComposite(); + AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateRefLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); + + RefPtr 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(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> 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( + 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&& 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(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& 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 // for size_t +#include // 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 EditArray; + typedef InfallibleTArray OpDestroyArray; + typedef InfallibleTArray EditReplyArray; + typedef InfallibleTArray 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& 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&& 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 mLayerManager; + CompositorBridgeParentBase* mCompositorBridge; + // Hold the root because it might be grafted under various + // containers in the "real" layer tree + RefPtr 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 // for std::make_pair + +namespace mozilla { +namespace layers { + +static StaticAutoPtr 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 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 + +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 aCallback); + +private: + LayerTreeOwnerTracker(); + + mozilla::Mutex mLayerIdsLock; + std::map 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 + (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 rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId); + if (rootController) { + rootController->NotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers); + } + } +} + +void +RemoteContentController::PostDelayedTask(already_AddRefed 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 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(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(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(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(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(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 aTask, int aDelayMs) override; + + virtual bool IsRepaintThread() override; + + virtual void DispatchToRepaintThread(already_AddRefed 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(mLayer.get()) + : nullptr; +} + +CanvasLayerComposite* +ShadowLayerParent::AsCanvasLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_CANVAS + ? static_cast(mLayer.get()) + : nullptr; +} + +ColorLayerComposite* +ShadowLayerParent::AsColorLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_COLOR + ? static_cast(mLayer.get()) + : nullptr; +} + +ImageLayerComposite* +ShadowLayerParent::AsImageLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE + ? static_cast(mLayer.get()) + : nullptr; +} + +RefLayerComposite* +ShadowLayerParent::AsRefLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_REF + ? static_cast(mLayer.get()) + : nullptr; +} + +PaintedLayerComposite* +ShadowLayerParent::AsPaintedLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED + ? static_cast(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 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 { + 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 + : 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 // for Drawable, XID +#include // for Display, Visual, etc +#include // for XRenderPictFormat, etc +#include // for PictFormat +#include "cairo-xlib.h" +#include "X11UndefineNone.h" +#include // 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 +SurfaceDescriptorX11::OpenForeign() const +{ + Display* display = DefaultXDisplay(); + Screen* screen = DefaultScreenOfDisplay(display); + + RefPtr 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 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 { + 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 // for _Rb_tree_const_iterator, etc +#include // 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 BufferArray; +typedef std::vector EditVector; +typedef nsTHashtable> ShadowableLayerSet; +typedef nsTArray 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(1000, "CompositableForwarder"); +} + +template +struct ReleaseOnMainThreadTask : public Runnable +{ + UniquePtr mObj; + + explicit ReleaseOnMainThreadTask(UniquePtr& 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(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 +static void +CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer) +{ + aTxn->AddEdit(OpCreateT(nullptr, Shadow(aLayer))); +} + +void +ShadowLayerForwarder::CreatedPaintedLayer(ShadowableLayer* aThebes) +{ + CreatedLayer(mTxn, aThebes); +} +void +ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer) +{ + CreatedLayer(mTxn, aContainer); +} +void +ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage) +{ + CreatedLayer(mTxn, aImage); +} +void +ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor) +{ + CreatedLayer(mTxn, aColor); +} +void +ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas) +{ + CreatedLayer(mTxn, aCanvas); +} +void +ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef) +{ + CreatedLayer(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& aTextures) +{ + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + AutoTArray 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& + 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* 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 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(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(); + } else { + return reinterpret_cast(memOrShmem.get_uintptr_t()); + } +} + +already_AddRefed +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 +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(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(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 // for size_t +#include // 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 +#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 +{ +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& 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* 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& + 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 mShadowManager; + RefPtr mCompositorBridgeChild; + +private: + + ClientLayerManager* mClientLayerManager; + Transaction* mTxn; + MessageLoop* mMessageLoop; + DiagnosticTypes mDiagnosticTypes; + bool mIsFirstPaint; + bool mWindowOverlayChanged; + int32_t mPaintSyncId; + InfallibleTArray mPluginWindowData; + UniquePtr 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 // for size_t +#include // 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 +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(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 // 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 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 mTextureClient; + RefPtr 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 +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 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 +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 // for size_t +#include // 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 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 GetAsSourceSurface() override; + + bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat); +private: + gfx::IntSize mSize; + RefPtr mCompositable; + RefPtr 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 // 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 +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 sVideoBridgeChildSingleton; + +/* static */ void +VideoBridgeChild::Startup() +{ + sVideoBridgeChildSingleton = new VideoBridgeChild(); + RefPtr 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 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& 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& 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 mSelfRef; + + std::map 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 Binary files /dev/null and b/gfx/layers/layerviewer/hide.png 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 @@ + + + + GFX Display List & Layer Visualizer + + + + + + +

    GFX Layers dump visualizer:

    + Paste your display list or layers dump into this textarea:
    + +
    + +
    +
    + Help: To get a layers dump go to about:config and set layout.display-list.dump;true or layers.dump;true. + + + 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 += "
    Item: " + this.displayItem.name + " (" + this.displayItem.address + ")"; + description += "
    Layer: " + root.name + " (" + root.address + ")"; + if (this.displayItem.frame) { + description += "
    Frame: " + this.displayItem.frame; + } + if (this.displayItem.layerBounds) { + description += "
    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 += "
    Z: " + this.displayItem.z; + } + // At the end + if (this.displayItem.rest) { + description += "
    " + 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 Binary files /dev/null and b/gfx/layers/layerviewer/noise.png differ diff --git a/gfx/layers/layerviewer/show.png b/gfx/layers/layerviewer/show.png new file mode 100644 index 000000000..7038b660c Binary files /dev/null and b/gfx/layers/layerviewer/show.png 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 +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 + RenderTargetForWindow(CompositorOGL* aCompositor, + const gfx::IntSize& aSize) + { + RefPtr 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 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 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 // for size_t +#include // for uint32_t, uint8_t +#include // 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 +CompositorOGL::CreateContext() +{ + RefPtr 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(widgetOpenGLContext); + return already_AddRefed(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 ctx = mGLContext->GetSharedContext(); + if (!ctx) { + ctx = mGLContext; + } + + if (!ctx->MakeCurrent()) { + // Leak resources! + mQuadVBO = 0; + mTriangleVBO = 0; + mGLContext = nullptr; + mPrograms.clear(); + return; + } + + for (std::map::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 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 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 + 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 +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 surface + = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo); + surface->Initialize(aRect.Size(), FBOSize, mFBOTextureTarget, aInit); + return surface.forget(); +} + +already_AddRefed +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(aSource); + IntRect sourceRect(aSourcePoint, aRect.Size()); + if (aSource) { + CreateFBOWithTexture(sourceRect, true, sourceSurface->GetFBO(), + &fbo, &tex); + } else { + CreateFBOWithTexture(sourceRect, true, 0, + &fbo, &tex); + } + + RefPtr 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(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 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(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(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(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::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 +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(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(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(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(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(aEffectChain.mPrimaryEffect.get()); + source = effectComponentAlpha->mOnWhite->AsSourceOGL(); + } else { + TexturedEffect* texturedEffect = + static_cast(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; iSetLayerTransformInverse(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(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(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(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(aEffectChain.mPrimaryEffect.get()); + RefPtr surface + = static_cast(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(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 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(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 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 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 +CompositorOGL::CreateDataTextureSource(TextureFlags aFlags) +{ + return MakeAndAddRef(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(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 mTextures; + RefPtr 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 mGL; + nsTArray mCreatedTextures; + nsTArray 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 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 + 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 + CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override; + + virtual already_AddRefed + 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 + 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 mGLContext; + UniquePtr mBlitTextureImageHelper; + gfx::Matrix4x4 mProjMatrix; + + /** The size of the surface we are rendering to */ + gfx::IntSize mSurfaceSize; + + ScreenPoint mRenderOffset; + + already_AddRefed CreateContext(); + + /** Texture target to use for FBOs */ + GLenum mFBOTextureTarget; + + /** Currently bound render target */ + RefPtr 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 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& 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 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 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 +MacIOSurfaceTextureData::GetAsSurface() +{ + RefPtr surf = CreateSourceSurfaceFromMacIOSurface(mSurface); + return surf->GetDataSurface(); +} + +already_AddRefed +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 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 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 GetAsSurface(); + +protected: + MacIOSurfaceTextureData(MacIOSurface* aSurface, + gfx::BackendType aBackend); + + RefPtr 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 prev = mTextureSource; + for (size_t i = 1; i < mSurface->GetPlaneCount(); i++) { + RefPtr 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 mCompositor; + RefPtr 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 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 mCompositor; + RefPtr mTextureSource; + RefPtr 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 // for uint32_t +#include // 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 {"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 {"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 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& 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 + +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> mAttributes; + + KnownUniform mUniforms[KnownUniform::KnownUniformCount]; + nsTArray 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, ¤tProgram); \ + 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 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 +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 +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 + 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 mImage; + const gfx::IntSize mSize; +}; + +#ifdef MOZ_WIDGET_ANDROID + +class AndroidSurfaceTextureData : public TextureData +{ +public: + static already_AddRefed + 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 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 +CreateTextureHostOGL(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr 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(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 // for size_t +#include // 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 mTexImage; + RefPtr 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 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 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 mCompositor; + RefPtr 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 mCompositor; + RefPtr 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 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 mSurfTex; + const gfx::IntSize mSize; + RefPtr mCompositor; + RefPtr 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 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 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 mCompositor; + RefPtr 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 // 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 mCompositor; + RefPtr mSurface; + RefPtr 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 + +#include +#include +#include +#include +#include +// @@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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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(&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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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(&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( \ + &reinterpret_cast(16)->f) - \ + reinterpret_cast(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(&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 + +#include + +#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 +#include +#include +#include +// @@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(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(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; +} -- cgit v1.2.3